Как заполнить boost::fusion::vector во время выполнения?

Во-первых, приносим извинения за сходство с моим предыдущим вопросом здесь, но я не думаю, что спрашивал правильная вещь.

У меня есть метод:

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

который принимает тип fusion::vector<T1, T2, T3, ..., Tn> для определения во время выполнения - например. vector<int, double> в одном звонке и vector<int, double, int> в другом.

Я хочу динамически заполнить этот вектор чем-то вроде:

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

Это не работает, так как at_c ожидает const.

Я пробовал другие вещи (см. предыдущий вопрос), но до сих пор не могу понять, как этого добиться.

Любая помощь высоко ценится! Спасибо.


person arlogb    schedule 27.10.2012    source источник
comment
Что такое blah? Прямой цикл никогда не будет работать, потому что blah должен иметь другой тип на каждой итерации (вам нужно написать рекурсивную функцию шаблона). Можете ли вы привести несколько примеров того, как вы представляете вставляемые значения?   -  person Mankarse    schedule 27.10.2012
comment
Я предполагаю, что значения, которые будут вставлены, будут соответствующим образом приведены к нужному типу. Ради аргумента вы могли бы представить, что blah это int.   -  person arlogb    schedule 27.10.2012
comment
Они не будут определяться во время выполнения, они будут определяться во время компиляции.   -  person K-ballo    schedule 27.10.2012


Ответы (3)


Как правильно указал @Mankarse, вы не можете использовать контейнеры fusion в цикле for, и это потому, что контейнеры fusion связаны с tuple, и каждый элемент может иметь тип, отличный от других элементов, все функции, которые проходят через контейнер fusion, на самом деле являются парой функции и обычно реализуются как template или перегруженные функции. Таким образом, чтобы инициализировать контейнер fusion из vector, у вас должно быть несколько функций (или просто шаблон, который будет скомпилирован в несколько классов или функций), все из которых имеют доступ к этому вектору (или, по крайней мере, итератор из vector и состояние переменная, которая может увеличиваться для каждого вызова). Итак, у вас есть 2 варианта:

1) Используйте 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) Напишите функцию, которая рекурсивно вызывает себя со следующим элементом контейнера (помните, что синтаксис этой функции похож на рекурсивные функции, но он совсем не рекурсивен):

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

Как вы видите, второй случай намного сложнее, но если вы понимаете его логику, вы можете использовать его, чтобы делать что угодно с fusion контейнерами, так что выбор только за вами!!

person BigBoss    schedule 27.10.2012

Вы можете использовать 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);
}

В попытке демистифицировать for_each, вот некоторый в основном эквивалентный код, который вместо этого использует числовые индексы:

#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);
}
person Mankarse    schedule 27.10.2012
comment
Это очень полезно. На самом деле вы совершенно правы, я должен был быть более конкретным насчет blah... Это vector<int>, и я хочу отобразить значение из этого blah в fusion::vector. Возможно ли предложенным вами способом? - person arlogb; 27.10.2012
comment
@arlogb: Да, заархивируйте последовательность вместе с набором индексов, а затем используйте индексы для индексации вектора во время for_each. - person Mankarse; 27.10.2012

Как насчет этого?

Это похоже на приведенный выше случай с использованием boost::fusion::for_each .

Но быстрее, когда i ‹ size(t), чем указано выше.

Применение

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
}

тело

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

Это копия моего сообщения boost-users ML http://lists.boost.org/boost-users/2012/08/75493.php http://d.hatena.ne.jp/niitsuma/20120803/1343979718

person niitsuma    schedule 15.10.2013