Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fill boost::fusion::vector at runtime?

Firstly, apologies for the similarity to my previous question here, but I don't think I asked the right thing.

I have a method:

template <typename T>
void some_method( T &t)
{...}

which takes a type fusion::vector<T1, T2, T3, ..., Tn> to be determined at runtime - e.g. vector<int, double> in one call and vector<int, double, int> in another.

I want to fill this vector dynamically with something like:

int blah = 5;
for(int i = 0; i<size(t); i++){
at_c<i>(t) = blah;
}

This doesn't work since at_c expects a const.

I've tried other stuff (see the previous question) but still can't work out how to achieve this.

Any help much appreciated! Thanks.

like image 535
arlogb Avatar asked Oct 27 '12 00:10

arlogb


2 Answers

As @Mankarse specified correctly, you can't use fusion containers in a for loop and that's because fusion containers are all about tuple and each element may have different type from other elements, all functions that iterate through a fusion container are actually a couple of functions and usually implemented as template or overloaded functions. So in order to initialize a fusion container from a vector you should have multiple functions (or simply a template that will be compiled to multiple classes or functions) that all have access to that vector(or at least an iterator from the vector and an state variable that can increased for each call). So you have 2 options:

1) Use boost::fusion::fold:

template< class StdIteratorT >
struct initialize_fusion_container_from_std_iterator {
    typedef StdIteratorT    result_type;

    template< class T >
    StdIteratorT operator()( StdIteratorT i, T& val ) {
        val = *i;
        return ++i;
    }
};
void use_fold_demo() {
    int p1[] = {4, 5, 6};
    fusion::vector<int, double, int> fv;
    std::vector<int> sv2( p1, p1 + _countof(p1) );
    fusion::fold( fv, sv2.begin(),
    initialize_fusion_container_from_std_iterator<std::vector<int>::iterator>() );
}

2) Write a function that recursively call itself with next item of the container(remember syntax of this function is like recursive functions but it is not recursive at all):

// this will be called when we reach end of the fusion container(FIBeginT==FIEndT)
template< class FIBeginT, class FIEndT, class StdIteratorT >
void set_fusion_iterator( FIBeginT b, FIEndT e, StdIteratorT i, boost::mpl::true_ )
{
}
// this will be called when FIBeginT != FIEndT
template< class FIBeginT, class FIEndT, class StdIteratorT >
void set_fusion_iterator( FIBeginT b, FIEndT e, StdIteratorT i, boost::mpl::false_ )
{
    *b = *i;
    set_fusion_iterator( fusion::next(b), e, ++i,
        fusion::result_of::equal_to<
            typename fusion::result_of::next<FIBeginT>::type, FIEndT >() );
}

void recursive_function_demo() {
    typedef fusion::vector<int, double, int>    my_fusion_vector;

    int p1[] = {1, 2, 3};
    std::vector<int> sv1( p1, p1 + _countof(p1) );
    fusion::vector<int, double, int> fv;
    set_fusion_iterator( fusion::begin(fv), fusion::end(fv), sv1.begin(),
        fusion::result_of::equal_to<
            typename fusion::result_of::end<my_fusion_vector>::type,
            typename fusion::result_of::begin<my_fusion_vector>::type>() );
}

As you see second case is much more complicated, but if you understand its logic you can use it to do anything with fusion containers, so the choice is all yours!!

like image 63
BigBoss Avatar answered Oct 13 '22 22:10

BigBoss


You could use boost::fusion::for_each:

#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/container.hpp>

struct F {
    F(int blah): blah(blah){}
    template <typename T>
    void operator()(T& t) const {
        t = blah;
    }
    int blah;
};

template <typename T>
void some_method(T &t)
{
    boost::fusion::for_each(t, F(6));
}

int main() {
    boost::fusion::vector<int, double, int> idi;
    some_method(idi);
    boost::fusion::vector<int, double> id;
    some_method(id);
}

In an attempt to demystify the for_each, here is some mostly equivalent code that uses numerical indices instead:

#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/container.hpp>
#include <boost/fusion/sequence.hpp>

template<typename T, int N, int End>
struct some_method_impl {
    void operator()(T& t) const {
        int blah = 6;
        boost::fusion::at_c<N>(t) = blah;
        some_method_impl<T, N+1, End>()(t);
    }
};

template<typename T, int N>
struct some_method_impl<T,N,N> {
    void operator()(T& t) const {}
};


template <typename T>
void some_method(T &t)
{
    some_method_impl<T,0,boost::fusion::result_of::size<T>::type::value>()(t);
}

int main() {
    boost::fusion::vector<int, double, int> idi;
    some_method(idi);
    boost::fusion::vector<int, double> id;
    some_method(id);
}
like image 23
Mankarse Avatar answered Oct 13 '22 22:10

Mankarse