Skip to content

Commit

Permalink
Merge pull request #5 from njoy/feature/IteratorView
Browse files Browse the repository at this point in the history
Feature/iterator view
  • Loading branch information
whaeck authored Oct 12, 2023
2 parents 25c9273 + 6cfc66c commit 32d440d
Show file tree
Hide file tree
Showing 7 changed files with 351 additions and 1 deletion.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ if( DEFINED PROJECT_NAME )
endif()

project( tools
VERSION 0.2.0
VERSION 0.3.0
LANGUAGES CXX
)

Expand Down
2 changes: 2 additions & 0 deletions cmake/unit_testing.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ endfunction()
#######################################################################

add_subdirectory( src/tools/Log/test )

add_subdirectory( src/tools/ranges/IteratorView/test )
3 changes: 3 additions & 0 deletions src/tools.hpp
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
#include "tools/overload.hpp"
#include "tools/ranges.hpp"

#include "tools/Log.hpp"
1 change: 1 addition & 0 deletions src/tools/ranges.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "tools/ranges/IteratorView.hpp"
217 changes: 217 additions & 0 deletions src/tools/ranges/IteratorView.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
#ifndef NJOY_TOOLS_RANGES_ITERATORVIEW
#define NJOY_TOOLS_RANGES_ITERATORVIEW

// system includes
#include <iterator>
#include <stdexcept>

// other includes

namespace njoy {
namespace tools {
namespace ranges {

/**
*
* @brief A simple iterator based view class
*
* Currently only defined for random access iterators.
*/
template < class Iterator,
std::enable_if_t<
std::is_same< typename std::iterator_traits< Iterator >::iterator_category,
std::random_access_iterator_tag >::value, bool > = true >
class IteratorView {

public:

using value_type = typename std::iterator_traits< Iterator >::value_type;
using difference_type = typename std::iterator_traits< Iterator >::difference_type;
using size_type = std::size_t;
using pointer = typename std::iterator_traits< Iterator >::pointer;
using reference = typename std::iterator_traits< Iterator >::reference;
using const_reference = const reference;
using iterator = Iterator;

private:

/* fields */
Iterator begin_;
Iterator end_;

public:

/* constructor */

/**
* @brief Default constructor
*/
constexpr IteratorView() : IteratorView( Iterator{}, Iterator{} ) {}

/**
* @brief IteratorView constructor
*
* @param[in] begin the iterator to the beginning of the view
* @param[in] end the iterator to the end of the view
*/
constexpr IteratorView( Iterator begin, Iterator end ) :
begin_( std::move( begin ) ), end_( std::move( end ) ) {}

/* methods */

/**
* @brief Return the begin iterator to the view
*/
constexpr iterator begin() const noexcept { return begin_; }

/**
* @brief Return the end iterator to the view
*/
constexpr iterator end() const noexcept { return end_; }

/**
* @brief Return the reference to the front element of the view
*/
constexpr decltype(auto) front() const noexcept {

return *( this->begin() );
}

/**
* @brief Return the reference to the back element of the view
*/
constexpr decltype(auto) back() const noexcept {

return *( std::prev( this->end() ) );
}

/**
* @brief Return whether or not the view is empty
*/
constexpr bool empty() const noexcept { return this->begin() == this->end(); }

/**
* @brief Return the size of the iterator view
*/
constexpr size_type size() const noexcept {

return std::distance( this->begin(), this->end() );
}

/**
* @brief Return an element at a given index
*
* No range checking is performed.
*
* @param[in] i the index
*/
constexpr decltype(auto) operator[]( size_type i ) const noexcept {

return *( std::next( this->begin(), i ) );
}

/**
* @brief Return an element at a given index with range checking
*
* @param[in] i the index
*/
constexpr decltype(auto) at( size_type i ) const {

if ( i >= this->size() ) {

throw std::out_of_range( "Index out of range in IteratorView: " +
std::to_string( i ) + " >= size (" +
std::to_string( this->size() ) + ")" );
}
return this->operator[]( i );
}

/**
* @brief Verify if the IteratorView is equal to another container
*
* @param[in] other the other container to compare with
*/
template < typename Container >
constexpr bool operator==( const Container& other ) const {

return std::equal( this->begin(), this->end(), other.begin(), other.end() );
}

/**
* @brief Verify if the IteratorView is equal to another container
*
* @param[in] other the other container to compare with
*/
template < typename Container >
constexpr bool operator!=( const Container& other ) const {

return !this->operator==( other );
}
};

/**
* @brief Make an IteratorView based on two iterators
*
* @param[in] begin the iterator to the beginning of the view
* @param[in] end the iterator to the end of the view
*/
template < typename Iterator >
constexpr auto make_view( Iterator&& begin, Iterator&& end ) {

return IteratorView< Iterator >{ std::forward< Iterator >( begin ),
std::forward< Iterator >( end ) };
}

/**
* @brief Make an IteratorView based on a container
*
* @param[in] container the container
*/
template < typename Container >
constexpr auto make_view( Container& container ) {

return IteratorView< typename Container::iterator >{ container.begin(),
container.end() };
}

/**
* @brief Make an IteratorView based on a container
*
* @param[in] container the container
*/
template < typename Container >
constexpr auto make_view( const Container& container ) {

return IteratorView< typename Container::const_iterator >{ container.cbegin(),
container.cend() };
}

/**
* @brief Verify if the IteratorView is equal to another container
*
* @param[in] other the other container to compare with
*/
template < typename Container, typename Iterator >
constexpr bool operator==( const Container& left,
IteratorView< Iterator > right ) {

return right == left;
}

/**
* @brief Verify if the IteratorView is equal to another container
*
* @param[in] other the other container to compare with
*/
template < typename Container, typename Iterator >
constexpr bool operator!=( const Container& left,
IteratorView< Iterator > right ) {

return right != left;
}

} // ranges namespace
} // tools namespace
} // njoy namespace

#endif
1 change: 1 addition & 0 deletions src/tools/ranges/IteratorView/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_cpp_test( ranges.IteratorView IteratorView.test.cpp )
126 changes: 126 additions & 0 deletions src/tools/ranges/IteratorView/test/IteratorView.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// include Catch2
#include <catch2/catch_test_macros.hpp>

// what we are testing
#include "tools/ranges/IteratorView.hpp"

// other includes

// convenience typedefs
using namespace njoy::tools::ranges;

SCENARIO( "IteratorView" ) {

GIVEN( "a container with values" ) {

std::vector< int > values = { -2, -1, 0, 1, 2 };

WHEN( "when iterators are used" ) {

IteratorView< std::vector< int >::iterator > chunk( values.begin(), values.end() );

THEN( "an IteratorView can be constructed and members can be tested" ) {

CHECK( 5 == chunk.size() );
CHECK( false == chunk.empty() );

CHECK( -2 == chunk[0] );
CHECK( -1 == chunk[1] );
CHECK( 0 == chunk[2] );
CHECK( 1 == chunk[3] );
CHECK( 2 == chunk[4] );

CHECK( -2 == chunk.at( 0 ) );
CHECK( -1 == chunk.at( 1 ) );
CHECK( 0 == chunk.at( 2 ) );
CHECK( 1 == chunk.at( 3 ) );
CHECK( 2 == chunk.at( 4 ) );

CHECK( -2 == chunk.front() );
CHECK( 2 == chunk.back() );
} // THEN

THEN( "an exception is thrown when using an index that is too large or "
"too small" ) {

CHECK_NOTHROW( chunk.at( 0 ) );
CHECK_NOTHROW( chunk.at( 4 ) );
CHECK_THROWS( chunk.at( 5 ) );
CHECK_THROWS( chunk.at( 10 ) );
} // THEN
} // WHEN

WHEN( "when make functions are used" ) {

auto chunk = make_view( values );

THEN( "an IteratorView can be constructed and members can be tested" ) {

CHECK( 5 == chunk.size() );
CHECK( false == chunk.empty() );

CHECK( -2 == chunk[0] );
CHECK( -1 == chunk[1] );
CHECK( 0 == chunk[2] );
CHECK( 1 == chunk[3] );
CHECK( 2 == chunk[4] );

CHECK( -2 == chunk.at( 0 ) );
CHECK( -1 == chunk.at( 1 ) );
CHECK( 0 == chunk.at( 2 ) );
CHECK( 1 == chunk.at( 3 ) );
CHECK( 2 == chunk.at( 4 ) );

CHECK( -2 == chunk.front() );
CHECK( 2 == chunk.back() );
} // THEN

THEN( "an exception is thrown when using an index that is too large or "
"too small" ) {

CHECK_NOTHROW( chunk.at( 0 ) );
CHECK_NOTHROW( chunk.at( 4 ) );
CHECK_THROWS( chunk.at( 5 ) );
CHECK_THROWS( chunk.at( 10 ) );
} // THEN
} // WHEN
} // GIVEN

GIVEN( "containers and views with values" ) {

std::vector< double > container1 = { 1., 2., 3., 4. };
std::vector< double > container2 = { 1., 2., 3. };

std::vector< double > copy1 = container1;
std::vector< double > copy2 = container2;

auto view1 = make_view( copy1 );
auto view2 = make_view( copy2 );

WHEN( "when make comparison functions are used" ) {

THEN( "views and containers can be compared" ) {

CHECK( view1 == container1 );
CHECK( view1 == copy1 );
CHECK( container1 == view1 );
CHECK( copy1 == view1 );

CHECK( view1 != container2 );
CHECK( view1 != copy2 );
CHECK( container2 != view1 );
CHECK( copy2 != view1 );

CHECK( view2 == container2 );
CHECK( view2 == copy2 );
CHECK( container2 == view2 );
CHECK( copy2 == view2 );

CHECK( view2 != container1 );
CHECK( view2 != copy1 );
CHECK( container1 != view2 );
CHECK( copy1 != view2 );
} // THEN
} // WHEN
} // GIVEN
} // SCENARIO

0 comments on commit 32d440d

Please sign in to comment.