From 5ed53f0205348e7f43d018d073a8f674d74cb8d0 Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 1 Nov 2023 08:29:08 -0600 Subject: [PATCH 01/12] Adding some sfinae helper code --- cmake/unit_testing.cmake | 4 +++ src/tools/concepts.hpp | 4 +++ src/tools/concepts/IsIterator.hpp | 28 ++++++++++++++++ .../concepts/IsIterator/test/CMakeLists.txt | 1 + .../IsIterator/test/IsIterator.test.cpp | 31 ++++++++++++++++++ src/tools/concepts/IsRange.hpp | 26 +++++++++++++++ .../concepts/IsRange/test/CMakeLists.txt | 1 + .../concepts/IsRange/test/IsRange.test.cpp | 30 +++++++++++++++++ src/tools/concepts/Requires.hpp | 20 ++++++++++++ .../concepts/Requires/test/CMakeLists.txt | 1 + .../concepts/Requires/test/Requires.test.cpp | 32 +++++++++++++++++++ src/tools/ranges/concepts.hpp | 3 ++ 12 files changed, 181 insertions(+) create mode 100644 src/tools/concepts.hpp create mode 100644 src/tools/concepts/IsIterator.hpp create mode 100644 src/tools/concepts/IsIterator/test/CMakeLists.txt create mode 100644 src/tools/concepts/IsIterator/test/IsIterator.test.cpp create mode 100644 src/tools/concepts/IsRange.hpp create mode 100644 src/tools/concepts/IsRange/test/CMakeLists.txt create mode 100644 src/tools/concepts/IsRange/test/IsRange.test.cpp create mode 100644 src/tools/concepts/Requires.hpp create mode 100644 src/tools/concepts/Requires/test/CMakeLists.txt create mode 100644 src/tools/concepts/Requires/test/Requires.test.cpp create mode 100644 src/tools/ranges/concepts.hpp 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..3949e23 --- /dev/null +++ b/src/tools/concepts/IsIterator.hpp @@ -0,0 +1,28 @@ +#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; + + 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 {}; + +} // 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..91fe258 --- /dev/null +++ b/src/tools/concepts/IsIterator/test/IsIterator.test.cpp @@ -0,0 +1,31 @@ +// include Catch2 +#include + +// what we are testing +#include "tools/concepts/IsIterator.hpp" + +// other includes +#include +#include + +// convenience typedefs +using namespace njoy::tools::concepts; + +SCENARIO( "IsIterator" ) { + + GIVEN( "different types" ) { + + WHEN( "when applying IsIterator" ) { + + THEN( "true is returned for iterators and false for everything else" ) { + + CHECK( false == IsIterator< int >::value ); + + CHECK( true == IsIterator< std::vector< int >::iterator >::value ); + CHECK( true == IsIterator< std::vector< int >::const_iterator >::value ); + + CHECK( true == IsIterator< int* >::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..11fbf57 --- /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 = std::enable_if_t< Concept< T... >::value == TrueFalse, bool >; + +} // 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/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" From 1a050d11ed9301cd0b29e3b2a942106fdc3629b3 Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 1 Nov 2023 08:29:54 -0600 Subject: [PATCH 02/12] Introducing the ViewBase class --- src/tools/ranges/IteratorView.hpp | 94 ++++------------------ src/tools/ranges/ViewBase.hpp | 125 ++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 80 deletions(-) create mode 100644 src/tools/ranges/ViewBase.hpp diff --git a/src/tools/ranges/IteratorView.hpp b/src/tools/ranges/IteratorView.hpp index ead8246..21fbd45 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,14 @@ namespace ranges { * * Currently only defined for random access iterators. */ -template < class Iterator, +template < typename 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 { +class IteratorView : public ViewBase< IteratorView< Iterator > > { + + /* type aliases */ + using Parent = ViewBase< IteratorView< Iterator > >; public: @@ -69,84 +73,14 @@ class IteratorView { */ 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 ); - } + using Parent::front; + using Parent::back; + using Parent::empty; + using Parent::size; + using Parent::operator[]; + using Parent::at; + using Parent::operator==; + using Parent::operator!=; }; /** diff --git a/src/tools/ranges/ViewBase.hpp b/src/tools/ranges/ViewBase.hpp new file mode 100644 index 0000000..dfcb555 --- /dev/null +++ b/src/tools/ranges/ViewBase.hpp @@ -0,0 +1,125 @@ +#ifndef NJOY_TOOLS_RANGES_VIEWBASE +#define NJOY_TOOLS_RANGES_VIEWBASE + +// system includes +#include + +// other includes + +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 the reference to the front element of the view + */ + constexpr decltype(auto) front() const noexcept { + + return *( this->derived().begin() ); + } + + /** + * @brief Return the reference to the back element of the view + */ + constexpr decltype(auto) back() const noexcept { + + return *( std::prev( this->derived().end() ) ); + } + + /** + * @brief Return whether or not the view is empty + */ + constexpr bool empty() const noexcept { + + return this->derived().begin() == this->derived().end(); + } + + /** + * @brief Return the size of the iterator view + */ + constexpr std::size_t size() const noexcept { + + return std::distance( this->derived().begin(), 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 { + + 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 { + + 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 > + 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 > + constexpr bool operator!=( const Container& other ) const { + + return !this->operator==( other ); + } +}; + +} // ranges namespace +} // tools namespace +} // njoy namespace + +#endif From f9d6df5a9c7f84e50bcca03bc5040dd693735c5e Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 1 Nov 2023 11:21:03 -0600 Subject: [PATCH 03/12] Updated IsIterator concepts --- src/tools/concepts/IsIterator.hpp | 44 ++++- .../IsIterator/test/IsIterator.test.cpp | 154 +++++++++++++++++- src/tools/concepts/Requires.hpp | 2 +- 3 files changed, 193 insertions(+), 7 deletions(-) diff --git a/src/tools/concepts/IsIterator.hpp b/src/tools/concepts/IsIterator.hpp index 3949e23..1dfba6a 100644 --- a/src/tools/concepts/IsIterator.hpp +++ b/src/tools/concepts/IsIterator.hpp @@ -14,12 +14,52 @@ namespace concepts { 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 {}; + 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 diff --git a/src/tools/concepts/IsIterator/test/IsIterator.test.cpp b/src/tools/concepts/IsIterator/test/IsIterator.test.cpp index 91fe258..0bd420a 100644 --- a/src/tools/concepts/IsIterator/test/IsIterator.test.cpp +++ b/src/tools/concepts/IsIterator/test/IsIterator.test.cpp @@ -5,13 +5,19 @@ #include "tools/concepts/IsIterator.hpp" // other includes -#include +#include +#include +#include #include +#include +#include +#include +#include // convenience typedefs using namespace njoy::tools::concepts; -SCENARIO( "IsIterator" ) { +SCENARIO( "IsIterator sfinae objects" ) { GIVEN( "different types" ) { @@ -19,12 +25,152 @@ SCENARIO( "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 ); - CHECK( true == IsIterator< std::vector< int >::const_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 ); - CHECK( true == IsIterator< int* >::value ); + // random access iterator types + CHECK( true == IsRandomAccessIterator< std::vector< int >::iterator >::value ); } // THEN } // WHEN } // GIVEN diff --git a/src/tools/concepts/Requires.hpp b/src/tools/concepts/Requires.hpp index 11fbf57..50511e6 100644 --- a/src/tools/concepts/Requires.hpp +++ b/src/tools/concepts/Requires.hpp @@ -11,7 +11,7 @@ namespace tools { namespace concepts { template< bool TrueFalse, template< typename...> class Concept, typename... T > - using Requires = std::enable_if_t< Concept< T... >::value == TrueFalse, bool >; + using Requires = typename std::enable_if< Concept< T... >::value == TrueFalse, bool >::type; } // ranges namespace } // tools namespace From 2d88698ae2a5c7cb6e7c62e74693e5822f01d3ca Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 1 Nov 2023 11:23:37 -0600 Subject: [PATCH 04/12] Adding concepts to ViewBase methods --- src/tools/ranges/ViewBase.hpp | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/tools/ranges/ViewBase.hpp b/src/tools/ranges/ViewBase.hpp index dfcb555..e0d9865 100644 --- a/src/tools/ranges/ViewBase.hpp +++ b/src/tools/ranges/ViewBase.hpp @@ -5,6 +5,9 @@ #include // other includes +#include "tools/concepts/Requires.hpp" +#include "tools/concepts/IsRange.hpp" +#include "tools/concepts/IsIterator.hpp" namespace njoy { namespace tools { @@ -22,13 +25,13 @@ class ViewBase { constexpr Derived& derived() noexcept { - return static_cast< Derived& >( *this ); - } + return static_cast< Derived& >( *this ); + } - constexpr const Derived& derived() const noexcept { + constexpr const Derived& derived() const noexcept { - return static_cast< const Derived& >( *this ); - } + return static_cast< const Derived& >( *this ); + } public: @@ -37,6 +40,9 @@ class ViewBase { /** * @brief Return the reference to the front element of the view */ + template < typename Range = Derived, + concepts::Requires< true, concepts::IsForwardIterator, + typename Range::iterator > = true > constexpr decltype(auto) front() const noexcept { return *( this->derived().begin() ); @@ -45,6 +51,9 @@ class ViewBase { /** * @brief Return the reference to the back element of the view */ + template < typename Range = Derived, + concepts::Requires< true, concepts::IsForwardIterator, + typename Range::iterator > = true > constexpr decltype(auto) back() const noexcept { return *( std::prev( this->derived().end() ) ); @@ -53,6 +62,9 @@ class ViewBase { /** * @brief Return whether or not the view is empty */ + template < typename Range = Derived, + concepts::Requires< true, concepts::IsForwardIterator, + typename Range::iterator > = true > constexpr bool empty() const noexcept { return this->derived().begin() == this->derived().end(); @@ -61,6 +73,9 @@ class ViewBase { /** * @brief Return the size of the iterator view */ + template < typename Range = Derived, + concepts::Requires< true, concepts::IsForwardIterator, + typename Range::iterator > = true > constexpr std::size_t size() const noexcept { return std::distance( this->derived().begin(), this->derived().end() ); @@ -73,6 +88,9 @@ class ViewBase { * * @param[in] i the index */ + template < typename Range = Derived, + concepts::Requires< true, concepts::IsRandomAccessIterator, + typename Range::iterator > = true > constexpr decltype(auto) operator[]( std::size_t i ) const noexcept { return *( std::next( this->derived().begin(), i ) ); @@ -83,6 +101,9 @@ class ViewBase { * * @param[in] i the index */ + template < typename Range = Derived, + concepts::Requires< true, concepts::IsRandomAccessIterator, + typename Range::iterator > = true > constexpr decltype(auto) at( std::size_t i ) const { if ( i >= this->size() ) { From 208a0631f5b797c012da7dee7d6b7d5e1e31a9a4 Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 1 Nov 2023 11:56:31 -0600 Subject: [PATCH 05/12] Updating IteratorView and tests --- src/tools/ranges/IteratorView.hpp | 17 +----- .../IteratorView/test/IteratorView.test.cpp | 36 +++++++++++- .../ranges/make_view/test/make_view.test.cpp | 58 ++++++++++++++++++- 3 files changed, 92 insertions(+), 19 deletions(-) diff --git a/src/tools/ranges/IteratorView.hpp b/src/tools/ranges/IteratorView.hpp index 21fbd45..5a09759 100644 --- a/src/tools/ranges/IteratorView.hpp +++ b/src/tools/ranges/IteratorView.hpp @@ -18,15 +18,9 @@ namespace ranges { * * Currently only defined for random access iterators. */ -template < typename Iterator, - std::enable_if_t< - std::is_same< typename std::iterator_traits< Iterator >::iterator_category, - std::random_access_iterator_tag >::value, bool > = true > +template < typename Iterator > class IteratorView : public ViewBase< IteratorView< Iterator > > { - /* type aliases */ - using Parent = ViewBase< IteratorView< Iterator > >; - public: using value_type = typename std::iterator_traits< Iterator >::value_type; @@ -72,15 +66,6 @@ class IteratorView : public ViewBase< IteratorView< Iterator > > { * @brief Return the end iterator to the view */ constexpr iterator end() const noexcept { return end_; } - - using Parent::front; - using Parent::back; - using Parent::empty; - using Parent::size; - using Parent::operator[]; - using Parent::at; - using Parent::operator==; - using Parent::operator!=; }; /** diff --git a/src/tools/ranges/IteratorView/test/IteratorView.test.cpp b/src/tools/ranges/IteratorView/test/IteratorView.test.cpp index 3a35bcd..11e8b25 100644 --- a/src/tools/ranges/IteratorView/test/IteratorView.test.cpp +++ b/src/tools/ranges/IteratorView/test/IteratorView.test.cpp @@ -5,13 +5,42 @@ #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 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() ); + + // 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,6 +50,9 @@ 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() ); @@ -62,7 +94,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/make_view/test/make_view.test.cpp b/src/tools/ranges/make_view/test/make_view.test.cpp index 715bf28..9b4ec32 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,63 @@ #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 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() ); + + // 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() ); + + // 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,6 +71,9 @@ 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() ); @@ -47,6 +100,9 @@ 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() ); From fa457c7867a69a6531d214ae46ee70ed382cf5af Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 1 Nov 2023 12:04:24 -0600 Subject: [PATCH 06/12] Adding requirement to operator== and operator!= --- src/tools/ranges/ViewBase.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tools/ranges/ViewBase.hpp b/src/tools/ranges/ViewBase.hpp index e0d9865..63224d9 100644 --- a/src/tools/ranges/ViewBase.hpp +++ b/src/tools/ranges/ViewBase.hpp @@ -120,7 +120,10 @@ class ViewBase { * * @param[in] other the other container to compare with */ - template < typename Container > + 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(), @@ -132,7 +135,10 @@ class ViewBase { * * @param[in] other the other container to compare with */ - template < typename Container > + 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 ); From 1a88c746215f4ce33bb87a9b935f8aa977aeb269 Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 1 Nov 2023 12:26:14 -0600 Subject: [PATCH 07/12] Added assertions --- src/tools/ranges/ViewBase.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/ranges/ViewBase.hpp b/src/tools/ranges/ViewBase.hpp index 63224d9..f0f65e3 100644 --- a/src/tools/ranges/ViewBase.hpp +++ b/src/tools/ranges/ViewBase.hpp @@ -2,6 +2,7 @@ #define NJOY_TOOLS_RANGES_VIEWBASE // system includes +#include #include // other includes @@ -45,6 +46,7 @@ class ViewBase { typename Range::iterator > = true > constexpr decltype(auto) front() const noexcept { + assert( ( !this->empty(), "front(): view is empty" ) ); return *( this->derived().begin() ); } @@ -56,6 +58,7 @@ class ViewBase { typename Range::iterator > = true > constexpr decltype(auto) back() const noexcept { + assert( ( !this->empty(), "back(): view is empty" ) ); return *( std::prev( this->derived().end() ) ); } From 6ca45d8bddeeb6d046e18d7975020fc20ae492ad Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 1 Nov 2023 12:33:29 -0600 Subject: [PATCH 08/12] Removed assertions since they generate compiler warnings --- src/tools/ranges/ViewBase.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tools/ranges/ViewBase.hpp b/src/tools/ranges/ViewBase.hpp index f0f65e3..63224d9 100644 --- a/src/tools/ranges/ViewBase.hpp +++ b/src/tools/ranges/ViewBase.hpp @@ -2,7 +2,6 @@ #define NJOY_TOOLS_RANGES_VIEWBASE // system includes -#include #include // other includes @@ -46,7 +45,6 @@ class ViewBase { typename Range::iterator > = true > constexpr decltype(auto) front() const noexcept { - assert( ( !this->empty(), "front(): view is empty" ) ); return *( this->derived().begin() ); } @@ -58,7 +56,6 @@ class ViewBase { typename Range::iterator > = true > constexpr decltype(auto) back() const noexcept { - assert( ( !this->empty(), "back(): view is empty" ) ); return *( std::prev( this->derived().end() ) ); } From 10c5262dc1ad1a03ebe9b69569030dd1e220cf4f Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 1 Nov 2023 13:04:41 -0600 Subject: [PATCH 09/12] ViewBase::back() should be enabled for bidirectional iterators, not forward --- .../IteratorView/test/IteratorView.test.cpp | 28 ++++++++++ src/tools/ranges/ViewBase.hpp | 2 +- .../ranges/make_view/test/make_view.test.cpp | 51 +++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/tools/ranges/IteratorView/test/IteratorView.test.cpp b/src/tools/ranges/IteratorView/test/IteratorView.test.cpp index 11e8b25..7e63e11 100644 --- a/src/tools/ranges/IteratorView/test/IteratorView.test.cpp +++ b/src/tools/ranges/IteratorView/test/IteratorView.test.cpp @@ -14,6 +14,34 @@ using namespace njoy::tools::ranges; SCENARIO( "IteratorView" ) { + 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() ); + + // 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 }; diff --git a/src/tools/ranges/ViewBase.hpp b/src/tools/ranges/ViewBase.hpp index 63224d9..47ba7dc 100644 --- a/src/tools/ranges/ViewBase.hpp +++ b/src/tools/ranges/ViewBase.hpp @@ -52,7 +52,7 @@ class ViewBase { * @brief Return the reference to the back element of the view */ template < typename Range = Derived, - concepts::Requires< true, concepts::IsForwardIterator, + concepts::Requires< true, concepts::IsBidirectionalIterator, typename Range::iterator > = true > constexpr decltype(auto) back() const noexcept { 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 9b4ec32..c877ddc 100644 --- a/src/tools/ranges/make_view/test/make_view.test.cpp +++ b/src/tools/ranges/make_view/test/make_view.test.cpp @@ -14,6 +14,57 @@ using namespace njoy::tools::ranges; SCENARIO( "make_view" ) { + 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() ); + + // 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() ); + + // 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 }; From 6cea849a1f5de24a9b34225db10a952568c08edb Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 1 Nov 2023 13:23:43 -0600 Subject: [PATCH 10/12] adding the operator bool method to ViewBase to satisfy the C++20 view_interface interface --- .../IteratorView/test/IteratorView.test.cpp | 3 ++ src/tools/ranges/ViewBase.hpp | 37 ++++++++++++------- .../ranges/make_view/test/make_view.test.cpp | 6 +++ 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/tools/ranges/IteratorView/test/IteratorView.test.cpp b/src/tools/ranges/IteratorView/test/IteratorView.test.cpp index 7e63e11..a129750 100644 --- a/src/tools/ranges/IteratorView/test/IteratorView.test.cpp +++ b/src/tools/ranges/IteratorView/test/IteratorView.test.cpp @@ -29,6 +29,7 @@ SCENARIO( "IteratorView" ) { 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] ); @@ -57,6 +58,7 @@ SCENARIO( "IteratorView" ) { 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] ); @@ -83,6 +85,7 @@ SCENARIO( "IteratorView" ) { CHECK( 5 == chunk.size() ); CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); CHECK( -2 == chunk[0] ); CHECK( -1 == chunk[1] ); diff --git a/src/tools/ranges/ViewBase.hpp b/src/tools/ranges/ViewBase.hpp index 47ba7dc..52dfa9a 100644 --- a/src/tools/ranges/ViewBase.hpp +++ b/src/tools/ranges/ViewBase.hpp @@ -38,47 +38,58 @@ class ViewBase { /* methods */ /** - * @brief Return the reference to the front element of the view + * @brief Return whether or not the view is empty */ template < typename Range = Derived, concepts::Requires< true, concepts::IsForwardIterator, typename Range::iterator > = true > - constexpr decltype(auto) front() const noexcept { + constexpr bool empty() const noexcept { - return *( this->derived().begin() ); + return this->derived().begin() == this->derived().end(); } /** - * @brief Return the reference to the back element of the view + * @brief Return whether or not the view is empty */ template < typename Range = Derived, - concepts::Requires< true, concepts::IsBidirectionalIterator, + concepts::Requires< true, concepts::IsForwardIterator, typename Range::iterator > = true > - constexpr decltype(auto) back() const noexcept { + constexpr explicit operator bool() const noexcept { - return *( std::prev( this->derived().end() ) ); + return this->empty(); } /** - * @brief Return whether or not the view is empty + * @brief Return the size of the iterator view */ template < typename Range = Derived, concepts::Requires< true, concepts::IsForwardIterator, typename Range::iterator > = true > - constexpr bool empty() const noexcept { + constexpr std::size_t size() const noexcept { - return this->derived().begin() == this->derived().end(); + return std::distance( this->derived().begin(), this->derived().end() ); } /** - * @brief Return the size of the iterator view + * @brief Return the reference to the front element of the view */ template < typename Range = Derived, concepts::Requires< true, concepts::IsForwardIterator, typename Range::iterator > = true > - constexpr std::size_t size() const noexcept { + constexpr decltype(auto) front() const noexcept { - return std::distance( this->derived().begin(), this->derived().end() ); + return *( this->derived().begin() ); + } + + /** + * @brief Return the reference to the back element of the view + */ + template < typename Range = Derived, + concepts::Requires< true, concepts::IsBidirectionalIterator, + typename Range::iterator > = true > + constexpr decltype(auto) back() const noexcept { + + return *( std::prev( this->derived().end() ) ); } /** 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 c877ddc..9634ce4 100644 --- a/src/tools/ranges/make_view/test/make_view.test.cpp +++ b/src/tools/ranges/make_view/test/make_view.test.cpp @@ -29,6 +29,7 @@ SCENARIO( "make_view" ) { 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] ); @@ -52,6 +53,7 @@ SCENARIO( "make_view" ) { 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] ); @@ -80,6 +82,7 @@ SCENARIO( "make_view" ) { 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] ); @@ -101,6 +104,7 @@ SCENARIO( "make_view" ) { 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] ); @@ -127,6 +131,7 @@ SCENARIO( "make_view" ) { CHECK( 5 == chunk.size() ); CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); CHECK( -2 == chunk[0] ); CHECK( -1 == chunk[1] ); @@ -156,6 +161,7 @@ SCENARIO( "make_view" ) { CHECK( 5 == chunk.size() ); CHECK( false == chunk.empty() ); + CHECK( false == bool( chunk ) ); CHECK( -2 == chunk[0] ); CHECK( -1 == chunk[1] ); From e7621b2abddbe805f8a638cf701cc70f8eb71778 Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 1 Nov 2023 14:28:59 -0600 Subject: [PATCH 11/12] Adding code to simplify error messages related to sfinae --- src/tools/ranges/ViewBase.hpp | 79 +++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/tools/ranges/ViewBase.hpp b/src/tools/ranges/ViewBase.hpp index 52dfa9a..f9ea6dd 100644 --- a/src/tools/ranges/ViewBase.hpp +++ b/src/tools/ranges/ViewBase.hpp @@ -154,6 +154,85 @@ class ViewBase { return !this->operator==( other ); } + + // the following code is added to make compiler errors more comprehensible + + template < typename Range = Derived, + concepts::Requires< false, concepts::IsForwardIterator, + typename Range::iterator > = true > + constexpr bool empty() const noexcept { + + static_assert( + concepts::IsForwardIterator< typename Range::iterator >::value == true, + "the empty() method can only be made available for forward iterators" ); + return true; + } + + template < typename Range = Derived, + concepts::Requires< false, concepts::IsForwardIterator, + typename Range::iterator > = true > + constexpr explicit operator bool() const noexcept { + + static_assert( + concepts::IsForwardIterator< typename Range::iterator >::value == true, + "the operator bool() method can only be made available for forward iterators" ); + return true; + } + + template < typename Range = Derived, + concepts::Requires< false, concepts::IsForwardIterator, + typename Range::iterator > = true > + constexpr std::size_t size() const noexcept { + + static_assert( + concepts::IsForwardIterator< typename Range::iterator >::value == true, + "the size() method can only be made available for forward iterators" ); + return std::size_t{}; + } + + template < typename Range = Derived, + concepts::Requires< false, concepts::IsForwardIterator, + typename Range::iterator > = true > + constexpr decltype(auto) front() const noexcept { + + static_assert( + concepts::IsBidirectionalIterator< typename Range::iterator >::value == true, + "the front() method can only be made available for bidirectional iterators" ); + return typename Derived::value_type{}; + } + + template < typename Range = Derived, + concepts::Requires< false, concepts::IsBidirectionalIterator, + typename Range::iterator > = true > + constexpr decltype(auto) back() const noexcept { + + static_assert( + concepts::IsBidirectionalIterator< typename Range::iterator >::value == true, + "the back() method can only be made available for bidirectional iterators" ); + return typename Derived::value_type{}; + } + + template < typename Range = Derived, + concepts::Requires< false, concepts::IsRandomAccessIterator, + typename Range::iterator > = true > + constexpr decltype(auto) operator[]( std::size_t i ) const { + + static_assert( + concepts::IsRandomAccessIterator< typename Range::iterator >::value == true, + "the operator[] method can only be made available for random access iterators" ); + return typename Derived::value_type{}; + } + + template < typename Range = Derived, + concepts::Requires< false, concepts::IsRandomAccessIterator, + typename Range::iterator > = true > + constexpr decltype(auto) at( std::size_t i ) const { + + static_assert( + concepts::IsRandomAccessIterator< typename Range::iterator >::value == true, + "the at() method can only be made available for random access iterators" ); + return typename Derived::value_type{}; + } }; } // ranges namespace From d6e0893a4b2d1448c95021ada86a3562b5438ee8 Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Mon, 6 Nov 2023 17:30:25 -0700 Subject: [PATCH 12/12] Replacing sfinae with static_assert --- src/tools/ranges/ViewBase.hpp | 128 ++++++++-------------------------- 1 file changed, 28 insertions(+), 100 deletions(-) diff --git a/src/tools/ranges/ViewBase.hpp b/src/tools/ranges/ViewBase.hpp index f9ea6dd..c07ea11 100644 --- a/src/tools/ranges/ViewBase.hpp +++ b/src/tools/ranges/ViewBase.hpp @@ -40,55 +40,60 @@ class ViewBase { /** * @brief Return whether or not the view is empty */ - template < typename Range = Derived, - concepts::Requires< true, concepts::IsForwardIterator, - typename Range::iterator > = true > 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 */ - template < typename Range = Derived, - concepts::Requires< true, concepts::IsForwardIterator, - typename Range::iterator > = true > 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 */ - template < typename Range = Derived, - concepts::Requires< true, concepts::IsForwardIterator, - typename Range::iterator > = true > 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 */ - template < typename Range = Derived, - concepts::Requires< true, concepts::IsForwardIterator, - typename Range::iterator > = true > 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 */ - template < typename Range = Derived, - concepts::Requires< true, concepts::IsBidirectionalIterator, - typename Range::iterator > = true > 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() ) ); } @@ -99,11 +104,12 @@ class ViewBase { * * @param[in] i the index */ - template < typename Range = Derived, - concepts::Requires< true, concepts::IsRandomAccessIterator, - typename Range::iterator > = true > 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 ) ); } @@ -112,11 +118,12 @@ class ViewBase { * * @param[in] i the index */ - template < typename Range = Derived, - concepts::Requires< true, concepts::IsRandomAccessIterator, - typename Range::iterator > = true > 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: " + @@ -154,85 +161,6 @@ class ViewBase { return !this->operator==( other ); } - - // the following code is added to make compiler errors more comprehensible - - template < typename Range = Derived, - concepts::Requires< false, concepts::IsForwardIterator, - typename Range::iterator > = true > - constexpr bool empty() const noexcept { - - static_assert( - concepts::IsForwardIterator< typename Range::iterator >::value == true, - "the empty() method can only be made available for forward iterators" ); - return true; - } - - template < typename Range = Derived, - concepts::Requires< false, concepts::IsForwardIterator, - typename Range::iterator > = true > - constexpr explicit operator bool() const noexcept { - - static_assert( - concepts::IsForwardIterator< typename Range::iterator >::value == true, - "the operator bool() method can only be made available for forward iterators" ); - return true; - } - - template < typename Range = Derived, - concepts::Requires< false, concepts::IsForwardIterator, - typename Range::iterator > = true > - constexpr std::size_t size() const noexcept { - - static_assert( - concepts::IsForwardIterator< typename Range::iterator >::value == true, - "the size() method can only be made available for forward iterators" ); - return std::size_t{}; - } - - template < typename Range = Derived, - concepts::Requires< false, concepts::IsForwardIterator, - typename Range::iterator > = true > - constexpr decltype(auto) front() const noexcept { - - static_assert( - concepts::IsBidirectionalIterator< typename Range::iterator >::value == true, - "the front() method can only be made available for bidirectional iterators" ); - return typename Derived::value_type{}; - } - - template < typename Range = Derived, - concepts::Requires< false, concepts::IsBidirectionalIterator, - typename Range::iterator > = true > - constexpr decltype(auto) back() const noexcept { - - static_assert( - concepts::IsBidirectionalIterator< typename Range::iterator >::value == true, - "the back() method can only be made available for bidirectional iterators" ); - return typename Derived::value_type{}; - } - - template < typename Range = Derived, - concepts::Requires< false, concepts::IsRandomAccessIterator, - typename Range::iterator > = true > - constexpr decltype(auto) operator[]( std::size_t i ) const { - - static_assert( - concepts::IsRandomAccessIterator< typename Range::iterator >::value == true, - "the operator[] method can only be made available for random access iterators" ); - return typename Derived::value_type{}; - } - - template < typename Range = Derived, - concepts::Requires< false, concepts::IsRandomAccessIterator, - typename Range::iterator > = true > - constexpr decltype(auto) at( std::size_t i ) const { - - static_assert( - concepts::IsRandomAccessIterator< typename Range::iterator >::value == true, - "the at() method can only be made available for random access iterators" ); - return typename Derived::value_type{}; - } }; } // ranges namespace