Skip to content

Commit

Permalink
Merge pull request #6 from njoy/feature/TransformIterator
Browse files Browse the repository at this point in the history
Feature/transform iterator
  • Loading branch information
whaeck authored Oct 12, 2023
2 parents 32d440d + 5f2850f commit 8794ddd
Show file tree
Hide file tree
Showing 13 changed files with 484 additions and 75 deletions.
3 changes: 3 additions & 0 deletions cmake/unit_testing.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ endfunction()
add_subdirectory( src/tools/Log/test )

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

#include "tools/ranges/make_view.hpp"
#include "tools/ranges/make_transform_view.hpp"
39 changes: 1 addition & 38 deletions src/tools/ranges/IteratorView.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class IteratorView {
constexpr IteratorView() : IteratorView( Iterator{}, Iterator{} ) {}

/**
* @brief IteratorView constructor
* @brief Constructor
*
* @param[in] begin the iterator to the beginning of the view
* @param[in] end the iterator to the end of the view
Expand Down Expand Up @@ -149,43 +149,6 @@ class IteratorView {
}
};

/**
* @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
*
Expand Down
39 changes: 2 additions & 37 deletions src/tools/ranges/IteratorView/test/IteratorView.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,41 +49,6 @@ SCENARIO( "IteratorView" ) {
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" ) {
Expand All @@ -94,8 +59,8 @@ SCENARIO( "IteratorView" ) {
std::vector< double > copy1 = container1;
std::vector< double > copy2 = container2;

auto view1 = make_view( copy1 );
auto view2 = make_view( copy2 );
IteratorView< std::vector< double >::iterator > view1( copy1.begin(), copy1.end() );
IteratorView< std::vector< double >::iterator > view2( copy2.begin(), copy2.end() );

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

Expand Down
157 changes: 157 additions & 0 deletions src/tools/ranges/TransformIterator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#ifndef NJOY_UTILITY_TRANSFORMITERATOR
#define NJOY_UTILITY_TRANSFORMITERATOR

// system includes
#include <iterator>

// other includes

namespace njoy {
namespace tools {
namespace ranges {

/**
*
* @brief An iterator class that applies a transformation when dereferencing
* the iterator
*/
template < typename Iterator, typename Transformation >
class TransformIterator {

/* fields */
Iterator iter_;
Transformation transform_;

public:

using iterator_category = typename Iterator::iterator_category;
using difference_type = typename Iterator::difference_type;
using value_type = decltype( std::declval< Transformation >()( std::declval< typename Iterator::value_type >() ) );
using pointer = value_type*;
using reference = value_type;

/* constructor */

constexpr TransformIterator() = delete;

/**
* @brief Constructor
*
* @param[in] iter the iterator
* @param[in] transformation the transformation to be applied
*/
constexpr TransformIterator( Iterator iter, Transformation transformation ) :
iter_( std::move( iter ) ), transform_( std::move( transformation ) ) {}

/* methods */

constexpr reference operator*() const {

return this->transform_( *this->iter_ );
}

constexpr reference operator[]( difference_type i ) const {

return this->transform_( this->iter_[i] );
}

constexpr TransformIterator& operator+=( difference_type i ) {

this->iter_ += i;
return *this;
}

constexpr TransformIterator& operator-=( difference_type i ) {

this->iter_ -= i;
return *this;
}

constexpr TransformIterator& operator++( void ) {

++this->iter_;
return *this;
}

constexpr TransformIterator operator++( int ) {

return TransformIterator( this->iter_++, transform_ );
}

constexpr TransformIterator& operator--( void ) {

--this->iter_;
return *this;
}

constexpr TransformIterator operator--( int ) {

return TransformIterator( this->iter_--, this->transform_ );
}

friend constexpr TransformIterator operator+( difference_type i,
TransformIterator iter ) {

return TransformIterator( iter.iter_ + i, iter.transform_ );
}

friend constexpr TransformIterator operator+( TransformIterator iter,
difference_type i ) {

return TransformIterator( iter.iter_ + i, iter.transform_ );
}

friend constexpr TransformIterator operator-( TransformIterator iter,
difference_type i ) {

return TransformIterator( iter.iter_ - i, iter.transform_ );
}

friend constexpr difference_type operator-( const TransformIterator& left,
const TransformIterator& right ) {

return left.iter_ - right.iter_;
}

friend constexpr bool operator==( const TransformIterator& left,
const TransformIterator& right ) {

return left.iter_ == right.iter_;
};

friend constexpr bool operator!=( const TransformIterator& left,
const TransformIterator& right ) {

return !( left == right );
};

friend constexpr bool operator<( const TransformIterator& left,
const TransformIterator& right) {

return left.iter_ < right.iter_;
}

friend constexpr bool operator>( const TransformIterator& left,
const TransformIterator& right ) {

return right < left;
}

friend constexpr bool operator<=( const TransformIterator& left,
const TransformIterator& right ) {

return !( right < left );
}

friend constexpr bool operator>=( const TransformIterator& left,
const TransformIterator& right ) {

return !( left < right );
}
};

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

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

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

// other includes

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

SCENARIO( "TransformIterator" ) {

GIVEN( "a container with values and a transformation operation" ) {

std::vector< int > values = { -2, -1, 0, 1, 2 };
auto transform = [] ( auto&& value ) { return 2 * value; };

WHEN( "when iterators are used" ) {

TransformIterator iter( values.begin(), transform );
TransformIterator begin( values.begin(), transform );
TransformIterator end( values.end(), transform );

THEN( "TransformIterator behaves as an iterator" ) {

CHECK( *iter == -4 );

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

CHECK( *( begin + 0 ) == -4 );
CHECK( *( begin + 1 ) == -2 );
CHECK( *( begin + 2 ) == 0 );
CHECK( *( begin + 3 ) == 2 );
CHECK( *( begin + 4 ) == 4 );

CHECK( *( 0 + begin ) == -4 );
CHECK( *( 1 + begin ) == -2 );
CHECK( *( 2 + begin ) == 0 );
CHECK( *( 3 + begin ) == 2 );
CHECK( *( 4 + begin ) == 4 );

CHECK( *( end - 1 ) == 4 );
CHECK( *( end - 2 ) == 2 );
CHECK( *( end - 3 ) == 0 );
CHECK( *( end - 4 ) == -2 );
CHECK( *( end - 5 ) == -4 );

CHECK( end - begin == 5 );

iter += 3;
CHECK( *iter == 2 );
iter -= 3;
CHECK( *iter == -4 );

++iter;
CHECK( *iter == -2 );
--iter;
CHECK( *iter == -4 );

CHECK( *( iter++ ) == -4 );
CHECK( *iter == -2 );
CHECK( *( iter-- ) == -2 );
CHECK( *iter == -4 );

CHECK( true == ( iter == begin ) );
CHECK( false == ( iter == end ) );
CHECK( true == ( iter != end ) );
CHECK( false == ( iter != begin ) );
CHECK( true == ( iter < end ) );
CHECK( false == ( iter < begin ) );
CHECK( true == ( iter <= begin ) );
CHECK( false == ( end <= iter ) );
CHECK( true == ( end > iter ) );
CHECK( false == ( iter > begin ) );
CHECK( true == ( end >= iter ) );
CHECK( false == ( begin >= end ) );
} // THEN
} // WHEN
} // GIVEN
} // SCENARIO
Loading

0 comments on commit 8794ddd

Please sign in to comment.