join.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. // Boost.Range library
  2. //
  3. // Copyright Neil Groves 2010. Use, modification and
  4. // distribution is subject to the Boost Software License, Version
  5. // 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. //
  9. // For more information, see http://www.boost.org/libs/range/
  10. //
  11. // Credits:
  12. // Trac 7376 - was raised by Leonid Gershanovich and his sample was used to
  13. // make the test case to cover this condition.
  14. //
  15. #include <boost/range/join.hpp>
  16. #include <boost/range/adaptor/transformed.hpp>
  17. #include <boost/foreach.hpp>
  18. #include <boost/test/test_tools.hpp>
  19. #include <boost/test/unit_test.hpp>
  20. #include <boost/assign.hpp>
  21. #include <boost/range/algorithm_ext.hpp>
  22. #include <boost/range/irange.hpp>
  23. #include <boost/iterator/iterator_facade.hpp>
  24. #include <algorithm>
  25. #include <deque>
  26. #include <list>
  27. #include <vector>
  28. namespace boost
  29. {
  30. namespace
  31. {
  32. // This function is a helper function that writes integers
  33. // of increasing value into a range. It is used to test
  34. // that joined ranged may be written to.
  35. //
  36. // Requires:
  37. // - Range uses shallow copy semantics.
  38. template< typename Range >
  39. void fill_with_ints(Range rng)
  40. {
  41. typedef typename range_iterator<Range>::type iterator;
  42. iterator target = boost::begin(rng);
  43. const int count = boost::distance(rng);
  44. for (int i = 0; i < count; ++i)
  45. {
  46. *target = i;
  47. ++target;
  48. }
  49. }
  50. // The test_join_traversal function is used to provide additional
  51. // tests based upon the underlying join iterator traversal.
  52. // The join iterator takes care of the appropriate demotion, and
  53. // this demotion.
  54. // test_join_traversal - additional tests for input and forward
  55. // traversal iterators. This is of course a no-op.
  56. template< typename Range1, typename Range2, typename TraversalTag >
  57. void test_join_traversal(Range1& rng1, Range2& rng2, TraversalTag)
  58. {
  59. }
  60. // test_join_traversal - additional tests for bidirectional
  61. // traversal iterators.
  62. template< typename Range1, typename Range2 >
  63. void test_join_traversal(Range1& rng1, Range2& rng2, boost::bidirectional_traversal_tag)
  64. {
  65. typedef typename range_value<Range1>::type value_type;
  66. std::vector<value_type> reference(boost::begin(rng1), boost::end(rng1));
  67. boost::push_back(reference, rng2);
  68. std::reverse(reference.begin(), reference.end());
  69. std::vector<value_type> test_result;
  70. BOOST_REVERSE_FOREACH( value_type x, join(rng1, rng2) )
  71. {
  72. test_result.push_back(x);
  73. }
  74. BOOST_CHECK_EQUAL_COLLECTIONS( reference.begin(), reference.end(),
  75. test_result.begin(), test_result.end() );
  76. }
  77. // Test helper function to implement the additional tests for random
  78. // access traversal iterators. This is used by the test_join_traversal
  79. // function for random access iterators. The reason that the test
  80. // implementation is put into this function is to utilise
  81. // template parameter type deduction for the joined range type.
  82. template< typename Range1, typename Range2, typename JoinedRange >
  83. void test_random_access_join(Range1& rng1, Range2& rng2, JoinedRange joined)
  84. {
  85. BOOST_CHECK_EQUAL( boost::end(joined) - boost::begin(joined), boost::distance(joined) );
  86. BOOST_CHECK( boost::end(joined) <= boost::begin(joined) );
  87. BOOST_CHECK( boost::begin(joined) >= boost::end(joined) );
  88. if (boost::empty(joined))
  89. {
  90. BOOST_CHECK(!(boost::begin(joined) < boost::end(joined)));
  91. BOOST_CHECK(!(boost::end(joined) > boost::begin(joined)));
  92. }
  93. else
  94. {
  95. BOOST_CHECK(boost::begin(joined) < boost::end(joined));
  96. BOOST_CHECK(boost::end(joined) < boost::begin(joined));
  97. }
  98. typedef typename boost::range_difference<JoinedRange>::type difference_t;
  99. const difference_t count = boost::distance(joined);
  100. BOOST_CHECK( boost::begin(joined) + count == boost::end(joined) );
  101. BOOST_CHECK( boost::end(joined) - count == boost::begin(joined) );
  102. typedef typename boost::range_iterator<JoinedRange>::type iterator_t;
  103. iterator_t it = boost::begin(joined);
  104. it += count;
  105. BOOST_CHECK( it == boost::end(joined) );
  106. it = boost::end(joined);
  107. it -= count;
  108. BOOST_CHECK( it == boost::begin(joined) );
  109. }
  110. // test_join_traversal function for random access traversal joined
  111. // ranges.
  112. template< typename Range1, typename Range2 >
  113. void test_join_traversal(Range1& rng1, Range2& rng2, boost::random_access_traversal_tag)
  114. {
  115. test_join_traversal(rng1, rng2, boost::bidirectional_traversal_tag());
  116. test_random_access_join(rng1, rng2, join(rng1, rng2));
  117. }
  118. // Test the ability to write values into a joined range. This is
  119. // achieved by copying the constant collections, altering them
  120. // and then checking the result. Hence this relies upon both
  121. // rng1 and rng2 having value copy semantics.
  122. template< typename Collection1, typename Collection2 >
  123. void test_write_to_joined_range(const Collection1& rng1, const Collection2& rng2)
  124. {
  125. Collection1 c1(rng1);
  126. Collection2 c2(rng2);
  127. typedef BOOST_DEDUCED_TYPENAME boost::range_value<
  128. Collection1
  129. >::type value_t BOOST_RANGE_UNUSED;
  130. fill_with_ints(boost::join(c1,c2));
  131. // Ensure that the size of the written range has not been
  132. // altered.
  133. BOOST_CHECK_EQUAL( boost::distance(c1), boost::distance(rng1) );
  134. BOOST_CHECK_EQUAL( boost::distance(c2), boost::distance(rng2) );
  135. // For each element x, in c1 ensure that it has been written to
  136. // with incrementing integers
  137. int x = 0;
  138. typedef typename range_iterator<Collection1>::type iterator1;
  139. iterator1 it1 = boost::begin(c1);
  140. for (; it1 != boost::end(c1); ++it1)
  141. {
  142. BOOST_CHECK_EQUAL( x, *it1 );
  143. ++x;
  144. }
  145. // For each element y, in c2 ensure that it has been written to
  146. // with incrementing integers
  147. typedef typename range_iterator<Collection2>::type iterator2;
  148. iterator2 it2 = boost::begin(c2);
  149. for (; it2 != boost::end(c2); ++it2)
  150. {
  151. BOOST_CHECK_EQUAL( x, *it2 );
  152. ++x;
  153. }
  154. }
  155. // Perform a unit test of a Boost.Range join() comparing
  156. // it to a reference that is populated by appending
  157. // elements from both source ranges into a vector.
  158. template< typename Collection1, typename Collection2 >
  159. void test_join_impl(Collection1& rng1, Collection2& rng2)
  160. {
  161. typedef typename range_value<Collection1>::type value_type;
  162. std::vector<value_type> reference(boost::begin(rng1), boost::end(rng1));
  163. boost::push_back(reference, rng2);
  164. std::vector<value_type> test_result;
  165. boost::push_back(test_result, join(rng1, rng2));
  166. BOOST_CHECK_EQUAL_COLLECTIONS( reference.begin(), reference.end(),
  167. test_result.begin(), test_result.end() );
  168. typedef boost::range_detail::join_iterator<
  169. typename boost::range_iterator<Collection1>::type,
  170. typename boost::range_iterator<Collection2>::type
  171. > join_iterator_t;
  172. typedef boost::iterator_traversal< join_iterator_t > tag_t;
  173. test_join_traversal(rng1, rng2, tag_t());
  174. test_write_to_joined_range(rng1, rng2);
  175. }
  176. // Make a collection filling it with items from the source
  177. // range. This is used to build collections of various
  178. // sizes populated with various values designed to optimize
  179. // the code coverage exercised by the core test function
  180. // test_join_impl.
  181. template<typename Collection, typename Range>
  182. boost::shared_ptr<Collection> makeCollection(const Range& source)
  183. {
  184. boost::shared_ptr<Collection> c(new Collection);
  185. c->insert(c->end(), boost::begin(source), boost::end(source));
  186. return c;
  187. }
  188. // This templatised version of the test_join_impl function
  189. // generates and populates collections which are later
  190. // used as input to the core test function.
  191. // The caller of this function explicitly provides the
  192. // template parameters. This supports the generation
  193. // of testing a large combination of range types to be
  194. // joined. It is of particular importance to remember
  195. // to combine a random_access range with a bidirectional
  196. // range to determine that the correct demotion of
  197. // types occurs in the join_iterator.
  198. template< typename Collection1, typename Collection2 >
  199. void test_join_impl()
  200. {
  201. typedef boost::shared_ptr<Collection1> collection1_ptr;
  202. typedef boost::shared_ptr<Collection2> collection2_ptr;
  203. typedef boost::shared_ptr<const Collection1> collection1_cptr;
  204. typedef boost::shared_ptr<const Collection2> collection2_cptr;
  205. std::vector< collection1_cptr > left_containers;
  206. std::vector< collection2_cptr > right_containers;
  207. left_containers.push_back(collection1_ptr(new Collection1));
  208. left_containers.push_back(makeCollection<Collection1>(irange(0,1)));
  209. left_containers.push_back(makeCollection<Collection1>(irange(0,100)));
  210. right_containers.push_back(collection2_ptr(new Collection2));
  211. right_containers.push_back(makeCollection<Collection2>(irange(0,1)));
  212. right_containers.push_back(makeCollection<Collection2>(irange(0,100)));
  213. BOOST_FOREACH( collection1_cptr left_container, left_containers )
  214. {
  215. BOOST_FOREACH( collection2_cptr right_container, right_containers )
  216. {
  217. test_join_impl(*left_container, *right_container);
  218. }
  219. }
  220. }
  221. // entry-point into the unit test for the join() function
  222. // this tests a representative sample of combinations of
  223. // source range type.
  224. void join_test()
  225. {
  226. test_join_impl< std::vector<int>, std::vector<int> >();
  227. test_join_impl< std::list<int>, std::list<int> >();
  228. test_join_impl< std::deque<int>, std::deque<int> >();
  229. test_join_impl< std::vector<int>, std::list<int> >();
  230. test_join_impl< std::list<int>, std::vector<int> >();
  231. test_join_impl< std::vector<int>, std::deque<int> >();
  232. test_join_impl< std::deque<int>, std::vector<int> >();
  233. }
  234. void test_join_iterator_reference_type_constness_ticket8483()
  235. {
  236. // Just test that this compiles.
  237. // Before the fix for bug 8483, the reference type of the joined
  238. // range's iterator was incorrect ('int&' instead of 'const int&'),
  239. // causing compiler errors.
  240. const std::vector<int> v1;
  241. std::vector<int> v2;
  242. std::vector<int> joined;
  243. boost::push_back(joined, join(v1, v2));
  244. boost::push_back(joined, join(v2, v1));
  245. }
  246. namespace trac7376
  247. {
  248. struct base_type
  249. {
  250. explicit base_type(boost::int32_t value)
  251. : value(value)
  252. {
  253. }
  254. virtual boost::int32_t get() const = 0;
  255. boost::int32_t value;
  256. };
  257. struct derived_type1
  258. : base_type
  259. {
  260. derived_type1(boost::int32_t value)
  261. : base_type(value)
  262. {
  263. }
  264. virtual boost::int32_t get() const
  265. {
  266. return value * 2;
  267. }
  268. };
  269. struct derived_type2
  270. : base_type
  271. {
  272. derived_type2(boost::int32_t value)
  273. : base_type(value)
  274. {
  275. }
  276. virtual boost::int32_t get() const
  277. {
  278. return value * 4;
  279. }
  280. };
  281. struct apply_get
  282. {
  283. typedef boost::int32_t result_type;
  284. result_type operator()(const base_type& arg) const
  285. {
  286. return arg.get();
  287. }
  288. };
  289. void test_reference_types()
  290. {
  291. using namespace boost::adaptors;
  292. typedef boost::range_detail::join_iterator<
  293. std::vector<derived_type1>::iterator,
  294. std::vector<derived_type2>::iterator,
  295. const base_type&,
  296. const base_type&
  297. > join_iterator_t;
  298. std::vector<boost::int32_t> reference_output;
  299. std::vector<derived_type1> x;
  300. for (boost::int32_t i = 0; i < 10; ++i)
  301. {
  302. x.push_back(derived_type1(i));
  303. reference_output.push_back(i * 2);
  304. }
  305. std::vector<derived_type2> y;
  306. for (boost::int32_t i = 0; i < 10; ++i)
  307. {
  308. y.push_back(derived_type2(i));
  309. reference_output.push_back(i * 4);
  310. }
  311. join_iterator_t it(
  312. x,
  313. y,
  314. boost::range_detail::join_iterator_begin_tag());
  315. std::vector<boost::int32_t> output;
  316. boost::push_back(
  317. output,
  318. boost::make_iterator_range(
  319. join_iterator_t(
  320. x, y,
  321. boost::range_detail::join_iterator_begin_tag()),
  322. join_iterator_t(
  323. x, y,
  324. boost::range_detail::join_iterator_end_tag()))
  325. | transformed(apply_get()));
  326. BOOST_CHECK_EQUAL_COLLECTIONS(
  327. output.begin(), output.end(),
  328. reference_output.begin(), reference_output.end());
  329. }
  330. } // namespace trac7376
  331. }
  332. }
  333. boost::unit_test::test_suite*
  334. init_unit_test_suite(int argc, char* argv[])
  335. {
  336. boost::unit_test::test_suite* test
  337. = BOOST_TEST_SUITE( "RangeTestSuite.adaptor.joined" );
  338. test->add( BOOST_TEST_CASE( &boost::join_test ) );
  339. test->add( BOOST_TEST_CASE( &boost::test_join_iterator_reference_type_constness_ticket8483 ) );
  340. test->add( BOOST_TEST_CASE( &boost::trac7376::test_reference_types ) );
  341. return test;
  342. }