
Roman Perepelitsa wrote:
2008/9/24 Nat Goodspeed <nat@lindenlab.com>
As I work with Boost.Range, I frequently want to call a method returning an iterator_range and, from that range, instantiate a standard container. I have a couple of helpers I'd like to propose for inclusion in the Boost.Range library.
/** * From any range compatible with Boost.Range, return an instance of any * class with a constructor accepting a compatible iterator pair. */ template<class TYPE, typename RANGE> TYPE make_from_range(const RANGE& range) { return TYPE(boost::begin(range), boost::end(range)); }
It would probably be appropriate to provide a non-const RANGE& overload as well.
make_from_range<CONTAINER>(range) is helpful for, e.g., passing an argument to a function accepting const CONTAINER&. But what if you want to declare a local instance of such a container?
/** * From any range compatible with Boost.Range, instantiate any class * with a constructor accepting a compatible iterator pair. */ template<class TYPE> struct instance_from_range: public TYPE { template<typename RANGE> instance_from_range(const RANGE& range): TYPE(boost::begin(range), boost::end(range)) {} // Again, a non-const RANGE& overload would probably be useful. };
Usage example:
instance_from_range< std::vector<std::string> > my_vector(function_returning_range_of_string());
Why not vector<string> my_vector(make_from_range<vector<string> >(get_range()); ? RVO will make sure that no copies of a container are created.
If you don't want to specify vector<string> twice you can follow the approach of Boost.Assign -- create an object with a conversion to anything and let it be converted to a container.
template <class Range> struct from_range_impl { from_range_impl(const Range& range) : range_(range) {} template <class Container> operator Container() const { return Container(begin(range_), end(range_)); } private: const Range& range_; };
template <class Range> from_range_impl<Range> make_from_range(const Range& range) { return from_range_impl<Range>(range); }
Example: void foo(const vector<string>& v);
iterator_range<string*> range; foo(make_from_range(get_range(range)); vector<string> v(make_from_range(range));
Sometimes you'll still want to explicitly specify type of the container that should be created by make_from_range (for example when you pass it to an overloaded function), but you can keep both versions of make_from_range and use each of them when appropriate.
Roman Perepelitsa. I've been using this for years: #ifndef const_from_range_hpp #define const_from_range_hpp
#include <boost/range.hpp> #include <algorithm> #include <boost/numeric/ublas/vector.hpp> #include <boost/multi_array.hpp> namespace detail { template<typename cont_t, typename range> struct construct_from_range_impl { cont_t operator() (range const& r) { return cont_t (boost::begin (r), boost::end (r)); } }; template<typename T, typename range> struct construct_from_range_impl<boost::numeric::ublas::vector<T>, range> { typedef typename boost::numeric::ublas::vector<T> ret_t; ret_t operator() (range const& r) { ret_t v (boost::size (r)); std::copy (boost::begin (r), boost::end (r), v.begin()); return v; } }; template<typename T, typename range> struct construct_from_range_impl<boost::multi_array<T,1>, range> { typedef typename boost::multi_array<T,1> ret_t; ret_t operator() (range const& r) { ret_t v (r); return v; } }; } template<typename cont_t, typename range> cont_t inline construct_from_range (range const& r) { return detail::construct_from_range_impl<cont_t,range>() (r); } #endif