4

I am trying to iterate over a C++ template structure thanks to BOOST_FUSION_ADAPT_TPL_STRUCT. My structure contains fixed-size multidimensional arrays whose sizes are template parameters. If we consider Boost's example modified to fit my problem:

#include <iostream>
#include <string>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

// Example:
// http://www.boost.org/doc/libs/1_53_0/libs/fusion/doc/html/fusion/adapted/adapt_tpl_struct.html

namespace demo
{
    template<typename T, unsigned int SIZE1, unsigned int SIZE2, typename Name, typename Age>
    struct employee
    {
        Name name;
        Age age;
        T ar[SIZE1][SIZE2];
    };
}

// Any instantiated demo::employee is now a Fusion sequence
BOOST_FUSION_ADAPT_TPL_STRUCT(
    (T)(SIZE1)(SIZE2)(Name)(Age),
    (demo::employee) (T)(SIZE1)(SIZE2)(Name)(Age),
    (Name, name)
    (Age, age)
    (T, ar[SIZE1][SIZE2]))

int main()
{
    demo::employee<float, 2, 2, std::string, int> e;
    e.name = "Bob";
    e.age = 25;
    e.ar[0][0] = e.ar[1][0] = 0.1;
    e.ar[0][1] = e.ar[1][1] = 0.2;
}

The compilation fails. Moreover, it also fails if we only add an integer template parameter without even using it for the array size.

Is that even possible with BOOST_FUSION_ADAPT_TPL_STRUCT? If not, how should I go about this?

BenC
  • 8,729
  • 3
  • 49
  • 68

2 Answers2

3

From doc:

The sequence (template_param0)(template_param1)... declares the names of the template type parameters used.

While you have non-type template parameter SIZE:

template<typename T, unsigned int SIZE, typename Name, typename Age>
struct employee

You may convert it to type template parameter and use boost::mpl::int_ as wrapper for carry size.

Now, your code is compiled.

template<int Size>
struct Array
{
    template<typename T>
    struct Of
    {
        typedef T type[Size];
    };
};

namespace demo
{
    template<typename T, typename SIZE, typename Name, typename Age>
    struct employee
    {
        Name name;
        Age age;
        T ar[SIZE::value];
    };
}

// Any instantiated demo::employee is now a Fusion sequence
BOOST_FUSION_ADAPT_TPL_STRUCT(
    (T)(SIZE)(Name)(Age),
    (demo::employee) (T)(SIZE)(Name)(Age),
    (Name, name)
    (Age, age)
    (typename Array<SIZE::value>::template Of<T>::type, ar))

 //...
 demo::employee<float, int_<2>, std::string, int> e;
Evgeny Panasyuk
  • 9,076
  • 1
  • 33
  • 54
  • Ok, thanks for the answer! I guess I will have to see if using `BOOST_FUSION_ADAPT_TPL_STRUCT` is worth doing some refactoring in my library. – BenC Apr 12 '13 at 02:14
  • You are welcome! Maybe you should also consider [`BOOST_FUSION_DEFINE_TPL_STRUCT`](http://www.boost.org/doc/libs/1_53_0/libs/fusion/doc/html/fusion/adapted/define_tpl_struct.html) if appropriate, in order to reduce duplication. – Evgeny Panasyuk Apr 12 '13 at 02:16
  • The data structure I am working on is used for GPGPU computing with CUDA. I guess I would have to check whether `nvcc` can handle these Boost-generated structures properly. – BenC Apr 12 '13 at 02:19
  • note, I updated answer - all type info including array size should be as first parameter. – Evgeny Panasyuk Apr 12 '13 at 02:40
  • I see, I guess this is a requirement for `BOOST_FUSION_DEFINE_TPL_STRUCT`. I will adapt your code to n-dimensional arrays then. Or should I try `Boost.MultiArray`? – BenC Apr 12 '13 at 02:52
  • It is also requirenment of `BOOST_FUSION_ADAPT_TPL_STRUCT` - `ar[SIZE::value]` is not actually "member_name", size of array is part of type. "Or should I try Boost.MultiArray?" - I guess you meant `boost::array` - you still should use some kind of trick to remove `,` from `array` in macro - http://stackoverflow.com/q/13842468/1762344 – Evgeny Panasyuk Apr 12 '13 at 02:57
  • I did mean `boost::multi_array`, my arrays are actually multidimensional (with different template parameters for each dimension). – BenC Apr 12 '13 at 03:42
  • You mentioned that you use this struct with CUDA. Do you use it only on host, or on device also? – Evgeny Panasyuk Apr 12 '13 at 03:47
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/28071/discussion-between-benc-and-evgeny-panasyuk) – BenC Apr 12 '13 at 03:49
1

After a long talk with Evgeny Panasyuk, I ended up doing something a bit different. Since I wanted to be able to do some simple arithmetic operations on the data structures, I decided to use Eigen::Map rather than Boost::multi_array since it offers a wide range of operators as well as a clear documentation.

Thus, the higher-level loop is handled by boost::fusion::for_each and the lower-level loops are handled by Eigen. The arrays are mapped linearly to Eigen vectors. The sizes are passed in data_eigen's constructor.

#include <iostream>

#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/bind.hpp>

#include <Eigen/Core>

namespace demo
{
template<typename T, int SIZE1, int SIZE2>
struct data
{
    T ar1[SIZE1][SIZE2];
    T ar2[SIZE1][SIZE2];
};

template<typename T>
struct EigenMap
{
    typedef Eigen::Map<Eigen::Matrix<T, Eigen::Dynamic, 1> > type;
};

template<typename T>
struct data_eigen
{
    template <int SIZE1, int SIZE2>
    data_eigen(data<T,SIZE1,SIZE2>& src)
        : ar1(typename EigenMap<T>::type(&src.ar1[0][0], SIZE1*SIZE2)),
          ar2(typename EigenMap<T>::type(&src.ar2[0][0], SIZE1*SIZE2))
    {
    }

    typename EigenMap<T>::type ar1;
    typename EigenMap<T>::type ar2;
};


struct print
{
    template<typename T>
    void operator()(const Eigen::Map<Eigen::Matrix<T, Eigen::Dynamic, 1> >& t) const
    {
        std::cout << t.transpose() << std::endl;
    }
};

struct scalarMult
{
    template<typename T, typename U>
    void operator()(T& t, U& u) const
    {
        t *= u;
    }
};

}

BOOST_FUSION_ADAPT_TPL_STRUCT
(
    (T),
    (demo::data_eigen) (T),
    (typename demo::EigenMap<T>::type, ar1)
    (typename demo::EigenMap<T>::type, ar2)
)

int main()
{
    typedef float REALTYPE;
    const int SIZE1 = 2;
    const int SIZE2 = 2;

    // Basic data structure with multidimensional arrays
    demo::data<REALTYPE, SIZE1, SIZE2> d;
    for (unsigned int i = 0; i < SIZE1; ++i)
        for (unsigned int j = 0; j < SIZE2; ++j)
        {
            d.ar1[i][j] = (i+1)*(j+1);
            d.ar2[i][j] = i + j;
        }

    // Eigen::Map + BOOST_FUSION_ADAPT_TPL_STRUCT
    demo::data_eigen<REALTYPE> eig_d(d);

    std::cout << "d:" << std::endl;
    boost::fusion::for_each(eig_d, demo::print());
    std::cout << std::endl;

    boost::fusion::for_each(eig_d, boost::bind<void>(demo::scalarMult(), _1, 2.0));
    std::cout << "2 * d:" << std::endl;
    boost::fusion::for_each(eig_d, demo::print());
    std::cout << std::endl;
}
BenC
  • 8,729
  • 3
  • 49
  • 68