Search code examples
c++boostboost-fusion

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.


Solution

  • 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!!