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

早过忘川 提交于 2019-12-04 06:03:08

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

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);
}

How about this?

This is similar to the above case using boost::fusion::for_each .

But faster when i < size(t) than the above.

usage

main(){
  boost::fusion::vector<int,double,std::string,char> vec(9 ,2.2 ,"aaa" ,'b');
  std::cout << at_n_dynamic<double>(vec, 1) << std::endl; //=> 2.2
}

body

#include <boost/fusion/include/vector.hpp>    
template<typename V>
struct fusion_at_n_functor
{
   mutable int i;
   int n;
   mutable V value;
   fusion_at_n_functor(int _n):i(0),n(_n){}
   void operator()(const V & t) const
   { if(i==n){value=t;} i++;}  
   template<typename T>
   void operator()(const T & t) const
   { i++;}
};

template <typename First,typename Last,typename AtN > void
at_n_dynamic_fusion_impl(First i,Last last,AtN &atn,boost::mpl::true_ ){}
template <typename First,typename Last,typename AtN > void
at_n_dynamic_fusion_impl(First i,Last last,AtN &atn,boost::mpl::false_ ){  
  if(atn.i == atn.n ){atn(boost::fusion::deref(i));}
  else{
    atn(boost::fusion::deref(i));
    at_n_dynamic_fusion_impl(boost::fusion::next(i),last,atn,
           boost::fusion::result_of::equal_to<
             typename boost::fusion::result_of::next<First>::type,Last>  ());}
}

template <typename Ret,typename Sequence>  Ret
 at_n_dynamic(Sequence & seq, int n){ 
   fusion_at_n_functor<Ret> atn(n);
#if 0 
  // enabling this if is same to the above case of boost::fusion::for_each 
   boost::fusion::for_each(seq, atn);
#else
  // this recursion loop stop at n. but boost::fusion::for_each stop at last
   at_n_dynamic_fusion_impl(boost::fusion::begin(seq),boost::fusion::end(seq) ,atn,
         boost::fusion::result_of::equal_to<
           typename boost::fusion::result_of::begin<Sequence>::type,
           typename boost::fusion::result_of::end<Sequence>::type>());    
#endif
    return atn.value;}

This is copy of boost-users ML my post http://lists.boost.org/boost-users/2012/08/75493.php http://d.hatena.ne.jp/niitsuma/20120803/1343979718

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!