4

Currently, boost::fusion::for_each iterates over the elements of a single sequence. I am trying to create a function which will work in a similar way but with many sequences and will iterate over all possible combinations between sequences.

For example if I have three sequences S1, S2, S3, I would like to create a functor like this

struct my_functor {

template <class x, class y, class z>
void operator()(x& el1, y& el2, z& el3) {...}
}

and then call

for_each(s1, s2, s3, my_functor()) // applies the functor to all combinations of elements of s1, s2, s3

where s1, s2, s3 are instances of S1, S2, S3.

I started by writing code for the general case (any number of sequences) but found it too hard. So I decided to start with only two sequences and take it from there. I have managed to get it done when I have two sequences (assuming fusion::vectors for simplicity) like this:

//for_each.hpp

#include <boost/fusion/include/mpl.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/back.hpp>
#include <boost/mpl/size.hpp>

template <class Seq1, class Seq2, int i1, int i2, class F>                                     
struct my_call {                                                           

  static void apply(Seq1& seq1, Seq2& seq2, F& f) {                                        
    f(boost::fusion::at_c<i1>(seq1), boost::fusion::at_c<i2>(seq2)); // apply functor for a given pair of ints             
    my_call<Seq1, Seq2, i1, i2+1, F>::apply(seq1, seq2, f); // increase second int by 1 and apply functor again            
  }                                                                
};                                                                 

// terminal condition for 2nd sequence                                                 
template <class Seq1, class Seq2, int i1, class F>                                         
struct my_call<Seq1, Seq2, i1, boost::mpl::size<Seq2>::type::value - 1, F> {                               

  static void apply(Seq1& seq1, Seq2& seq2, F& f) {                                        
    f(boost::fusion::at_c<i1>(seq1), boost::fusion::back(seq2));                                   
    my_call<Seq1, Seq2, i1+1, 0, F>::apply(seq1, seq2, f); // reset 2nd int and increase 1st by 1                  
  }                                                                
};                                                                 

// terminal condition for both sequences                                               
template <class Seq1, class Seq2, class F>                                             
struct my_call<Seq1, Seq2, boost::mpl::size<Seq2>::type::value - 1, boost::mpl::size<Seq2>::type::value - 1, F> {          

  static void apply(Seq1& seq1, Seq2& seq2, F& f) {                                        
    f(boost::fusion::back(seq1), boost::fusion::back(seq2));                                       
  }                                                                
};                                                                 


// the actual function                                                         
template <class Seq1, class Seq2, class F>                                             
void for_each(Seq1& seq1, Seq2& seq2, F& f) {                                              
  my_call<Seq1, Seq2, 0, 0, F>::apply(seq1, seq2, f);                                          
}                                                                                                                                  

and the main as

//main.cpp
#include "for_each.hpp"
#include <iostream>

struct myf {
  template <class X, class Y>
  void operator()(X& x, Y& y) {
    std::cout << x + y << std::endl;
  }
};

int main() {
  boost::fusion::vector<int, double> x(1, 2.5);
  boost::fusion::vector<double, int> y(2, 5);
  myf F;
  for_each(x, y, F);
  return 0;
}

My main (no pun intended) problem is generalising the above to get it working with any number of sequences. Any suggestions are very welcome! Thanks

Constructor
  • 7,273
  • 2
  • 24
  • 66
linuxfever
  • 3,763
  • 2
  • 19
  • 43
  • 2
    It is easy in C++14 ([demo](http://coliru.stacked-crooked.com/a/a41165a2133b69ce) ).. I am working to do it in C++03 with fusion. – sbabbi Jun 27 '14 at 01:15

1 Answers1

1

You can make sequence of sequences and pass them to a caller function.

int main()
{
    boost::fusion::vector<int, double> x(1, 2.5);
    boost::fusion::vector<double, int> y(2, 5);
    boost::fusion::vector<double, double, double> z(10, 20, 30);

    CallFunc(myf(), boost::fusion::make_vector(x, y));
    CallFunc(myf(), boost::fusion::make_vector(x, y, z));
}

The CallFunc makes cartesian product from elements of each sequence and then passess them to the given functor.

#include <iostream>

#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/container/generation/make_vector.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/include/empty.hpp>
#include <boost/fusion/include/pop_front.hpp>
#include <boost/fusion/include/front.hpp>
#include <boost/fusion/include/push_back.hpp>
#include <boost/fusion/include/invoke.hpp>

struct myf {
    typedef void result_type;

    template <class X, class Y>
    void operator()(X x, Y y) {
        std::cout << x + y << std::endl;
    }
    template <class X, class Y, class Z>
    void operator()(X x, Y y, Z z) {
        std::cout << x + y + z << std::endl;
    }
};

template<class Stop> struct CallFuncOuter;

template<class Func, class Tail, class CallTuple>
struct CallFuncInner
{
    CallFuncInner(Func &f, Tail &seq, CallTuple & args)
        : f(f)
        , tail(seq)
        , args(args)
    {
    }

    template<class HeadArg>
    void operator()(HeadArg & head_arg) const
    {
        CallFuncOuter<boost::fusion::result_of::empty<Tail>::type>()
            (f, tail, boost::fusion::push_back(args, head_arg));
    }

    Func &f;
    Tail &tail;
    CallTuple &args;
};

template<class Func, class Tail, class CallTuple>
CallFuncInner<Func, Tail, CallTuple> MakeCallFuncInner(Func &f, Tail &tail, CallTuple &arg)
{
    return CallFuncInner<Func, Tail, CallTuple>(f, tail, arg);
}

template<class Stop>
struct CallFuncOuter
{
    template<class Func, class SeqOfSeq, class CallTuple>
    void operator()(Func &f, SeqOfSeq & seq, CallTuple & args) const
    {
        boost::fusion::for_each(boost::fusion::front(seq),
            MakeCallFuncInner(
                f,
                boost::fusion::pop_front(seq),
                args));
    }
};

template<>
struct CallFuncOuter<boost::mpl::true_>
{
    template<class Func, class SeqOfSeq, class CallTuple>
    void operator()(Func &f, SeqOfSeq & seq, CallTuple & args) const
    {
        boost::fusion::invoke(f, args);
    }
};

template<class Func, class SeqOfSeq>
void CallFunc(Func &f, SeqOfSeq & seq)
{
    CallFuncOuter<boost::fusion::result_of::empty<SeqOfSeq>::type>()
        (f, seq, boost::fusion::vector<>());
}
pure cuteness
  • 1,635
  • 11
  • 26