Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use boost::filter_iterator for output?

Tags:

c++

stl

boost

I am using std::transform with an std::back_inserter to append elements to an std::deque. Now the transformation may fail and will return a invalid object (say an uninitialized boost::optional or a null pointer) in some cases. I would like to filter out the invalid objects from getting appended.

I thought about using boost::filter_iterator, but not sure how to present the end() parameter of the filtered range.

The documentation of boost::filter_iterator suggests that output filtering is possible. Should I just specialize operator == for std::back_insert_iterator in this case to always return false?

In addition to this, if I want to append values of initialized boost::optional or pointers, can I chain boost::filter_iterator and boost::indirect_iterator?

I am trying to avoid rolling out my own transform_valid function that takes an optional extractor function.

Is it even possible to use filter_iterator as an output iterator?

like image 399
zrb Avatar asked Aug 31 '11 08:08

zrb


1 Answers

I suggest using boost range (algorithms & adaptors) for ease of use, you'd write:

boost::copy(
    data | transformed(makeT) | filtered(validate) /* | indirected */, 
    std::back_inserter(queue));

Here is a complete working example of that:

#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/optional.hpp>

#include <vector>
#include <deque>

typedef boost::optional<int> T;
typedef std::deque<T> Q;

static T makeT(int i)
{
    if (i%2) return T();
    else     return i;
}

static bool validate(const T& optional) 
{ 
    return (bool) optional; // select the optional that had a value set
}

int main()
{
    static const int data[] =  { 1,2,3,4,5,6,7,8,9 };

    Q q;

    using boost::adaptors::filtered;
    using boost::adaptors::transformed;

    // note how Boost Range elegantly supports an int[] as an input range
    boost::copy(data | transformed(makeT) | filtered(validate), std::back_inserter(q));

    // demo output: 2, 4, 6, 8 printed
    for (Q::const_iterator it=q.begin(); it!=q.end(); ++it)
    {
        std::cout << (*it? "set" : "unset") << "\t" << it->get_value_or(0) << std::endl;
    }

    return 0;
}

Update

With a little help from this answer: Use boost::optional together with boost::adaptors::indirected

I now include an elegant demonstration of using the indirected range adaptor as well for immediate output of the queue (dereferencing the optionals):

Note that for (smart) pointer types there would obviously be no need to provide the pointee<> specialisation. I reckon this is by design: optional<> is not, and does not model, a pointer

#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>

#include <boost/optional.hpp>

namespace boost {
    template<typename P> struct pointee<optional<P> > {
        typedef typename optional<P>::value_type type;
    };
}

typedef boost::optional<int> T;

static T    makeT(int i)                { return i%2?  T() : i; }
static bool validate(const T& optional) { return (bool) optional; }

int main() {
    using namespace boost::adaptors;

    static int data[] =  { 1,2,3,4,5,6,7,8,9 };
    boost::copy(data | transformed(makeT) 
                     | filtered(validate) 
                     | indirected, 
                     std::ostream_iterator<int>(std::cout, ", "));
}
like image 103
sehe Avatar answered Oct 23 '22 21:10

sehe