Fixed range adaptors that were creating underlying iterators that were not default constructible.

This commit is contained in:
Neil Groves
2014-06-16 14:49:25 +01:00
parent 31ace63a0c
commit 3ecf600434
7 changed files with 272 additions and 32 deletions

View File

@@ -12,6 +12,7 @@
#define BOOST_RANGE_ADAPTOR_FILTERED_HPP #define BOOST_RANGE_ADAPTOR_FILTERED_HPP
#include <boost/range/adaptor/argument_fwd.hpp> #include <boost/range/adaptor/argument_fwd.hpp>
#include <boost/range/detail/default_constructible_unary_fn.hpp>
#include <boost/range/iterator_range.hpp> #include <boost/range/iterator_range.hpp>
#include <boost/range/concepts.hpp> #include <boost/range/concepts.hpp>
#include <boost/iterator/filter_iterator.hpp> #include <boost/iterator/filter_iterator.hpp>
@@ -23,21 +24,28 @@ namespace boost
template< class P, class R > template< class P, class R >
struct filtered_range : struct filtered_range :
boost::iterator_range< boost::iterator_range<
boost::filter_iterator< P, boost::filter_iterator<
BOOST_DEDUCED_TYPENAME range_iterator<R>::type typename default_constructible_unary_fn_gen<P, bool>::type,
typename range_iterator<R>::type
> >
> >
{ {
private: private:
typedef boost::iterator_range< typedef boost::iterator_range<
boost::filter_iterator< P, boost::filter_iterator<
BOOST_DEDUCED_TYPENAME range_iterator<R>::type typename default_constructible_unary_fn_gen<P, bool>::type,
> typename range_iterator<R>::type
> base; >
> base;
public: public:
filtered_range( P p, R& r ) typedef typename default_constructible_unary_fn_gen<P, bool>::type
: base( make_filter_iterator( p, boost::begin(r), boost::end(r) ), pred_t;
make_filter_iterator( p, boost::end(r), boost::end(r) ) )
filtered_range(P p, R& r)
: base(make_filter_iterator(pred_t(p),
boost::begin(r), boost::end(r)),
make_filter_iterator(pred_t(p),
boost::end(r), boost::end(r)))
{ } { }
}; };

View File

@@ -20,6 +20,7 @@
#include <boost/range/concepts.hpp> #include <boost/range/concepts.hpp>
#include <boost/iterator/iterator_adaptor.hpp> #include <boost/iterator/iterator_adaptor.hpp>
#include <boost/iterator/transform_iterator.hpp> #include <boost/iterator/transform_iterator.hpp>
#include <boost/optional/optional.hpp>
namespace boost namespace boost
{ {
@@ -32,19 +33,36 @@ namespace boost
typedef const Value& result_type; typedef const Value& result_type;
typedef const Value& first_argument_type; typedef const Value& first_argument_type;
// Rationale:
// The default constructor is required to allow the transform
// iterator to properly model the iterator concept.
replace_value()
{
}
replace_value(const Value& from, const Value& to) replace_value(const Value& from, const Value& to)
: m_from(from), m_to(to) : m_impl(data(from, to))
{ {
} }
const Value& operator()(const Value& x) const const Value& operator()(const Value& x) const
{ {
return (x == m_from) ? m_to : x; return (x == m_impl->m_from) ? m_impl->m_to : x;
} }
private: private:
Value m_from; struct data
Value m_to; {
data(const Value& from, const Value& to)
: m_from(from)
, m_to(to)
{
}
Value m_from;
Value m_to;
};
boost::optional<data> m_impl;
}; };
template< class R > template< class R >

View File

@@ -20,6 +20,7 @@
#include <boost/range/concepts.hpp> #include <boost/range/concepts.hpp>
#include <boost/iterator/iterator_adaptor.hpp> #include <boost/iterator/iterator_adaptor.hpp>
#include <boost/iterator/transform_iterator.hpp> #include <boost/iterator/transform_iterator.hpp>
#include <boost/optional/optional.hpp>
namespace boost namespace boost
{ {
@@ -32,19 +33,34 @@ namespace boost
typedef const Value& result_type; typedef const Value& result_type;
typedef const Value& first_argument_type; typedef const Value& first_argument_type;
// Rationale:
// required to allow the iterator to be default constructible.
replace_value_if()
{
}
replace_value_if(const Pred& pred, const Value& to) replace_value_if(const Pred& pred, const Value& to)
: m_pred(pred), m_to(to) : m_impl(data(pred, to))
{ {
} }
const Value& operator()(const Value& x) const const Value& operator()(const Value& x) const
{ {
return m_pred(x) ? m_to : x; return m_impl->m_pred(x) ? m_impl->m_to : x;
} }
private: private:
Pred m_pred; struct data
Value m_to; {
data(const Pred& p, const Value& t)
: m_pred(p), m_to(t)
{
}
Pred m_pred;
Value m_to;
};
boost::optional<data> m_impl;
}; };
template< class Pred, class R > template< class Pred, class R >

View File

@@ -12,6 +12,7 @@
#define BOOST_RANGE_ADAPTOR_TRANSFORMED_HPP #define BOOST_RANGE_ADAPTOR_TRANSFORMED_HPP
#include <boost/range/adaptor/argument_fwd.hpp> #include <boost/range/adaptor/argument_fwd.hpp>
#include <boost/range/detail/default_constructible_unary_fn.hpp>
#include <boost/range/iterator_range.hpp> #include <boost/range/iterator_range.hpp>
#include <boost/range/concepts.hpp> #include <boost/range/concepts.hpp>
#include <boost/iterator/transform_iterator.hpp> #include <boost/iterator/transform_iterator.hpp>
@@ -21,31 +22,46 @@ namespace boost
{ {
namespace range_detail namespace range_detail
{ {
// A type generator to produce the transform_iterator type conditionally
// including a wrapped predicate as appropriate.
template<typename P, typename It>
struct transform_iterator_gen
{
typedef transform_iterator<
typename default_constructible_unary_fn_gen<
P,
typename transform_iterator<P, It>::reference
>::type,
It
> type;
};
template< class F, class R > template< class F, class R >
struct transformed_range : struct transformed_range :
public boost::iterator_range< public boost::iterator_range<
boost::transform_iterator< F, typename transform_iterator_gen<
BOOST_DEDUCED_TYPENAME range_iterator<R>::type F, typename range_iterator<R>::type>::type>
>
>
{ {
private: private:
typedef boost::iterator_range< typedef typename transform_iterator_gen<
boost::transform_iterator< F, F, typename range_iterator<R>::type>::type transform_iter_t;
BOOST_DEDUCED_TYPENAME range_iterator<R>::type
> typedef boost::iterator_range<transform_iter_t> base;
>
base;
public: public:
typedef F transform_fn_type; typedef typename default_constructible_unary_fn_gen<
F,
typename transform_iterator<
F,
typename range_iterator<R>::type
>::reference
>::type transform_fn_type;
typedef R source_range_type; typedef R source_range_type;
transformed_range( F f, R& r ) transformed_range(transform_fn_type f, R& r)
: base( boost::make_transform_iterator( boost::begin(r), f ), : base(transform_iter_t(boost::begin(r), f),
boost::make_transform_iterator( boost::end(r), f ) ) transform_iter_t(boost::end(r), f))
{ {
} }
}; };

View File

@@ -0,0 +1,64 @@
// Boost.Range library
//
// Copyright Neil Groves 2014. 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_DETAIL_DEFAULT_CONSTRUCTIBLE_UNARY_FN_HPP_INCLUDED
#define BOOST_RANGE_DETAIL_DEFAULT_CONSTRUCTIBLE_UNARY_FN_HPP_INCLUDED
#include <boost/optional/optional.hpp>
#include <boost/mpl/if.hpp>
#include <boost/type_traits/has_trivial_constructor.hpp>
namespace boost
{
namespace range_detail
{
template<typename F, typename R>
class default_constructible_unary_fn_wrapper
{
public:
typedef R result_type;
default_constructible_unary_fn_wrapper()
{
}
default_constructible_unary_fn_wrapper(const F& source)
: m_impl(source)
{
}
template<typename Arg>
R operator()(const Arg& arg) const
{
BOOST_ASSERT(m_impl);
return (*m_impl)(arg);
}
template<typename Arg>
R operator()(Arg& arg) const
{
BOOST_ASSERT(m_impl);
return (*m_impl)(arg);
}
private:
boost::optional<F> m_impl;
};
template<typename F, typename R>
struct default_constructible_unary_fn_gen
{
typedef typename boost::mpl::if_<
boost::has_trivial_default_constructor<F>,
F,
default_constructible_unary_fn_wrapper<F,R>
>::type type;
};
} // namespace range_detail
} // namespace boost
#endif // include guard

View File

@@ -57,6 +57,7 @@ test-suite range :
[ compile-fail compile_fail/adaptor/uniqued_concept3.cpp ] [ compile-fail compile_fail/adaptor/uniqued_concept3.cpp ]
[ compile-fail compile_fail/adaptor/uniqued_concept4.cpp ] [ compile-fail compile_fail/adaptor/uniqued_concept4.cpp ]
[ range-test adaptor_test/adjacent_filtered ] [ range-test adaptor_test/adjacent_filtered ]
[ range-test adaptor_test/chained ]
[ range-test adaptor_test/copied ] [ range-test adaptor_test/copied ]
[ range-test adaptor_test/filtered ] [ range-test adaptor_test/filtered ]
[ range-test adaptor_test/indexed ] [ range-test adaptor_test/indexed ]

View File

@@ -0,0 +1,117 @@
// Boost.Range library
//
// Copyright Neil Groves 2014. 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:
// Jurgen Hunold provided a test case that demonstrated that the range adaptors
// were producing iterators that were not default constructible. This became
// symptomatic after enabling concept checking assertions. This test is a
// lightly modified version of his supplied code to ensure that his use case
// never breaks again. (hopefully!)
//
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/bind.hpp>
#include <boost/test/test_tools.hpp>
#include <boost/test/unit_test.hpp>
#include <vector>
#include <set>
namespace boost_range_test
{
namespace
{
class foo
{
public:
static foo from_string(const std::string& source)
{
foo f;
f.m_valid = true;
f.m_value = 0u;
for (std::string::const_iterator it = source.begin();
it != source.end(); ++it)
{
f.m_value += *it;
if ((*it < 'a') || (*it > 'z'))
f.m_valid = false;
}
return f;
}
bool is_valid() const
{
return m_valid;
}
bool operator<(const foo& other) const
{
return m_value < other.m_value;
}
bool operator==(const foo& other) const
{
return m_value == other.m_value && m_valid == other.m_valid;
}
bool operator!=(const foo& other) const
{
return !operator==(other);
}
friend inline std::ostream& operator<<(std::ostream& out, const foo& obj)
{
out << "{value=" << obj.m_value
<< ", valid=" << std::boolalpha << obj.m_valid << "}\n";
return out;
}
private:
boost::uint64_t m_value;
bool m_valid;
};
void chained_adaptors_test()
{
std::vector<std::string> sep;
sep.push_back("AB");
sep.push_back("ab");
sep.push_back("aghj");
std::set<foo> foos;
boost::copy(sep
| boost::adaptors::transformed(boost::bind(&foo::from_string, _1))
| boost::adaptors::filtered(boost::bind(&foo::is_valid, _1)),
std::inserter(foos, foos.end()));
std::vector<foo> reference;
reference.push_back(foo::from_string("ab"));
reference.push_back(foo::from_string("aghj"));
BOOST_CHECK_EQUAL_COLLECTIONS(
reference.begin(), reference.end(),
foos.begin(), foos.end());
}
} // anonymous namespace
} // namespace boost_range_test
boost::unit_test::test_suite*
init_unit_test_suite(int argc, char* argv[])
{
boost::unit_test::test_suite* test
= BOOST_TEST_SUITE( "RangeTestSuite.adaptor.chained adaptors" );
test->add(BOOST_TEST_CASE( boost_range_test::chained_adaptors_test));
return test;
}