From 3225aaf82b609a75a24cf5006205e77821e3825b Mon Sep 17 00:00:00 2001 From: Robin Eckert Date: Wed, 4 Nov 2015 19:12:26 +0100 Subject: [PATCH] add a new range adaptor boost::adaptors::ref_unwrapped The new adaptor is like boost::adaptors::indirected, but for std::reference_wrapper values (instead of pointer). It calls .get() on every value and returns the result. It is useful for range-based iteration of ranges of std::reference_wrapper (or related) types. --- doc/reference/adaptors.qbk | 1 + doc/reference/adaptors/ref_unwrapped.qbk | 32 ++++++ include/boost/range/adaptor/ref_unwrapped.hpp | 102 ++++++++++++++++++ test/Jamfile.v2 | 2 + test/adaptor_test/ref_unwrapped.cpp | 101 +++++++++++++++++ test/adaptor_test/ref_unwrapped_example.cpp | 47 ++++++++ 6 files changed, 285 insertions(+) create mode 100644 doc/reference/adaptors/ref_unwrapped.qbk create mode 100644 include/boost/range/adaptor/ref_unwrapped.hpp create mode 100644 test/adaptor_test/ref_unwrapped.cpp create mode 100644 test/adaptor_test/ref_unwrapped_example.cpp diff --git a/doc/reference/adaptors.qbk b/doc/reference/adaptors.qbk index b3efaee..2cead36 100644 --- a/doc/reference/adaptors.qbk +++ b/doc/reference/adaptors.qbk @@ -172,6 +172,7 @@ rng | boost::adaptors::adaptor_generator [include adaptors/indirected.qbk] [include adaptors/map_keys.qbk] [include adaptors/map_values.qbk] +[include adaptors/ref_unwrapped.qbk] [include adaptors/replaced.qbk] [include adaptors/replaced_if.qbk] [include adaptors/reversed.qbk] diff --git a/doc/reference/adaptors/ref_unwrapped.qbk b/doc/reference/adaptors/ref_unwrapped.qbk new file mode 100644 index 0000000..156ad67 --- /dev/null +++ b/doc/reference/adaptors/ref_unwrapped.qbk @@ -0,0 +1,32 @@ +[/ + Copyright 2015 Robin Eckert + Distributed under 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) +/] +[section:ref_unwrapped ref_unwrapped] + +[table + [[Syntax] [Code]] + [[Pipe] [`rng | boost::adaptors::ref_unwrapped`]] + [[Function] [`boost::adaptors::ref_unwrap(rng)`]] +] + +This adaptor produces a range than applies `.get()` on all values in +the range. It is useful for iterating ranges of +`std::reference_wrapper` values or values using similar semantics. + +The adaptor is C++11 (and above) only. + +* [*Precondition:] The `value_type` of the range has a `.get() const`. +* [*Postcondition:] For all elements `x` in the returned range, `x` is the result of `y.get()` where `y` is the corresponding element in the original range. +* [*Range Category:] __single_pass_range__ +* [*Range Return Type:] `boost::unwrap_ref_range` +* [*Returned Range Category:] The range category of `rng`. + +[section:ref_unwrapped_example ref_unwrapped example] +[import ../../../test/adaptor_test/ref_unwrapped_example.cpp] +[ref_unwrapped_example] +[endsect] + +This would produce the output `123`. +[endsect] diff --git a/include/boost/range/adaptor/ref_unwrapped.hpp b/include/boost/range/adaptor/ref_unwrapped.hpp new file mode 100644 index 0000000..71af483 --- /dev/null +++ b/include/boost/range/adaptor/ref_unwrapped.hpp @@ -0,0 +1,102 @@ +// Boost.Range library +// +// Copyright Robin Eckert 2015. +// Copyright Thorsten Ottosen, Neil Groves 2006 - 2008. 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_ADAPTOR_REF_UNWRAPPED_HPP +#define BOOST_RANGE_ADAPTOR_REF_UNWRAPPED_HPP + +#include +#include +#include + +#include + +#if !defined(BOOST_NO_CXX11_DECLTYPE) + +namespace boost +{ + namespace range_detail + { + struct ref_unwrapped_forwarder {}; + + template + struct unwrap_ref + { + typedef BOOST_DEDUCED_TYPENAME + range_reference::type argument_type; + + using result_type = decltype(std::declval().get() ); + + result_type operator()( argument_type &&r ) const + { + return r.get(); + } + }; + + + template + class unwrap_ref_range + : public transformed_range, + SinglePassRange> + { + using base = transformed_range, + SinglePassRange>; + public: + using transform_fn_type = unwrap_ref; + using source_range_type = SinglePassRange; + + unwrap_ref_range(transform_fn_type fn, source_range_type &rng) + : base(fn, rng) + { + } + + unwrap_ref_range(const base &other) : base(other) {} + }; + + template + inline unwrap_ref_range + operator|(SinglePassRange& r, ref_unwrapped_forwarder) + { + BOOST_RANGE_CONCEPT_ASSERT(( + SinglePassRangeConcept)); + + return operator|( r, + boost::adaptors::transformed(unwrap_ref())); + } + + } + + using range_detail::unwrap_ref_range; + + namespace adaptors + { + namespace + { + const range_detail::ref_unwrapped_forwarder ref_unwrapped = + range_detail::ref_unwrapped_forwarder(); + } + + template + inline unwrap_ref_range + ref_unwrap(SinglePassRange& rng) + { + BOOST_RANGE_CONCEPT_ASSERT(( + SinglePassRangeConcept)); + + return unwrap_ref_range( + range_detail::unwrap_ref(), rng ); + } + } // 'adaptors' + +} + +#endif + +#endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 05937c7..f038026 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -59,6 +59,8 @@ test-suite range : [ range-test adaptor_test/indexed ] [ range-test adaptor_test/indirected ] [ range-test adaptor_test/map ] + [ range-test adaptor_test/ref_unwrapped ] + [ range-test adaptor_test/ref_unwrapped_example ] [ range-test adaptor_test/replaced ] [ range-test adaptor_test/replaced_if ] [ range-test adaptor_test/reversed ] diff --git a/test/adaptor_test/ref_unwrapped.cpp b/test/adaptor_test/ref_unwrapped.cpp new file mode 100644 index 0000000..ea190b3 --- /dev/null +++ b/test/adaptor_test/ref_unwrapped.cpp @@ -0,0 +1,101 @@ +// Boost.Range library +// +// Copyright Robin Eckert 2015. 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 + +#define BOOST_TEST_MAIN + +#include +#include + +#include + +#if !defined(BOOST_NO_CXX11_AUTO_DECLARATIONS) && !defined(BOOST_NO_CXX11_RANGE_BASED_FOR) + +namespace boost +{ + + BOOST_AUTO_TEST_CASE(test_mutable) + { + int one = 1; + int two = 2; + int three = 3; + + std::vector> input_values{one, two, three}; + + const std::vector expected{&one, &two, &three}; + std::vector actual; + + for (auto&& value : input_values | adaptors::ref_unwrapped) + { + actual.push_back(&value); + } + + BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), + expected.end(), + actual.begin(), + actual.end()); + } + + BOOST_AUTO_TEST_CASE(test_const_range) + { + int one = 1; + int two = 2; + int three = 3; + + const std::vector> input_values{one, two, three}; + + const std::vector expected{&one, &two, &three}; + std::vector actual; + + for (auto&& value : input_values | adaptors::ref_unwrapped) + { + actual.push_back(&value); + } + + BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), + expected.end(), + actual.begin(), + actual.end()); + } + + BOOST_AUTO_TEST_CASE(test_const_reference) + { + const int one = 1; + const int two = 2; + const int three = 3; + + const std::vector> input_values{one, two, three}; + + const std::vector expected{&one, &two, &three}; + std::vector actual; + + for (auto&& value : input_values | adaptors::ref_unwrapped) + { + actual.push_back(&value); + } + + BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), + expected.end(), + actual.begin(), + actual.end()); + } + + +} + +#else + +BOOST_AUTO_TEST_CASE(empty) +{ + // C++11 only +} + +#endif diff --git a/test/adaptor_test/ref_unwrapped_example.cpp b/test/adaptor_test/ref_unwrapped_example.cpp new file mode 100644 index 0000000..ab3e401 --- /dev/null +++ b/test/adaptor_test/ref_unwrapped_example.cpp @@ -0,0 +1,47 @@ +// Boost.Range library +// +// Copyright Robin Eckert 2015. 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/ +// +//[ref_unwrapped_example +#include +#include +#include + +struct example +{ + int value; +}; + +int main(int argc, const char* argv[]) +{ +//<- +#if !defined(BOOST_NO_CXX11_DECLTYPE) \ + && !defined(BOOST_NO_CXX11_RANGE_BASED_FOR) \ + && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX) \ + && !defined(BOOST_NO_CXX11_AUTO_DECLARATIONS) +//-> + using boost::adaptors::ref_unwrapped; + + example one{1}; + example two{2}; + example three{3}; + + std::vector > input{one, two, three}; + + for (auto&& entry : input | ref_unwrapped) + { + std::cout << entry.value; + } + + return 0; +//<- +#endif +//-> +} +//]