Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to emulate something like conditional_back_inserter?

Tags:

c++

stl

I wanted to replace the loop with an algorithm in the following code

int numbers[] = { ... };
vector<int> output;

for( int* it = numbers+from; it != numbers+to ; ++it ) 
{
    int square = func( *it );
    if( predicate(square) )
    {
         output.push_back(square);
    }
}

The program is meant to transform the values and copy them to a destination if a condition occurs.

  • I could not use std::copy_if because that would not apply a transformation.
  • I could not use std::transform because that lacks a predicate
  • It is not even a good idea to write a transform_copy_if() , because of the intermediate copy of the transformed variable.

It looks like my only hope is to create a conditional_back_insert_iterator. Then I could have a pretty decent call like:

int numbers[] = { ... };
vector<int> output;

std::transform(numbers+from, numbers+to, 
               conditional_back_inserter(predicate, output),   
               func);

Is this solution the best way to treat such cases ? I couldn't even google for conditional inserters, so I am worried I'm on the wrong path.

I could also imagine that I could implement an alternative solution such as

std::copy_if( transform_iterator<func>(numbers+from), 
              transform_iterator<func>(numbers+to), 
              back_inserter(output) );

(which reminds me of an example of *filter_iterators* in boost) but that does not offer readability.

like image 936
Grim Fandango Avatar asked Nov 21 '13 12:11

Grim Fandango


1 Answers

I think creating your own iterator is the way to go:

#include <iostream>
#include <vector>
#include <iterator>
#include <functional>

template<class T>
class conditional_back_insert_iterator
    : public std::back_insert_iterator<std::vector<T>>
{
private:
    using Base       = std::back_insert_iterator<std::vector<T>>;
    using Container  = std::vector<T>;
    using value_type = typename Container::value_type;
public:
    template<class F>
    conditional_back_insert_iterator(Container& other, F&& pred)
        :  Base(other), c(other), predicate(std::forward<F>(pred))
    { }

    conditional_back_insert_iterator<T>& operator*()
    { return *this; }

    conditional_back_insert_iterator<T>&
        operator=(const value_type& val) const
    {
        if (predicate(val))
            c.push_back(val);
        return *this;
    }

    conditional_back_insert_iterator<T>&
        operator=(value_type&& val) const
    {
        if (predicate(val))
            c.push_back(std::move(val));
        return *this;
    }
private:
    Container& c;
    std::function<bool (const value_type&)> predicate;
};

template<
    class Container,
    class F,
    class value_type = typename Container::value_type
>
conditional_back_insert_iterator<value_type>
    conditional_back_inserter(Container& c, F&& predicate)
{
    return conditional_back_insert_iterator<value_type>(c, std::forward<F>(predicate));
}

int main()
{
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};
    std::vector<int> to;

    auto is_even = [] (int x) { return (x % 2) == 0; };

    std::copy(v.begin(), v.end(), conditional_back_inserter(to, is_even));
}
like image 199
David G Avatar answered Oct 12 '22 14:10

David G