views:

694

answers:

4

Edit: I've edited the sample to better resemble the problem I have, now the function depends on a regular parameter (and not only on template parameters) which means that the computations can't be made at compile time.


I wrote some code with a hand written typelist and now we've started using boost and I'm trying to move it to the mpl library.

I can't seem to find any decent documentation for mpl::list and I'm even failing to port the code to boost::mpl. I've got the feeling that even when (if?) I do succeed in porting the code it will still not be idiomatic. Can' you please let me know how the following should be written with boost (note that this is not the actual code, it's a contrived simplification).

Original code (codepad.org paste)

class nil {};

template <class Head, class Tail = nil>
struct type_list {
    typedef Head head;
    typedef Tail tail;
};

template <class List>
struct foo;

template <class Head, class Tail>
struct foo<type_list<Head, Tail> >{
    template <class T>
    static void* bar(T* obj, size_t size)
    {
        if (sizeof(Head) == size) 
            return reinterpret_cast<Head*>(obj);

        // Otherwise check the rest of the list
        return foo<Tail>::bar(obj, size);
    }
};


template <>
struct foo<nil>
{
    template <class T>
    static void* bar(T*, size_t)  { return NULL; }
};

#include <iostream>

int main()
{
    int n = 3;
    void *p = foo<type_list<char, type_list<bool, 
                      type_list<double, type_list<long> > > >
                 >::bar(&n, 4); 
    std::cout<< p << std::endl;
}

Failed Attempt to use Boost (codepad.org paste)

#include <boost/mpl/list.hpp>

template <class List>
struct foo{
    template <class T>
    static void* bar(T* obj, size_t size)
    {
        typedef typename boost::mpl::front<List>::type type;
        if (sizeof(type) == size) 
            return reinterpret_cast<type*>(obj);

        // Otherwise check the rest of the list
    return foo<typename List::next>::bar(obj, size);
  }
};

template <>
struct foo<boost::mpl::list0<boost::mpl::na> >
{
    template <class T>
    static void* bar(T*)
    {
        return NULL;
    }
};

#include <iostream>

int main()
{
    int n = 3;
    void *p = foo<boost::mpl::list<char, bool, double, long> >::bar(&n, 4);
    std::cout << p << std::endl;
}
+1  A: 

Use boost::mpl::fold like this:

#include <boost/mpl/list.hpp>
#include <boost/mpl/fold.hpp>

#include <iostream>

using namespace boost::mpl;

// Initial state:
struct foo_start {
    template <typename T>
    static void * bar( T *, size_t ) { return 0; }
};

// Folding Step: add This to Prev
template <typename Prev, typename This>
struct foo_iteration {
    struct type {
        template <typename T>
        static void * bar( T * obj, size_t size ) {
            if ( sizeof(This) == size )
                return reinterpret_cast<This*>(obj);
            else
                return Prev::bar( obj, size );
        }
    };
};

// foo is just calling mpl::fold now:
template <typename List>
struct foo : fold< List, foo_start, foo_iteration<_,_> >::type {};

int main() {
    int n = 3;
    void * p = foo< list<char, bool, double, long> >::bar( &n, 4 );
    std::cout << p << std::endl;
}

Prints 0 here, but then I'm on amd64, so I need to change the 4 to an 8, and get something non-zero.

HTH

This happens at compile time, I've updated the question to show that I need the computations to occur at run-time.
Motti
Folding is exactly what you're doing. You just need the correct initial state, and the correct meta function...
I don't get it, could you please post how you would achieve what I wrote with fold?
Motti
Thanks for bearing with me :)
Motti
A: 

Warning: haven't done any C++ (meta programming for that matter) in a while, so I might be wrong.

If I understand correctly, your original code finds the first type, which has the same size as does the passed argument and casts the argument to that type. Assuming that, implementing it with mpl would basically boil down to using the find_if algorithm with a custom written predicate to test for type size (see the example in the link above). Just typedef the result, cast it and you're done.

Saulius
A: 

If I understand correctly, you're passing in a T at runtime and hoping that your MPL list of {A, B, C, D} will select and act upon the correct T in that set?

I may be wrong, but this sounds like something suited to Boost.Fusion, which is designed for runtime iteration of compile-time sequences.

Kaz Dragon
+1  A: 

The MPL is ill-suited for mixed compile-time / run-time operations.

The only operation allowed at runtime on MPL sequences is 'for_each'. For all other situations you should roll your own.

So you should effectively consider that MPL types are not meant to be instanciated.

However, there are other facilities in Boost for such a thing.

The old: Boost.Tuple

The new: Boost.Fusion > http://spirit.sourceforge.net/dl_more/fusion_v2/libs/fusion/doc/html/index.html

Boost.Fusion is a bit more complex (it integrates the concept of views, for example), but would be far better suited to your example.

It does not mean that you should not use the MPL. On the contrary, it is explicitly stated in the Boost.Fusion reference document that one should use MPL algorithms for compile-time computations, and then build the Boost.Fusion container only at the moment you cross the compile-time / runtime boundary (even though Boost.Fusion container are supposed to work with MPL algorithms).

So, keep your mpl implementation transform the resulting list to a Boost.Fusion sequence. Then instantiate the sequence and make use of all the Boost.Fusion facilities.

Matthieu M.