[Boost-users] [range] What's the best way to initialize a new container from a range?

487 views
Skip to first unread message

Martin B.

unread,
Aug 26, 2011, 8:30:42 AM8/26/11
to boost...@lists.boost.org
Hi!

Say I have a range R and I want to construct a new container from the
range R. Will I always have to repeat the expression yielding the range,
or is there a shorter way?

Example:

std::vector<int> numbers(
boost::irange(7, 42).begin(),
boost::irange(7, 42).end()
);

Note that it's just an example.

Is it possible to create a container C from a range expression R in one
step? Any helper function for this?

cheers,
Martin

_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users

Ovanes Markarian

unread,
Aug 26, 2011, 2:21:40 PM8/26/11
to boost...@lists.boost.org
Hi!

On Fri, Aug 26, 2011 at 2:30 PM, Martin B. <0xCDC...@gmx.at> wrote:
Hi!

Say I have a range R and I want to construct a new container from the range R. Will I always have to repeat the expression yielding the range, or is there a shorter way?

Example:

   std::vector<int> numbers(
     boost::irange(7, 42).begin(),
     boost::irange(7, 42).end()
   );

Note that it's just an example.

Is it possible to create a container C from a range expression R in one step? Any helper function for this?

What about that:

#include <boost/range/irange.hpp>
#include <vector>

using namespace std;
using namespace boost;

int main()
{
integer_range<int> ir=irange(7,42);
vector<int> numbers(ir.begin(), ir.end());

return 0;
}
 
Regards,
Ovanes

Martin B.

unread,
Aug 29, 2011, 8:15:13 AM8/29/11
to boost...@lists.boost.org
On 26.08.2011 20:21, Ovanes Markarian wrote:
> On Fri, Aug 26, 2011 at 2:30 PM, Martin B. <0xCDC...@gmx.at
> <mailto:0xCDC...@gmx.at>> wrote:
>
> Hi!
>
> Say I have a range R and I want to construct a new container from
> the range R. Will I always have to repeat the expression yielding
> the range, or is there a shorter way?
>
> Example:
>
> std::vector<int> numbers(
> boost::irange(7, 42).begin(),
> boost::irange(7, 42).end()
> );
>
> Note that it's just an example.
>
> Is it possible to create a container C from a range expression R in
> one step? Any helper function for this?
>
>
> What about that:
> ...

> integer_range<int> ir=irange(7,42);
> vector<int> numbers(ir.begin(), ir.end());
>

For this to work I need the exact type of the range, which can be quite
annoying as far as I could tell. (Plus, I *don't want* to care what type
of the range is.)

Really, if I had C++11/auto, I wouldn't mind so much, i.e.

auto xr = get_some_range(...);
vector<int> numbers(xr.begin(), xr.end());

but I don't have an `auto` capable compiler, so spelling out the range
type for this is really crappy.

Writing a make_container function seems fiddly wrt. to the C++03
reference type of the passed range, but maybe someone can help:

template<typename C, typename R>
C make_container(R /*value? ref? const-ref?*/ range) {
return C(range.begin(), range.end());
}

and it would still be very ugly to call:

const vector<int> numbers =
make_container< vector<int> >(irange(7,42));

(Note: I did not compile/test this code!)

any ideas?

Szymon Gatner

unread,
Aug 29, 2011, 8:41:06 AM8/29/11
to boost...@lists.boost.org
2011/8/29 Martin B. <0xCDC...@gmx.at>:

> On 26.08.2011 20:21, Ovanes Markarian wrote:
>>
>> On Fri, Aug 26, 2011 at 2:30 PM, Martin B. <0xCDC...@gmx.at
>> <mailto:0xCDC...@gmx.at>> wrote:
>>
>>    Hi!
>>
>>    Say I have a range R and I want to construct a new container from
>>    the range R. Will I always have to repeat the expression yielding
>>    the range, or is there a shorter way?
>>

I do something like this:

template <class SinglePassRange>
boost::container::vector<typename
boost::range_value<SinglePassRange>::type> makeVector(const
SinglePassRange& rng)
{
boost::container::vector<typename
boost::range_value<SinglePassRange>::type> v(boost::begin(rng),
boost::end(rng));

return move(v);
}

and using it surprisingly often as move semantics allow for cheap
return-by-value

I guess you would also add template parameter for container type.

--
Szymon Gatner
The Lordz Games Studio
www.thelordzgamesstudio.com

Darren Garvey

unread,
Aug 29, 2011, 8:41:43 AM8/29/11
to boost...@lists.boost.org
Hi Martin,

On 29 August 2011 13:15, Martin B. <0xCDC...@gmx.at> wrote:
On 26.08.2011 20:21, Ovanes Markarian wrote:
On Fri, Aug 26, 2011 at 2:30 PM, Martin B. <0xCDC...@gmx.at
<mailto:0xCDC...@gmx.at>> wrote:

   Say I have a range R and I want to construct a new container from
   the range R. Will I always have to repeat the expression yielding
   the range, or is there a shorter way?

   Example:

       std::vector<int> numbers(
         boost::irange(7, 42).begin(),
         boost::irange(7, 42).end()
       );

What about that:
...

integer_range<int> ir=irange(7,42);
vector<int> numbers(ir.begin(), ir.end());


For this to work I need the exact type of the range, which can be quite annoying as far as I could tell. (Plus, I *don't want* to care what type of the range is.)

Really, if I had C++11/auto, I wouldn't mind so much, i.e.

   auto xr = get_some_range(...);
   vector<int> numbers(xr.begin(), xr.end());

but I don't have an `auto` capable compiler, so spelling out the range type for this is really crappy.

If you don't care about the type of range, you could always try:

BOOST_AUTO( xr, get_some_range(...) );
vector<int> numbers( xr.begin(), xr.end() ); 


Cheers,
Darren

Martin B.

unread,
Aug 29, 2011, 11:38:43 AM8/29/11
to boost...@lists.boost.org
On 29.08.2011 14:41, Szymon Gatner wrote:
> 2011/8/29 Martin B.<0xCDC...@gmx.at>:
>> On 26.08.2011 20:21, Ovanes Markarian wrote:
>>>
>>> On Fri, Aug 26, 2011 at 2:30 PM, Martin B.<0xCDC...@gmx.at
>>> <mailto:0xCDC...@gmx.at>> wrote:
>>>
>>> Hi!
>>>
>>> Say I have a range R and I want to construct a new container from
>>> the range R. Will I always have to repeat the expression yielding
>>> the range, or is there a shorter way?
>>>
>
> I do something like this:
>
> ...
> {
> ... v(boost::begin(rng), boost::end(rng));

>
> return move(v);
> }
>
> and using it surprisingly often as move semantics allow for cheap
> return-by-value
>

That code is wrong. (As far as I understand C++11.)
AFAIK, you should *never*(?) explicitly move the return value as:

* The compiler is free to do so anyway. (AFAIK)
* It will prevent RVO. (AFAIK)

so I would just leave the move and do a `return v;` relying on the fact
that even my C++03 my compiler will do RVO and in-place construction of
the returned vector anyway.

cheers,
Martin

Nil Geisweiller

unread,
Aug 29, 2011, 1:22:18 PM8/29/11
to boost...@lists.boost.org
> so I would just leave the move and do a `return v;` relying on the fact that
> even my C++03 my compiler will do RVO and in-place construction of the
> returned vector anyway.

a year ago I check carefully what gcc would do by inspecting the
assembler code generated and indeed it does in-place construction. I
since then carelessly always return objects by copy, and when I
convert portion of the code that was returning by ref to return by
copy I usually do not observe any performance slow-down (although I
must say do not always check).

Nil

Alastair Rankine

unread,
Aug 30, 2011, 7:19:21 AM8/30/11
to boost...@lists.boost.org
On 2011-08-26 12:30:42 +0000, Martin B. said:

> Hi!
>
> Say I have a range R and I want to construct a new container from the
> range R. Will I always have to repeat the expression yielding the range,
> or is there a shorter way?

This is an interesting topic.

There is *almost* a way to do what you want, using boost assign
list_of, as per the example below. Note that I am assuming that you
want to do this efficiently, hence use the slightly less convenient
(because of the size parameter) cref_list_of function.

There are two main problems with this approach.

1. The initial element of the range has to be explictly provided to
cref_list_of. This is the 0 element in the examples below.

2. The range() function for cref_list_of completely fails when using a
boost::irange. With the exception of the initial value, the container
is filled with the final value of the irange (5 in this example). This
looks like a bug to me…

It would be really good to get boost assign enhanced to remove these
limitations. In fact I wonder if it would be particularly hard to
implement an assign::range_of function which uses the same underlying
mechanism (ie an object which is convertible to the requisite container
type).

#define BOOST_TEST_MODULE assignrange
#include <boost/test/unit_test.hpp>

#include <boost/array.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/range/irange.hpp>

BOOST_AUTO_TEST_CASE(assignrange)
{
const boost::array<int, 5> input = {{ 1, 2, 3, 4, 5 }};

const std::vector<int> ivec =
boost::assign::cref_list_of<6>(0).range(input);

const boost::array<int, 6> expected = {{ 0, 1, 2, 3, 4, 5 }};

// Test passes
BOOST_CHECK_EQUAL_COLLECTIONS(ivec.begin(), ivec.end(),
expected.begin(), expected.end());
}

BOOST_AUTO_TEST_CASE(assignirange)
{
const std::vector<int> ivec =
boost::assign::cref_list_of<6>(0).range(boost::irange(1,6));

const boost::array<int, 6> expected = {{ 0, 1, 2, 3, 4, 5 }};

// Test fails!
BOOST_CHECK_EQUAL_COLLECTIONS(ivec.begin(), ivec.end(),
expected.begin(), expected.end());

Thorsten Ottosen

unread,
Sep 3, 2011, 7:11:22 AM9/3/11
to boost...@lists.boost.org
Den 26-08-2011 14:30, Martin B. skrev:
> Hi!
>
> Say I have a range R and I want to construct a new container from the
> range R. Will I always have to repeat the expression yielding the range,
> or is there a shorter way?
>
> Example:
>
> std::vector<int> numbers(
> boost::irange(7, 42).begin(),
> boost::irange(7, 42).end()
> );
>
> Note that it's just an example.
>
> Is it possible to create a container C from a range expression R in one
> step? Any helper function for this?
>

std::vector<int> numbers =
boost::copy_range<std::vector<int>>( boost::irange(7,42) );

-Thorsten

Reply all
Reply to author
Forward
0 new messages