// Boost.Range library // // Copyright Neil Groves 2010. 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/ // // Credits: // Trac 7376 - was raised by Leonid Gershanovich and his sample was used to // make the test case to cover this condition. // #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace { // This function is a helper function that writes integers // of increasing value into a range. It is used to test // that joined ranged may be written to. // // Requires: // - Range uses shallow copy semantics. template< typename Range > void fill_with_ints(Range rng) { typedef typename range_iterator::type iterator; iterator target = boost::begin(rng); const int count = boost::distance(rng); for (int i = 0; i < count; ++i) { *target = i; ++target; } } // The test_join_traversal function is used to provide additional // tests based upon the underlying join iterator traversal. // The join iterator takes care of the appropriate demotion, and // this demotion. // test_join_traversal - additional tests for input and forward // traversal iterators. This is of course a no-op. template< typename Range1, typename Range2, typename TraversalTag > void test_join_traversal(Range1& rng1, Range2& rng2, TraversalTag) { } // test_join_traversal - additional tests for bidirectional // traversal iterators. template< typename Range1, typename Range2 > void test_join_traversal(Range1& rng1, Range2& rng2, boost::bidirectional_traversal_tag) { typedef typename range_value::type value_type; std::vector reference(boost::begin(rng1), boost::end(rng1)); boost::push_back(reference, rng2); std::reverse(reference.begin(), reference.end()); std::vector test_result; BOOST_REVERSE_FOREACH( value_type x, join(rng1, rng2) ) { test_result.push_back(x); } BOOST_CHECK_EQUAL_COLLECTIONS( reference.begin(), reference.end(), test_result.begin(), test_result.end() ); } // Test helper function to implement the additional tests for random // access traversal iterators. This is used by the test_join_traversal // function for random access iterators. The reason that the test // implementation is put into this function is to utilise // template parameter type deduction for the joined range type. template< typename Range1, typename Range2, typename JoinedRange > void test_random_access_join(Range1& rng1, Range2& rng2, JoinedRange joined) { BOOST_CHECK_EQUAL( boost::end(joined) - boost::begin(joined), boost::distance(joined) ); BOOST_CHECK( boost::end(joined) <= boost::begin(joined) ); BOOST_CHECK( boost::begin(joined) >= boost::end(joined) ); if (boost::empty(joined)) { BOOST_CHECK(!(boost::begin(joined) < boost::end(joined))); BOOST_CHECK(!(boost::end(joined) > boost::begin(joined))); } else { BOOST_CHECK(boost::begin(joined) < boost::end(joined)); BOOST_CHECK(boost::end(joined) < boost::begin(joined)); } typedef typename boost::range_difference::type difference_t; const difference_t count = boost::distance(joined); BOOST_CHECK( boost::begin(joined) + count == boost::end(joined) ); BOOST_CHECK( boost::end(joined) - count == boost::begin(joined) ); typedef typename boost::range_iterator::type iterator_t; iterator_t it = boost::begin(joined); it += count; BOOST_CHECK( it == boost::end(joined) ); it = boost::end(joined); it -= count; BOOST_CHECK( it == boost::begin(joined) ); } // test_join_traversal function for random access traversal joined // ranges. template< typename Range1, typename Range2 > void test_join_traversal(Range1& rng1, Range2& rng2, boost::random_access_traversal_tag) { test_join_traversal(rng1, rng2, boost::bidirectional_traversal_tag()); test_random_access_join(rng1, rng2, join(rng1, rng2)); } // Test the ability to write values into a joined range. This is // achieved by copying the constant collections, altering them // and then checking the result. Hence this relies upon both // rng1 and rng2 having value copy semantics. template< typename Collection1, typename Collection2 > void test_write_to_joined_range(const Collection1& rng1, const Collection2& rng2) { Collection1 c1(rng1); Collection2 c2(rng2); typedef BOOST_DEDUCED_TYPENAME boost::range_value< Collection1 >::type value_t BOOST_RANGE_UNUSED; fill_with_ints(boost::join(c1,c2)); // Ensure that the size of the written range has not been // altered. BOOST_CHECK_EQUAL( boost::distance(c1), boost::distance(rng1) ); BOOST_CHECK_EQUAL( boost::distance(c2), boost::distance(rng2) ); // For each element x, in c1 ensure that it has been written to // with incrementing integers int x = 0; typedef typename range_iterator::type iterator1; iterator1 it1 = boost::begin(c1); for (; it1 != boost::end(c1); ++it1) { BOOST_CHECK_EQUAL( x, *it1 ); ++x; } // For each element y, in c2 ensure that it has been written to // with incrementing integers typedef typename range_iterator::type iterator2; iterator2 it2 = boost::begin(c2); for (; it2 != boost::end(c2); ++it2) { BOOST_CHECK_EQUAL( x, *it2 ); ++x; } } // Perform a unit test of a Boost.Range join() comparing // it to a reference that is populated by appending // elements from both source ranges into a vector. template< typename Collection1, typename Collection2 > void test_join_impl(Collection1& rng1, Collection2& rng2) { typedef typename range_value::type value_type; std::vector reference(boost::begin(rng1), boost::end(rng1)); boost::push_back(reference, rng2); std::vector test_result; boost::push_back(test_result, join(rng1, rng2)); BOOST_CHECK_EQUAL_COLLECTIONS( reference.begin(), reference.end(), test_result.begin(), test_result.end() ); typedef boost::range_detail::join_iterator< typename boost::range_iterator::type, typename boost::range_iterator::type > join_iterator_t; typedef boost::iterator_traversal< join_iterator_t > tag_t; test_join_traversal(rng1, rng2, tag_t()); test_write_to_joined_range(rng1, rng2); } // Make a collection filling it with items from the source // range. This is used to build collections of various // sizes populated with various values designed to optimize // the code coverage exercised by the core test function // test_join_impl. template boost::shared_ptr makeCollection(const Range& source) { boost::shared_ptr c(new Collection); c->insert(c->end(), boost::begin(source), boost::end(source)); return c; } // This templatised version of the test_join_impl function // generates and populates collections which are later // used as input to the core test function. // The caller of this function explicitly provides the // template parameters. This supports the generation // of testing a large combination of range types to be // joined. It is of particular importance to remember // to combine a random_access range with a bidirectional // range to determine that the correct demotion of // types occurs in the join_iterator. template< typename Collection1, typename Collection2 > void test_join_impl() { typedef boost::shared_ptr collection1_ptr; typedef boost::shared_ptr collection2_ptr; typedef boost::shared_ptr collection1_cptr; typedef boost::shared_ptr collection2_cptr; std::vector< collection1_cptr > left_containers; std::vector< collection2_cptr > right_containers; left_containers.push_back(collection1_ptr(new Collection1)); left_containers.push_back(makeCollection(irange(0,1))); left_containers.push_back(makeCollection(irange(0,100))); right_containers.push_back(collection2_ptr(new Collection2)); right_containers.push_back(makeCollection(irange(0,1))); right_containers.push_back(makeCollection(irange(0,100))); BOOST_FOREACH( collection1_cptr left_container, left_containers ) { BOOST_FOREACH( collection2_cptr right_container, right_containers ) { test_join_impl(*left_container, *right_container); } } } // entry-point into the unit test for the join() function // this tests a representative sample of combinations of // source range type. void join_test() { test_join_impl< std::vector, std::vector >(); test_join_impl< std::list, std::list >(); test_join_impl< std::deque, std::deque >(); test_join_impl< std::vector, std::list >(); test_join_impl< std::list, std::vector >(); test_join_impl< std::vector, std::deque >(); test_join_impl< std::deque, std::vector >(); } void test_join_iterator_reference_type_constness_ticket8483() { // Just test that this compiles. // Before the fix for bug 8483, the reference type of the joined // range's iterator was incorrect ('int&' instead of 'const int&'), // causing compiler errors. const std::vector v1; std::vector v2; std::vector joined; boost::push_back(joined, join(v1, v2)); boost::push_back(joined, join(v2, v1)); } namespace trac7376 { struct base_type { explicit base_type(boost::int32_t value) : value(value) { } virtual boost::int32_t get() const = 0; boost::int32_t value; }; struct derived_type1 : base_type { derived_type1(boost::int32_t value) : base_type(value) { } virtual boost::int32_t get() const { return value * 2; } }; struct derived_type2 : base_type { derived_type2(boost::int32_t value) : base_type(value) { } virtual boost::int32_t get() const { return value * 4; } }; struct apply_get { typedef boost::int32_t result_type; result_type operator()(const base_type& arg) const { return arg.get(); } }; void test_reference_types() { using namespace boost::adaptors; typedef boost::range_detail::join_iterator< std::vector::iterator, std::vector::iterator, const base_type&, const base_type& > join_iterator_t; std::vector reference_output; std::vector x; for (boost::int32_t i = 0; i < 10; ++i) { x.push_back(derived_type1(i)); reference_output.push_back(i * 2); } std::vector y; for (boost::int32_t i = 0; i < 10; ++i) { y.push_back(derived_type2(i)); reference_output.push_back(i * 4); } join_iterator_t it( x, y, boost::range_detail::join_iterator_begin_tag()); std::vector output; boost::push_back( output, boost::make_iterator_range( join_iterator_t( x, y, boost::range_detail::join_iterator_begin_tag()), join_iterator_t( x, y, boost::range_detail::join_iterator_end_tag())) | transformed(apply_get())); BOOST_CHECK_EQUAL_COLLECTIONS( output.begin(), output.end(), reference_output.begin(), reference_output.end()); } } // namespace trac7376 } } boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { boost::unit_test::test_suite* test = BOOST_TEST_SUITE( "RangeTestSuite.adaptor.joined" ); test->add( BOOST_TEST_CASE( &boost::join_test ) ); test->add( BOOST_TEST_CASE( &boost::test_join_iterator_reference_type_constness_ticket8483 ) ); test->add( BOOST_TEST_CASE( &boost::trac7376::test_reference_types ) ); return test; }