diff --git a/include/boost/range/detail/range_return.hpp b/include/boost/range/detail/range_return.hpp index 9b98e099c..c78d009f7 100644 --- a/include/boost/range/detail/range_return.hpp +++ b/include/boost/range/detail/range_return.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace boost @@ -19,16 +20,26 @@ namespace boost enum range_return_value { // (*) indicates the most common values - return_found, // only the found resulting iterator (*) - return_next, // next(found) iterator - return_prior, // prior(found) iterator - return_begin_found, // [begin, found) range (*) - return_begin_next, // [begin, next(found)) range - return_begin_prior, // [begin, prior(found)) range - return_found_end, // [found, end) range (*) - return_next_end, // [next(found), end) range - return_prior_end, // [prior(found), end) range - return_begin_end // [begin, end) range + return_found, // only the found resulting iterator (*) + return_next, // next(found) iterator + return_prior, // prior(found) iterator + return_begin_found, // [begin, found) range (*) + return_begin_next, // [begin, next(found)) range + return_begin_prior, // [begin, prior(found)) range + return_found_end, // [found, end) range (*) + return_next_end, // [next(found), end) range + return_prior_end, // [prior(found), end) range + return_begin_end, // [begin, end) range + return_root_found, // only the found resulting root iterator (*) + return_root_next, // next(found) root iterator + return_root_prior, // prior(found) root iterator + return_root_begin_found, // [begin, found) root iterator range (*) + return_root_begin_next, // [begin, next(found)) root iterator range + return_root_begin_prior, // [begin, prior(found)) root iterator range + return_root_found_end, // [found, end) root iterator range (*) + return_root_next_end, // [next(found), end) root iterator range + return_root_prior_end, // [prior(found), end) root iterator range + return_root_begin_end, // [begin, end) root iterator range }; template< class SinglePassRange, range_return_value > @@ -176,6 +187,165 @@ namespace boost } }; + template< class SinglePassRange > + struct range_return< SinglePassRange, return_root_found > + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator; + // range return type + typedef root_iterator type; + + static type pack(iterator found, SinglePassRange&) + { + return root_of(found); + } + }; + + template< class SinglePassRange > + struct range_return< SinglePassRange, return_root_next > + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator; + // range return type + typedef root_iterator type; + + static type pack(iterator found, SinglePassRange& rng) + { + return found == boost::end(rng) + ? root_of(found) + : root_of(boost::next(found)); + } + }; + + template< class BidirectionalRange > + struct range_return< BidirectionalRange, return_root_prior > + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator; + // range return type + typedef root_iterator type; + + static type pack(iterator found, BidirectionalRange& rng) + { + return found == boost::begin(rng) + ? root_of(found) + : root_of(boost::prior(found)); + } + }; + + template< class SinglePassRange > + struct range_return< SinglePassRange, return_root_begin_found > + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator; + // range return type + typedef iterator_range type; + + static type pack(iterator found, SinglePassRange& rng) + { + return type( root_of(boost::begin(rng)), + root_of(found) ); + } + }; + + template< class SinglePassRange > + struct range_return< SinglePassRange, return_root_begin_next > + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator; + // range return type + typedef iterator_range type; + + static type pack(iterator found, SinglePassRange& rng) + { + return type( root_of(boost::begin(rng)), + found == boost::end(rng) + ? root_of(found) + : root_of(boost::next(found)) ); + } + }; + + template< class BidirectionalRange > + struct range_return< BidirectionalRange, return_root_begin_prior > + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator; + // range return type + typedef iterator_range type; + + static type pack(iterator found, BidirectionalRange& rng) + { + return type( root_of(boost::begin(rng)), + found == boost::begin(rng) + ? root_of(found) + : root_of(boost::prior(found)) ); + } + }; + + template< class SinglePassRange > + struct range_return< SinglePassRange, return_root_found_end > + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator; + // range return type + typedef iterator_range type; + + static type pack(iterator found, SinglePassRange& rng) + { + return type( root_of(found), + root_of(boost::end(rng)) ); + } + }; + + template< class SinglePassRange > + struct range_return< SinglePassRange, return_root_next_end > + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator; + // range return type + typedef iterator_range type; + + static type pack(iterator found, SinglePassRange& rng) + { + return type( found == boost::end(rng) + ? root_of(found) + : root_of(boost::next(found)), + root_of(boost::end(rng)) ); + } + }; + + template< class BidirectionalRange > + struct range_return< BidirectionalRange, return_root_prior_end > + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator; + // range return type + typedef iterator_range type; + + static type pack(iterator found, BidirectionalRange& rng) + { + return type( found == boost::begin(rng) + ? root_of(found) + : root_of(boost::prior(found)), + root_of(boost::end(rng)) ); + } + }; + + template< class SinglePassRange > + struct range_return< SinglePassRange, return_root_begin_end > + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator; + // range return type + typedef iterator_range type; + + static type pack(iterator, SinglePassRange& rng) + { + return type( root_of(boost::begin(rng)), + root_of(boost::end(rng)) ); + } + }; + } #endif // include guard diff --git a/include/boost/range/root_iterator_of.hpp b/include/boost/range/root_iterator_of.hpp new file mode 100644 index 000000000..9af3b162f --- /dev/null +++ b/include/boost/range/root_iterator_of.hpp @@ -0,0 +1,91 @@ +// Copyright Klaus Triendl 2020. Use, modification and +// distribution is subject to the Boost Software License, Version +// 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// +// For more information, see http://www.boost.org/libs/range/ +// +#if defined(_MSC_VER) +#pragma once +#endif + +#ifndef BOOST_RANGE_ROOT_ITERATOR_OF_HPP_INCLUDED +#define BOOST_RANGE_ROOT_ITERATOR_OF_HPP_INCLUDED + +#include // BOOST_DEDUCED_TYPENAME +#include +#include + + +namespace boost +{ + + namespace range_detail + { + + BOOST_MPL_HAS_XXX_TRAIT_DEF(base_type) + + template::value> + struct root_iterator_of_impl; + + template + struct root_iterator_of_impl + { + typedef It type; + }; + + template + struct root_iterator_of_impl : + root_iterator_of_impl + {}; + + template + It root_of(It it, boost::mpl::bool_) + { + return it; + } + template + BOOST_DEDUCED_TYPENAME root_iterator_of_impl::type + root_of(It it, boost::mpl::bool_) + { + return root_of(it.base()); + } + + } + + namespace range + { + + template + struct root_iterator_of : + range_detail::root_iterator_of_impl + {}; + +#ifdef __cpp_alias_templates + template + using root_iterator_of_t = typename root_iterator_of::type; +#endif + + template + BOOST_DEDUCED_TYPENAME root_iterator_of::type + root_of(It it) + { + return range_detail::root_of( it, + BOOST_DEDUCED_TYPENAME range_detail::has_base_type::type{} ); + } + + template + iterator_range::type> + root_of(iterator_range rng) + { + return make_iterator_range(root_of(rng.begin()), root_of(rng.end())); + } + + } + using range::root_iterator_of; + using range::root_of; + +} + +#endif // include guard diff --git a/test/algorithm_test/find_return_root.cpp b/test/algorithm_test/find_return_root.cpp new file mode 100644 index 000000000..46a626df0 --- /dev/null +++ b/test/algorithm_test/find_return_root.cpp @@ -0,0 +1,152 @@ +// Boost.Range library +// +// Copyright Klaus Triendl 2020. Use, modification and +// distribution is subject to the Boost Software License, Version +// 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// +// For more information, see http://www.boost.org/libs/range/ +// +#include +#include + +#include +#include + +#include +#include "../test_driver/range_return_root_test_driver.hpp" +#include +#include +#include +#include +#include +#include + +namespace boost_range_test_algorithm_find_return_root +{ + class find_return_root_test_policy + { + public: + template + BOOST_DEDUCED_TYPENAME boost::root_iterator_of::type + test_iter(View& rng) + { + typedef BOOST_DEDUCED_TYPENAME boost::root_iterator_of::type iter_t; + iter_t result = boost::find(rng, 3); + iter_t result2 = boost::find(boost::make_iterator_range(rng), 3); + BOOST_CHECK( result == result2 ); + return root_of(result); + } + + template + struct test_range + { + template + BOOST_DEDUCED_TYPENAME boost::range_return::type + operator()(Policy&, View& rng) + { + typedef BOOST_DEDUCED_TYPENAME boost::range_return::type result_t; + result_t result = boost::find(rng, 3); + result_t result2 = boost::find(boost::make_iterator_range(rng), 3); + BOOST_CHECK( result == result2 ); + return result; + } + }; + + template + BOOST_DEDUCED_TYPENAME boost::root_iterator_of::type + reference(View& rng) + { + return root_of( std::find(boost::begin(rng), boost::end(rng), 3) ); + } + }; + + template + void test_find_container() + { + using namespace boost::assign; + + typedef BOOST_DEDUCED_TYPENAME boost::remove_const::type container_t; + typedef boost::reversed_range view_t; + + boost::range_test::range_return_root_test_driver test_driver; + + container_t mcont; + Container& cont = mcont; + view_t v1(cont); + test_driver(v1, find_test_policy()); + + mcont.clear(); + mcont += 1; + view_t v2(cont); + test_driver(v2, find_test_policy()); + + mcont.clear(); + mcont += 1,2,3,4,5,6,7,8,9; + view_t v3(cont); + test_driver(v3, find_test_policy()); + } + + void test_find() + { + test_find_container< std::vector >(); + test_find_container< std::list >(); + test_find_container< std::deque >(); + + test_find_container< const std::vector >(); + test_find_container< const std::list >(); + test_find_container< const std::deque >(); + + std::vector vi(0); + const std::vector& cvi = vi; + typedef std::vector::const_iterator it_t; + typedef boost::iterator_range itrng_t; + it_t it1_1 = root_of( boost::find(vi | boost::adaptors::reversed, 0) ); + it_t it2_1 = root_of( boost::find(cvi | boost::adaptors::reversed, 0) ); + BOOST_CHECK( it1_1 == it2_1 ); + it_t it1_2 = root_of( boost::find(vi | boost::adaptors::reversed, 0) ); + it_t it2_2 = root_of( boost::find(cvi | boost::adaptors::reversed, 0) ); + BOOST_CHECK( it1_2 == it2_2 ); + BOOST_CHECK( it1_1 == it1_2 ); + BOOST_CHECK( it2_1 == it2_2 ); + + itrng_t itrng1_1 = root_of( boost::find(vi | boost::adaptors::reversed, 0) ); + itrng_t itrng2_1 = root_of( boost::find(cvi | boost::adaptors::reversed, 0) ); + BOOST_CHECK( itrng1_1 == itrng2_1 ); + itrng_t itrng1_2 = boost::find(vi | boost::adaptors::reversed, 0); + itrng_t itrng2_2 = boost::find(cvi | boost::adaptors::reversed, 0); + BOOST_CHECK( itrng1_2 == itrng2_2 ); + BOOST_CHECK( itrng1_1 == itrng1_2 ); + BOOST_CHECK( itrng2_1 == itrng2_2 ); + } + + // The find algorithm can be used like a "contains" algorithm + // since the returned iterator_range is convertible to bool. + // Therefore if the return value is an empty range it will + // convert to the equivalent to "false" whereas a range that + // is not empty will convert to "true". Therefore one can + // use the syntax boost::find(rng, x) + // as a contains function. + void test_find_as_contains() + { + std::list l; + for (int i = 0; i < 10; ++i) + l.push_back(i); + + BOOST_CHECK(boost::find(l | boost::adaptors::reversed, 3)); + BOOST_CHECK(!boost::find(l | boost::adaptors::reversed, 10)); + } +} + +boost::unit_test::test_suite* +init_unit_test_suite(int argc, char* argv[]) +{ + boost::unit_test::test_suite* test + = BOOST_TEST_SUITE( "RangeTestSuite.algorithm.find.return_root" ); + + test->add( BOOST_TEST_CASE( &boost_range_test_algorithm_find_return_root::test_find ) ); + test->add( BOOST_TEST_CASE( &boost_range_test_algorithm_find_return_root::test_find_as_contains ) ); + + return test; +} diff --git a/test/test_driver/range_return_root_test_driver.hpp b/test/test_driver/range_return_root_test_driver.hpp new file mode 100644 index 000000000..5ee3b476c --- /dev/null +++ b/test/test_driver/range_return_root_test_driver.hpp @@ -0,0 +1,408 @@ +// Copyright Klaus Triendl 2020. Use, modification and +// distribution is subject to the Boost Software License, Version +// 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// +// For more information, see http://www.boost.org/libs/range/ +// +#ifndef BOOST_RANGE_TEST_TEST_DRIVER_RANGE_RETURN_BASE_TEST_DRIVER_HPP_INCLUDED +#define BOOST_RANGE_TEST_TEST_DRIVER_RANGE_RETURN_BASE_TEST_DRIVER_HPP_INCLUDED + +#include +#include +#include + +namespace boost +{ + namespace range_test + { + // check the results of an algorithm that returns + // a range_return featuring root iterators. + // + // This version is the general version. It should never be called. + // All calls should invoke specialized implementations. + template< range_return_value return_type > + struct check_results + { + template< class View, class RootIterator > + static void test( + View& test, + View& reference, + RootIterator test_it, + RootIterator reference_it + ) + { + BOOST_ASSERT( false ); + } + }; + + // check the results of an algorithm that returns + // a 'found' root iterator + template< > + struct check_results + { + template< class View, class RootIterator > + static void test( + View& test, + View& reference, + RootIterator test_it, + RootIterator reference_it + ) + { + BOOST_CHECK_EQUAL_COLLECTIONS( reference.begin(), reference.end(), + test.begin(), test.end() ); + + BOOST_CHECK_EQUAL( std::distance(root_of(test.begin()), test_it), + std::distance(root_of(reference.begin()), reference_it) ); + } + }; + + // check the results of an algorithm that returns + // a 'next(found)' root iterator + template< > + struct check_results + { + template< class View, class RootIterator > + static void test( + View& test, + View& reference, + RootIterator test_it, + RootIterator reference_it + ) + { + BOOST_CHECK_EQUAL_COLLECTIONS( reference.begin(), reference.end(), + test.begin(), test.end() ); + + if (reference_it == root_of(reference.end())) + { + BOOST_CHECK( test_it == root_of(test.end()) ); + } + else + { + BOOST_CHECK_EQUAL( + std::distance(root_of(test.begin()), test_it), + std::distance(root_of(reference.begin()), reference_it) + 1); + } + } + }; + + // check the results of an algorithm that returns + // a 'prior(found)' root iterator + template< > + struct check_results + { + template< class View, class RootIterator > + static void test( + View& test, + View& reference, + iterator_range test_rng, + RootIterator reference_it + ) + { + BOOST_CHECK_EQUAL_COLLECTIONS( reference.begin(), reference.end(), + test.begin(), test.end() ); + + if (reference_it == root_of(reference.begin())) + { + BOOST_CHECK( test_it == root_of(test.begin()) ); + } + else + { + BOOST_CHECK_EQUAL( + std::distance(root_of(test.begin()), test_it) + 1, + std::distance(root_of(reference.begin()), reference_it)); + } + } + }; + + // check the results of an algorithm that returns + // a '[begin, found)' root iterator range + template< > + struct check_results + { + template< class View, class RootIterator > + static void test( + View& test, + View& reference, + iterator_range test_rng, + RootIterator reference_it + ) + { + BOOST_CHECK_EQUAL_COLLECTIONS( + reference.begin(), reference.end(), + test.begin(), test.end() + ); + + BOOST_CHECK( test_rng.begin() == root_of(test.begin()) ); + + BOOST_CHECK_EQUAL_COLLECTIONS( + root_of(reference.begin()), reference_it, + boost::begin(test_rng), boost::end(test_rng) + ); + } + }; + + // check the results of an algorithm that returns + // a '[begin, next(found))' root iterator range + template< > + struct check_results + { + template< class View, class RootIterator > + static void test( + View& test, + View& reference, + iterator_range test_rng, + RootIterator reference_it + ) + { + BOOST_CHECK_EQUAL_COLLECTIONS( + reference.begin(), reference.end(), + test.begin(), test.end() + ); + + BOOST_CHECK( test_rng.begin() == root_of(test.begin()) ); + + if (reference_it == root_of(reference.end())) + { + BOOST_CHECK( test_rng.end() == root_of(test.end()) ); + } + else + { + BOOST_CHECK_EQUAL_COLLECTIONS( + root_of(reference.begin()), boost::next(reference_it), + test_rng.begin(), test_rng.end()); + } + } + }; + + // check the results of an algorithm that returns + // a '[begin, prior(found))' root iterator range + template< > + struct check_results + { + template< class View, class RootIterator > + static void test( + View& test, + View& reference, + iterator_range test_rng, + RootIterator reference_it + ) + { + BOOST_CHECK_EQUAL_COLLECTIONS( reference.begin(), reference.end(), + test.begin(), test.end() ); + + BOOST_CHECK( test_rng.begin() == root_of(test.begin()) ); + + if (reference_it == root_of(reference.begin())) + { + BOOST_CHECK( boost::end(test_rng) == root_of(test.begin()) ); + } + else + { + BOOST_CHECK_EQUAL( std::distance(boost::begin(test_rng), boost::end(test_rng)) + 1, + std::distance(root_of(reference.begin()), reference_it) ); + } + } + }; + + // check the results of an algorithm that returns + // a '[found, end)' root iterator range + template< > + struct check_results + { + template< class View, class RootIterator > + static void test( + View& test, + View& reference, + iterator_range test_rng, + RootIterator reference_it + ) + { + BOOST_CHECK_EQUAL_COLLECTIONS( reference.begin(), reference.end(), + test.begin(), test.end() ); + + BOOST_CHECK_EQUAL( + std::distance(root_of(test.begin()), boost::begin(test_rng)), + std::distance(root_of(reference.begin()), reference_it)); + + BOOST_CHECK( boost::end(test_rng) == root_of(test.end()) ); + } + }; + + // check the results of an algorithm that returns + // a '[next(found), end)' root iterator range + template< > + struct check_results + { + template< class View, class RootIterator > + static void test( + View& test, + View& reference, + iterator_range test_rng, + RootIterator reference_it + ) + { + BOOST_CHECK_EQUAL_COLLECTIONS( + reference.begin(), reference.end(), + test.begin(), test.end() + ); + + BOOST_CHECK( test_rng.end() == root_of(test.end()) ); + + if (reference_it == root_of(reference.end())) + { + BOOST_CHECK( test_rng.begin() == root_of(test.end()) ); + } + else + { + BOOST_CHECK_EQUAL_COLLECTIONS( + boost::next(reference_it), root_of(reference.end()), + test_rng.begin(), test_rng.end() + ); + } + } + }; + + // check the results of an algorithm that returns + // a 'prior(found), end)' root iterator range + template< > + struct check_results + { + template< class View, class RootIterator > + static void test( + View& test, + View& reference, + iterator_range test_rng, + RootIterator reference_it + ) + { + BOOST_CHECK_EQUAL_COLLECTIONS( + reference.begin(), reference.end(), + test.begin(), test.end() + ); + + BOOST_CHECK( test_rng.end() == root_of(test.end()) ); + + if (reference_it == root_of(reference.begin())) + { + BOOST_CHECK( test_rng.begin() == root_of(test.begin()) ); + } + else + { + BOOST_CHECK_EQUAL_COLLECTIONS( + boost::prior(reference_it), root_of(reference.end()), + test_rng.begin(), test_rng.end() + ); + } + } + }; + + // check the results of an algorithm that returns + // a '[begin, end)' root iterator range + template< > + struct check_results + { + template< class View, class RootIterator > + static void test( + View& test, + View& reference, + iterator_range test_rng, + RootIterator reference_it + ) + { + BOOST_CHECK_EQUAL_COLLECTIONS( + reference.begin(), reference.end(), + test.begin(), test.end() + ); + + BOOST_CHECK( test_rng.begin() == root_of(test.begin()) ); + BOOST_CHECK( test_rng.end() == root_of(test.end()) ); + } + }; + + // A test driver to exercise a test through all of the range_return + // combinations featuring root iterators. + // + // The test driver also contains the code required to check the + // return value correctness. + // + // The TestPolicy needs to implement two functions: + // + // - perform the boost range version of the algorithm that returns + // a range_return::type + // template + // BOOST_DEDUCED_TYPENAME range_return::type + // test(View& cont); + // + // - perform the reference std version of the algorithm that + // returns the standard iterator result + // template + // BOOST_DEDUCED_TYPENAME range_iterator::type + // reference(View& rng); + class range_return_root_test_driver + { + public: + template< class View, + class TestPolicy > + void operator()(View& rng, TestPolicy policy) + { + test_range_iter (rng, policy); + test_range ()(rng, policy); + test_range ()(rng, policy); + test_range ()(rng, policy); + test_range()(rng, policy); + test_range ()(rng, policy); + test_range()(rng, policy); + test_range ()(rng, policy); + test_range ()(rng, policy); + test_range ()(rng, policy); + test_range ()(rng, policy); + } + + private: + template< class View, class TestPolicy > + void test_range_iter( + View& rng, + TestPolicy policy + ) + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator_t; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator_t; + + View reference(rng); + View test(rng); + + root_iterator_t test_it = policy.test_iter(test); + root_iterator_t reference_it = policy.reference(reference); + + check_results::test(test, reference, + test_it, reference_it); + } + + template< range_return_value result_type, class View, class TestPolicy > + struct test_range + { + void operator()(View& rng, TestPolicy policy) + { + typedef BOOST_DEDUCED_TYPENAME range_iterator::type iterator_t; + typedef BOOST_DEDUCED_TYPENAME root_iterator_of::type root_iterator_t; + typedef BOOST_DEDUCED_TYPENAME range_return::type range_return_t; + typedef BOOST_DEDUCED_TYPENAME TestPolicy::template test_range test_range_t; + + View reference(rng); + View test(rng); + + test_range_t test_range_fn; + range_return_t test_rng = test_range_fn(policy, test); + root_iterator_t reference_it = policy.reference(reference); + + check_results::test(test, reference, + test_rng, reference_it); + } + }; + }; + } +} + +#endif // include guard