diff --git a/cmake/unit_testing.cmake b/cmake/unit_testing.cmake index 07342c5..c859835 100644 --- a/cmake/unit_testing.cmake +++ b/cmake/unit_testing.cmake @@ -28,6 +28,10 @@ endfunction() add_subdirectory( src/tools/Log/test ) +add_subdirectory( src/tools/concepts/Requires/test ) +add_subdirectory( src/tools/concepts/IsRange/test ) +add_subdirectory( src/tools/concepts/IsIterator/test ) + add_subdirectory( src/tools/ranges/IteratorView/test ) add_subdirectory( src/tools/ranges/TransformIterator/test ) add_subdirectory( src/tools/ranges/make_view/test ) diff --git a/src/tools/concepts.hpp b/src/tools/concepts.hpp new file mode 100644 index 0000000..c6e24c6 --- /dev/null +++ b/src/tools/concepts.hpp @@ -0,0 +1,4 @@ +#include "tools/concepts/Require.hpp" + +#include "tools/concepts/IsIterator.hpp" +#include "tools/concepts/IsRange.hpp" diff --git a/src/tools/concepts/IsIterator.hpp b/src/tools/concepts/IsIterator.hpp new file mode 100644 index 0000000..1dfba6a --- /dev/null +++ b/src/tools/concepts/IsIterator.hpp @@ -0,0 +1,68 @@ +#ifndef NJOY_TOOLS_CONCEPTS_ISITERATOR +#define NJOY_TOOLS_CONCEPTS_ISITERATOR + +// system includes +#include + +// other includes + +namespace njoy { +namespace tools { +namespace concepts { + + template< typename Iterator > + using iterator_category = + typename std::iterator_traits< Iterator >::iterator_category; + + // sfinae structures for iterator objects + + template< typename Iterator, typename = void > + struct IsIterator : std::false_type {}; + + template< typename Iterator > + struct IsIterator< + Iterator, + std::void_t< iterator_category< Iterator > > > : std::true_type {}; + + // the following template aliases only work on iterators + + // sfinae structures for input iterator objects + + template< typename Iterator > + using IsInputIterator = + typename std::is_base_of< std::input_iterator_tag, + iterator_category< Iterator > >; + + // sfinae structures for output iterator objects + + template< typename Iterator > + using IsOutputIterator = + typename std::is_base_of< std::output_iterator_tag, + iterator_category< Iterator > >; + + // sfinae structures for forward iterator objects + + template< typename Iterator > + using IsForwardIterator = + typename std::is_base_of< std::forward_iterator_tag, + iterator_category< Iterator > >; + + // sfinae structures for bidirectional iterator objects + + template< typename Iterator > + using IsBidirectionalIterator = + typename std::is_base_of< std::bidirectional_iterator_tag, + iterator_category< Iterator > >; + + // sfinae structures for random access iterator objects + + template< typename Iterator > + using IsRandomAccessIterator = + typename std::is_base_of< std::random_access_iterator_tag, + iterator_category< Iterator > >; + +} // ranges namespace +} // tools namespace +} // njoy namespace + +#endif diff --git a/src/tools/concepts/IsIterator/test/CMakeLists.txt b/src/tools/concepts/IsIterator/test/CMakeLists.txt new file mode 100644 index 0000000..1224069 --- /dev/null +++ b/src/tools/concepts/IsIterator/test/CMakeLists.txt @@ -0,0 +1 @@ +add_cpp_test( concepts.IsIterator IsIterator.test.cpp ) diff --git a/src/tools/concepts/IsIterator/test/IsIterator.test.cpp b/src/tools/concepts/IsIterator/test/IsIterator.test.cpp new file mode 100644 index 0000000..0bd420a --- /dev/null +++ b/src/tools/concepts/IsIterator/test/IsIterator.test.cpp @@ -0,0 +1,177 @@ +// include Catch2 +#include + +// what we are testing +#include "tools/concepts/IsIterator.hpp" + +// other includes +#include +#include +#include +#include +#include +#include +#include +#include + +// convenience typedefs +using namespace njoy::tools::concepts; + +SCENARIO( "IsIterator sfinae objects" ) { + + GIVEN( "different types" ) { + + WHEN( "when applying IsIterator" ) { + + THEN( "true is returned for iterators and false for everything else" ) { + + // non iterator type + CHECK( false == IsIterator< int >::value ); + + // input iterator types + CHECK( true == IsIterator< std::istream_iterator< int > >::value ); + + // output iterator types + CHECK( true == IsIterator< std::ostream_iterator< int > >::value ); + + // forward iterator types + CHECK( true == IsIterator< std::forward_list< int >::iterator >::value ); + CHECK( true == IsIterator< std::unordered_set< int >::iterator >::value ); + CHECK( true == IsIterator< std::unordered_map< int, int >::iterator >::value ); + + // bidirectional iterator types + CHECK( true == IsIterator< std::list< int >::iterator >::value ); + CHECK( true == IsIterator< std::set< int >::iterator >::value ); + CHECK( true == IsIterator< std::map< int, int >::iterator >::value ); + + // random access iterator types + CHECK( true == IsIterator< std::vector< int >::iterator >::value ); + } // THEN + } // WHEN + + WHEN( "when applying IsInputIterator" ) { + + THEN( "true is returned for iterators that conform to input iterators" ) { + + // input iterator type + CHECK( true == IsInputIterator< std::istream_iterator< int > >::value ); + + // output iterator types + CHECK( false == IsInputIterator< std::ostream_iterator< int > >::value ); + + // forward iterator types + CHECK( true == IsInputIterator< std::forward_list< int >::iterator >::value ); + CHECK( true == IsInputIterator< std::unordered_set< int >::iterator >::value ); + CHECK( true == IsInputIterator< std::unordered_map< int, int >::iterator >::value ); + + // bidirectional iterator types + CHECK( true == IsInputIterator< std::list< int >::iterator >::value ); + CHECK( true == IsInputIterator< std::set< int >::iterator >::value ); + CHECK( true == IsInputIterator< std::map< int, int >::iterator >::value ); + + // random access iterator types + CHECK( true == IsInputIterator< std::vector< int >::iterator >::value ); + } // THEN + } // WHEN + + WHEN( "when applying IsOutputIterator" ) { + + THEN( "true is returned for iterators that conform to output iterators" ) { + + // input iterator types + CHECK( false == IsOutputIterator< std::istream_iterator< int > >::value ); + + // output iterator types + CHECK( true == IsOutputIterator< std::ostream_iterator< int > >::value ); + + // forward iterator types + CHECK( false == IsOutputIterator< std::forward_list< int >::iterator >::value ); + CHECK( false == IsOutputIterator< std::unordered_set< int >::iterator >::value ); + CHECK( false == IsOutputIterator< std::unordered_map< int, int >::iterator >::value ); + + // bidirectional iterator types + CHECK( false == IsOutputIterator< std::list< int >::iterator >::value ); + CHECK( false == IsOutputIterator< std::set< int >::iterator >::value ); + CHECK( false == IsOutputIterator< std::map< int, int >::iterator >::value ); + + // random access iterator types + CHECK( false == IsOutputIterator< std::vector< int >::iterator >::value ); + } // THEN + } // WHEN + + WHEN( "when applying IsForwardIterator" ) { + + THEN( "true is returned for iterators that conform to forward iterators" ) { + + // input iterator types + CHECK( false == IsForwardIterator< std::istream_iterator< int > >::value ); + + // output iterator types + CHECK( false == IsForwardIterator< std::ostream_iterator< int > >::value ); + + // forward iterator types + CHECK( true == IsForwardIterator< std::forward_list< int >::iterator >::value ); + CHECK( true == IsForwardIterator< std::unordered_set< int >::iterator >::value ); + CHECK( true == IsForwardIterator< std::unordered_map< int, int >::iterator >::value ); + + // bidirectional iterator types + CHECK( true == IsForwardIterator< std::list< int >::iterator >::value ); + CHECK( true == IsForwardIterator< std::set< int >::iterator >::value ); + CHECK( true == IsForwardIterator< std::map< int, int >::iterator >::value ); + + // random access iterator types + CHECK( true == IsForwardIterator< std::vector< int >::iterator >::value ); + } // THEN + } // WHEN + + WHEN( "when applying IsBidirectionalIterator" ) { + + THEN( "true is returned for iterators that conform to bidirectional iterators" ) { + + // input iterator types + CHECK( false == IsBidirectionalIterator< std::istream_iterator< int > >::value ); + + // output iterator types + CHECK( false == IsBidirectionalIterator< std::ostream_iterator< int > >::value ); + + // forward iterator types + CHECK( false == IsBidirectionalIterator< std::forward_list< int >::iterator >::value ); + CHECK( false == IsBidirectionalIterator< std::unordered_set< int >::iterator >::value ); + CHECK( false == IsBidirectionalIterator< std::unordered_map< int, int >::iterator >::value ); + + // bidirectional iterator types + CHECK( true == IsBidirectionalIterator< std::list< int >::iterator >::value ); + CHECK( true == IsBidirectionalIterator< std::set< int >::iterator >::value ); + CHECK( true == IsBidirectionalIterator< std::map< int, int >::iterator >::value ); + + // random access iterator types + CHECK( true == IsBidirectionalIterator< std::vector< int >::iterator >::value ); + } // THEN + } // WHEN + + WHEN( "when applying IsRandomAccessIterator" ) { + + THEN( "true is returned for iterators that conform to random access iterators" ) { + + // input iterator types + CHECK( false == IsRandomAccessIterator< std::istream_iterator< int > >::value ); + + // output iterator types + CHECK( false == IsRandomAccessIterator< std::ostream_iterator< int > >::value ); + + // forward iterator types + CHECK( false == IsRandomAccessIterator< std::forward_list< int >::iterator >::value ); + CHECK( false == IsRandomAccessIterator< std::unordered_set< int >::iterator >::value ); + CHECK( false == IsRandomAccessIterator< std::unordered_map< int, int >::iterator >::value ); + + // bidirectional iterator types + CHECK( false == IsRandomAccessIterator< std::list< int >::iterator >::value ); + CHECK( false == IsRandomAccessIterator< std::set< int >::iterator >::value ); + CHECK( false == IsRandomAccessIterator< std::map< int, int >::iterator >::value ); + + // random access iterator types + CHECK( true == IsRandomAccessIterator< std::vector< int >::iterator >::value ); + } // THEN + } // WHEN + } // GIVEN +} // SCENARIO diff --git a/src/tools/concepts/IsRange.hpp b/src/tools/concepts/IsRange.hpp new file mode 100644 index 0000000..14bf4b5 --- /dev/null +++ b/src/tools/concepts/IsRange.hpp @@ -0,0 +1,26 @@ +#ifndef NJOY_TOOLS_CONCEPTS_ISRANGE +#define NJOY_TOOLS_CONCEPTS_ISRANGE + +// system includes +#include + +// other includes + +namespace njoy { +namespace tools { +namespace concepts { + + template< typename Range, typename = void > + struct IsRange : std::false_type {}; + + template< typename Range > + struct IsRange< Range, + std::void_t< decltype( std::begin( std::declval< Range >() ) ), + decltype( std::end( std::declval< Range >() ) ) + > > : std::true_type {}; + +} // ranges namespace +} // tools namespace +} // njoy namespace + +#endif diff --git a/src/tools/concepts/IsRange/test/CMakeLists.txt b/src/tools/concepts/IsRange/test/CMakeLists.txt new file mode 100644 index 0000000..67c0f31 --- /dev/null +++ b/src/tools/concepts/IsRange/test/CMakeLists.txt @@ -0,0 +1 @@ +add_cpp_test( concepts.IsRange IsRange.test.cpp ) diff --git a/src/tools/concepts/IsRange/test/IsRange.test.cpp b/src/tools/concepts/IsRange/test/IsRange.test.cpp new file mode 100644 index 0000000..565f8f4 --- /dev/null +++ b/src/tools/concepts/IsRange/test/IsRange.test.cpp @@ -0,0 +1,30 @@ +// include Catch2 +#include + +// what we are testing +#include "tools/concepts/IsRange.hpp" + +// other includes +#include +#include + +// convenience typedefs +using namespace njoy::tools::concepts; + +SCENARIO( "IsRange" ) { + + GIVEN( "different types" ) { + + WHEN( "when applying IsRange" ) { + + THEN( "true is returned for ranges and false for everything else" ) { + + CHECK( false == IsRange< int >::value ); + + CHECK( true == IsRange< std::vector< int > >::value ); + CHECK( true == IsRange< const std::vector< int > >::value ); + CHECK( true == IsRange< std::map< int, int > >::value ); + } // THEN + } // WHEN + } // GIVEN +} // SCENARIO diff --git a/src/tools/concepts/Requires.hpp b/src/tools/concepts/Requires.hpp new file mode 100644 index 0000000..50511e6 --- /dev/null +++ b/src/tools/concepts/Requires.hpp @@ -0,0 +1,20 @@ +#ifndef NJOY_TOOLS_CONCEPTS_REQUIRES +#define NJOY_TOOLS_CONCEPTS_REQUIRES + +// system includes +#include + +// other includes + +namespace njoy { +namespace tools { +namespace concepts { + + template< bool TrueFalse, template< typename...> class Concept, typename... T > + using Requires = typename std::enable_if< Concept< T... >::value == TrueFalse, bool >::type; + +} // ranges namespace +} // tools namespace +} // njoy namespace + +#endif diff --git a/src/tools/concepts/Requires/test/CMakeLists.txt b/src/tools/concepts/Requires/test/CMakeLists.txt new file mode 100644 index 0000000..fd6ea97 --- /dev/null +++ b/src/tools/concepts/Requires/test/CMakeLists.txt @@ -0,0 +1 @@ +add_cpp_test( concepts.Requires Requires.test.cpp ) diff --git a/src/tools/concepts/Requires/test/Requires.test.cpp b/src/tools/concepts/Requires/test/Requires.test.cpp new file mode 100644 index 0000000..5267501 --- /dev/null +++ b/src/tools/concepts/Requires/test/Requires.test.cpp @@ -0,0 +1,32 @@ +// include Catch2 +#include + +// what we are testing +#include "tools/concepts/Requires.hpp" + +// other includes + +// convenience typedefs +using namespace njoy::tools::concepts; + +template< typename T, Requires< true, std::is_integral, T > = true > +bool integer_type( const T& ){ return true; } + +template< typename T, Requires< false, std::is_integral, T > = true > +bool integer_type( const T& ){ return false; } + +SCENARIO( "Require" ) { + + GIVEN( "different types" ) { + + WHEN( "when applying Require" ) { + + THEN( "true is returned for integers and false for everything else" ) { + + CHECK( true == integer_type( 1 ) ); + CHECK( false == integer_type( 1.0 ) ); + CHECK( false == integer_type( std::vector< int >{} ) ); + } // THEN + } // WHEN + } // GIVEN +} // SCENARIO diff --git a/src/tools/ranges/IteratorView.hpp b/src/tools/ranges/IteratorView.hpp index ead8246..5a09759 100644 --- a/src/tools/ranges/IteratorView.hpp +++ b/src/tools/ranges/IteratorView.hpp @@ -6,6 +6,7 @@ #include // other includes +#include "tools/ranges/ViewBase.hpp" namespace njoy { namespace tools { @@ -17,11 +18,8 @@ namespace ranges { * * 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 { +template < typename Iterator > +class IteratorView : public ViewBase< IteratorView< Iterator > > { public: @@ -68,85 +66,6 @@ class IteratorView { * @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 ); - } }; /** diff --git a/src/tools/ranges/IteratorView/test/IteratorView.test.cpp b/src/tools/ranges/IteratorView/test/IteratorView.test.cpp index 3a35bcd..a129750 100644 --- a/src/tools/ranges/IteratorView/test/IteratorView.test.cpp +++ b/src/tools/ranges/IteratorView/test/IteratorView.test.cpp @@ -5,13 +5,72 @@ #include "tools/ranges/IteratorView.hpp" // other includes +#include +#include +#include // convenience typedefs using namespace njoy::tools::ranges; SCENARIO( "IteratorView" ) { - GIVEN( "a container with values" ) { + GIVEN( "a container with forward iterators" ) { + + std::forward_list< int > values = { -2, -1, 0, 1, 2 }; + + WHEN( "when iterators are used" ) { + + IteratorView< std::forward_list< int >::iterator > chunk( values.begin(), values.end() ); + + THEN( "an IteratorView can be constructed and members can be tested" ) { + + CHECK( values.begin() == chunk.begin() ); + CHECK( values.end() == chunk.end() ); + + CHECK( 5 == chunk.size() ); + CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); + + // the following should not compile: no random access iterator + // CHECK( -2 == chunk[0] ); + // CHECK( -2 == chunk.at(0) ); + + CHECK( -2 == chunk.front() ); + + // the following should not compile: no bidirectional iterator + // CHECK( 2 == chunk.back() ); + } // THEN + } // WHEN + } // GIVEN + + GIVEN( "a container with bidirectional iterators" ) { + + std::list< int > values = { -2, -1, 0, 1, 2 }; + + WHEN( "when iterators are used" ) { + + IteratorView< std::list< int >::iterator > chunk( values.begin(), values.end() ); + + THEN( "an IteratorView can be constructed and members can be tested" ) { + + CHECK( values.begin() == chunk.begin() ); + CHECK( values.end() == chunk.end() ); + + CHECK( 5 == chunk.size() ); + CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); + + // the following should not compile: no random access iterator + // CHECK( -2 == chunk[0] ); + // CHECK( -2 == chunk.at(0) ); + + CHECK( -2 == chunk.front() ); + CHECK( 2 == chunk.back() ); + } // THEN + } // WHEN + } // GIVEN + + GIVEN( "a container with random access iterators" ) { std::vector< int > values = { -2, -1, 0, 1, 2 }; @@ -21,8 +80,12 @@ SCENARIO( "IteratorView" ) { THEN( "an IteratorView can be constructed and members can be tested" ) { + CHECK( values.begin() == chunk.begin() ); + CHECK( values.end() == chunk.end() ); + CHECK( 5 == chunk.size() ); CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); CHECK( -2 == chunk[0] ); CHECK( -1 == chunk[1] ); @@ -62,7 +125,7 @@ SCENARIO( "IteratorView" ) { 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" ) { + WHEN( "when comparison functions are used" ) { THEN( "views and containers can be compared" ) { diff --git a/src/tools/ranges/ViewBase.hpp b/src/tools/ranges/ViewBase.hpp new file mode 100644 index 0000000..c07ea11 --- /dev/null +++ b/src/tools/ranges/ViewBase.hpp @@ -0,0 +1,170 @@ +#ifndef NJOY_TOOLS_RANGES_VIEWBASE +#define NJOY_TOOLS_RANGES_VIEWBASE + +// system includes +#include + +// other includes +#include "tools/concepts/Requires.hpp" +#include "tools/concepts/IsRange.hpp" +#include "tools/concepts/IsIterator.hpp" + +namespace njoy { +namespace tools { +namespace ranges { + +/** + * + * @brief A base class for views + * + * This CRTP base class assumes the Derived range has a begin() and end() + * method that define the begin and end iterator to the Derived range. + */ +template < typename Derived > +class ViewBase { + + constexpr Derived& derived() noexcept { + + return static_cast< Derived& >( *this ); + } + + constexpr const Derived& derived() const noexcept { + + return static_cast< const Derived& >( *this ); + } + +public: + + /* methods */ + + /** + * @brief Return whether or not the view is empty + */ + constexpr bool empty() const noexcept { + + static_assert( + concepts::IsForwardIterator< typename Derived::iterator >::value == true, + "the empty() method can only be made available for forward iterators" ); + + return this->derived().begin() == this->derived().end(); + } + + /** + * @brief Return whether or not the view is empty + */ + constexpr explicit operator bool() const noexcept { + + static_assert( + concepts::IsForwardIterator< typename Derived::iterator >::value == true, + "the operator bool() method can only be made available for forward iterators" ); + + return this->empty(); + } + + /** + * @brief Return the size of the iterator view + */ + constexpr std::size_t size() const noexcept { + + static_assert( + concepts::IsForwardIterator< typename Derived::iterator >::value == true, + "the size() method can only be made available for forward iterators" ); + + return std::distance( this->derived().begin(), this->derived().end() ); + } + + /** + * @brief Return the reference to the front element of the view + */ + constexpr decltype(auto) front() const noexcept { + + static_assert( + concepts::IsForwardIterator< typename Derived::iterator >::value == true, + "the front() method can only be made available for forward iterators" ); + + return *( this->derived().begin() ); + } + + /** + * @brief Return the reference to the back element of the view + */ + constexpr decltype(auto) back() const noexcept { + + static_assert( + concepts::IsBidirectionalIterator< typename Derived::iterator >::value == true, + "the back() method can only be made available for bidirectional iterators" ); + + return *( std::prev( this->derived().end() ) ); + } + + /** + * @brief Return an element at a given index + * + * No range checking is performed. + * + * @param[in] i the index + */ + constexpr decltype(auto) operator[]( std::size_t i ) const noexcept { + + static_assert( + concepts::IsRandomAccessIterator< typename Derived::iterator >::value == true, + "the operator[] method can only be made available for random access iterators" ); + + return *( std::next( this->derived().begin(), i ) ); + } + + /** + * @brief Return an element at a given index with range checking + * + * @param[in] i the index + */ + constexpr decltype(auto) at( std::size_t i ) const { + + static_assert( + concepts::IsRandomAccessIterator< typename Derived::iterator >::value == true, + "the at() method can only be made available for random access iterators" ); + + if ( i >= this->size() ) { + + throw std::out_of_range( "Index out of range: " + + 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, + typename Range = Derived, + concepts::Requires< true, concepts::IsInputIterator, + typename Range::iterator > = true > + constexpr bool operator==( const Container& other ) const { + + return std::equal( this->derived().begin(), this->derived().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, + typename Range = Derived, + concepts::Requires< true, concepts::IsInputIterator, + typename Range::iterator > = true > + constexpr bool operator!=( const Container& other ) const { + + return !this->operator==( other ); + } +}; + +} // ranges namespace +} // tools namespace +} // njoy namespace + +#endif diff --git a/src/tools/ranges/concepts.hpp b/src/tools/ranges/concepts.hpp new file mode 100644 index 0000000..84422dc --- /dev/null +++ b/src/tools/ranges/concepts.hpp @@ -0,0 +1,3 @@ +#include "tools/concepts/Requires.hpp" + +#include "tools/concepts/IsRange.hpp" diff --git a/src/tools/ranges/make_view/test/make_view.test.cpp b/src/tools/ranges/make_view/test/make_view.test.cpp index 715bf28..9634ce4 100644 --- a/src/tools/ranges/make_view/test/make_view.test.cpp +++ b/src/tools/ranges/make_view/test/make_view.test.cpp @@ -5,13 +5,118 @@ #include "tools/ranges/make_view.hpp" // other includes +#include +#include +#include // convenience typedefs using namespace njoy::tools::ranges; SCENARIO( "make_view" ) { - GIVEN( "a container with values" ) { + GIVEN( "a container with forward iterators" ) { + + std::forward_list< int > values = { -2, -1, 0, 1, 2 }; + + WHEN( "when using iterators" ) { + + auto chunk = make_view( values.begin(), values.end() ); + + THEN( "an IteratorView can be constructed and members can be tested" ) { + + CHECK( values.begin() == chunk.begin() ); + CHECK( values.end() == chunk.end() ); + + CHECK( 5 == chunk.size() ); + CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); + + // the following should not compile: no random access iterator + // CHECK( -2 == chunk[0] ); + // CHECK( -2 == chunk.at(0) ); + + CHECK( -2 == chunk.front() ); + + // the following should not compile: no bidirectional iterator + // CHECK( 2 == chunk.back() ); + } // THEN + } // WHEN + + WHEN( "when using the container" ) { + + auto chunk = make_view( values ); + + THEN( "an IteratorView can be constructed and members can be tested" ) { + + CHECK( values.begin() == chunk.begin() ); + CHECK( values.end() == chunk.end() ); + + CHECK( 5 == chunk.size() ); + CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); + + // the following should not compile: no random access iterator + // CHECK( -2 == chunk[0] ); + // CHECK( -2 == chunk.at(0) ); + + CHECK( -2 == chunk.front() ); + + // the following should not compile: no bidirectional iterator + // CHECK( 2 == chunk.back() ); + } // THEN + } // WHEN + } // GIVEN + + GIVEN( "a container with bidirectional iterators" ) { + + std::list< int > values = { -2, -1, 0, 1, 2 }; + + WHEN( "when using iterators" ) { + + auto chunk = make_view( values.begin(), values.end() ); + + THEN( "an IteratorView can be constructed and members can be tested" ) { + + CHECK( values.begin() == chunk.begin() ); + CHECK( values.end() == chunk.end() ); + + CHECK( 5 == chunk.size() ); + CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); + + // the following should not compile: no random access iterator + // CHECK( -2 == chunk[0] ); + // CHECK( -2 == chunk.at(0) ); + + CHECK( -2 == chunk.front() ); + CHECK( 2 == chunk.back() ); + } // THEN + } // WHEN + + WHEN( "when using the container" ) { + + auto chunk = make_view( values ); + + THEN( "an IteratorView can be constructed and members can be tested" ) { + + CHECK( values.begin() == chunk.begin() ); + CHECK( values.end() == chunk.end() ); + + CHECK( 5 == chunk.size() ); + CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); + + // the following should not compile: no random access iterator + // CHECK( -2 == chunk[0] ); + // CHECK( -2 == chunk.at(0) ); + + CHECK( -2 == chunk.front() ); + CHECK( 2 == chunk.back() ); + } // THEN + } // WHEN + } // GIVEN + + GIVEN( "a container with random access iterators" ) { std::vector< int > values = { -2, -1, 0, 1, 2 }; @@ -21,8 +126,12 @@ SCENARIO( "make_view" ) { THEN( "an IteratorView can be constructed and members can be tested" ) { + CHECK( values.begin() == chunk.begin() ); + CHECK( values.end() == chunk.end() ); + CHECK( 5 == chunk.size() ); CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); CHECK( -2 == chunk[0] ); CHECK( -1 == chunk[1] ); @@ -47,8 +156,12 @@ SCENARIO( "make_view" ) { THEN( "an IteratorView can be constructed and members can be tested" ) { + CHECK( values.begin() == chunk.begin() ); + CHECK( values.end() == chunk.end() ); + CHECK( 5 == chunk.size() ); CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); CHECK( -2 == chunk[0] ); CHECK( -1 == chunk[1] );