Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the benefits of using boost::any_range?

What are the benefits of using boost::any_range? Here is an example:

typedef boost::any_range<
    int
  , boost::forward_traversal_tag
  , int
  , std::ptrdiff_t
> integer_range;

void display_integers(const integer_range& rng)
{
    boost::copy(rng,
                std::ostream_iterator<int>(std::cout, ","));

    std::cout << std::endl;
}

int main(){
    std::vector<int> input{ ... };
    std::list<int> input2{ ... };
    display_integers(input);
    display_integers(input2);
}

But the same functionality with more efficiency can be achieved with a template parameter, which satisfies the ForwardRange concept:

template <class ForwardRange>
void display_integers(const ForwardRange& rng)
{
    boost::copy(rng,
                std::ostream_iterator<int>(std::cout, ","));

    std::cout << std::endl;
}

So I am searching for scenarios when it is worth to use any_range. Maybe I am missing something.

like image 935
Gabor Marton Avatar asked Mar 15 '13 21:03

Gabor Marton


2 Answers

This technique is called Type Erasure. There is a full article describing the pros and cons on the example of any_iterator: On the Tension Between Object-Oriented and Generic Programming in C++.

It is possible to hide (in a separate file/library) the implementation/definition of

void display_integers(const integer_range& rng)

But in the case of

template <class ForwardRange>
void display_integers(const ForwardRange& rng)

you have to provide source code to users (or at least make explicit instantiations somewhere).

Moreover, in the first case, display_integers will be compiled only once, but in the second it will be compiled for every type of the passed range.

Also, you may have somewhere

integer_range rng;

and during lifetime of rng you may assign ranges of different types to it:

vector<int> v;
list<int> l;
integer_range rng;
rng = v;
rng = l;

The biggest disadvantage of type erasure is its runtime cost; all operations are virtual, and cannot be inlined (easily).


P.S. another famous example of type erasure is std::function

like image 54
Evgeny Panasyuk Avatar answered Oct 14 '22 04:10

Evgeny Panasyuk


boost::any_range can used for returning ranges from functions. Imagine the following example:

auto make_range(std::vector<int> v) -> decltype(???)
{   
    return v | filter([](int x){ return x % 2 == 0;})
        | transform([](int x){ return x * 2;});
}

*: gcc does not compile the above without wrapping it in std::function, hower clang 3.2 works by directly passing the lambda

It is very difficult to know what is being returned from this function. Also, lambda and decltype don't work together so we cannot deduce the type using decltype when passing only a lambda. One solution is to use boost::any_range like the one in your example (another workaround is to use std::function as pointed out by Evgeny Panasyuk in the comments):

integer_range make_range(std::vector<int> v)
{
     return v | filter([](int x){ return x % 2 == 0;})
        | transform([](int x){ return x * 2;});
}

Working example with gcc using std::function.

Working example with clang passing lambdas directly.

like image 10
Jesse Good Avatar answered Oct 14 '22 03:10

Jesse Good