1

I am trying to write a metafunction which (in haskell) looks roughly like:

gather :: [a] -> [a] -> ([a], [a])
gather (x:xs) (_:_:ys) = <something using x, xs, and ys>
...other pattern matches...

I was able to do this using a roll-my-own own variadic template sequence but can't seem to figure out how to do this using mpl.

for simplicity I was trying this sample function (should help me understand what I need):

//get_first :: [a] -> a
template<class SEQ_C>
get_first { 
    enum { value = -1 }; 
    typedef get_first<SEQ_C> type;
}; 

//get_first (x:xs) = x
template<template<class T, T... S> class SEQ_C, class T, T x, T... xs>
struct get_first<SEQ_C<T, x, xs...>> {
    enum { value = x };
    typedef get_first<SEQ_C<T, x, xs...>> type;
};

...

typedef boost::mpl::vector_c<int 1, 2, 3> listA;
typedef get_first<listA>::type first;
std::cout << first::value << std::endl;

outputs -1.

I have tried a number of different ways of getting a match at this point I am just taking stabs in the dark. The documentation makes it seem like mpl::vector_c<int, x> is actually a list of integral_c<int, x> -- but attempt to use this result is other errors.

Maybe pattern matching

nickdmax
  • 539
  • 2
  • 4
  • 11
  • As far as I can tell the problem is that boost::mpl sequences do not actually use variadic templates but simulate them. So `mpl::vector_c` becomes `mpl::vector` where the `2147483647l` is repeated until we reach BOOST_MPL_LIMIT_VECTOR_SIZE. In my roll-my-own I actually used `template` which told the compiler what to expect. I suppose it is off to the standard to try and work out how things are *supposed* to work: If you pass a non-variadic template type as a template parameter should it match? – nickdmax Jun 27 '13 at 14:23

2 Answers2

2

Wow, I find the root of the problem. Look at the error message(you could see it if you comment typedef get_first<SEQ_C> type; line):

error: ‘type’ in ‘struct get_first<boost::mpl::vector_c<int, 1l, 2l, 3l> >’ does not name a type
//                                                            ^   ^   ^

As you can see, g++ interprets passed arguments as long, not as int. So, if you change your specification to:

template<template<class T, long... S> class SEQ_C, class T, long x, long... xs>
struct get_first<SEQ_C<T, x, xs...>> {
    enum { value = x };
    typedef get_first<SEQ_C<T, x, xs...>> type;
};

It would work.

Of course, it is not a solution, I just show, how it would work. I think, it's bug in g++, since clang++ produces 1 for your code.

awesoon
  • 32,469
  • 11
  • 74
  • 99
  • You know I saw the 'l' on the end of the numbers there and wondered if it made a difference. I dismissed it because my roll-my-own version worked, I think the 'l' are somehow a product of mpl not necessarily the g++. This is particularly irritating BUT I think the key may be a higher level of abstraction. I *think* that `boost::mpl::vector_c::type` might give a more useful type to work with. My concern is that the documentation says this returns a vectorn_c type which may not be very useful to me. – nickdmax Jun 26 '13 at 18:38
  • yes `boost::mpl::vector_c::type` is `boost:mpl::vector1_c` so not a lot of help. The other irritating thing is that even changing the vector type to `mpl::vector` does not allow the template parameter T to work without hard coding the type in. – nickdmax Jun 26 '13 at 19:43
1

So with the help given I was able to come up with something that seems to work. It requires one specialization with long that is used for char, short, int, long types, and the original template for use with long long.

So the final template looks like this:

// get_first :: [a] -> a
// get_first x:xs = x
template<class SEQ_C>
struct get_first { 
    enum { value = -1 }; 
    typedef get_first<SEQ_C> type;
    typedef typename SEQ_C::value_type value_type;
    typedef SEQ_C sequence_type;
};

//needed for char/short/int/long
template<template<class T, long... S> class SEQ_C, class T0, long X, long... XS>
struct get_first<SEQ_C<T0, X, XS...>> {
    enum { value = X };
    typedef get_first<SEQ_C<T0, X, XS...>> type;
    typedef T0 value_type;
    typedef SEQ_C<T0, X, XS...> sequence_type;
};

//needed for long long
template<template<class T, T... S> class SEQ_C, class T0, T0 X, T0... XS>
struct get_first<SEQ_C<T0, X, XS...>> {
    enum { value = X };
    typedef get_first<SEQ_C<T0, X, XS...>> type;
    typedef T0 value_type;
    typedef SEQ_C<T0, X, XS...> sequence_type;
};

demangling the get_first<SEQ>::sequence_type was quite revealing. To that end I found this bit of code quite useful (if you don't feel like using c++filt all the time):

#include<typeinfo>
#include<string>
#include<cstdlib>
#include<cxxabi.h>

std::string demangle(const char* name) {
    int status;
    char *realname;
    std::string retValue;
    realname = abi::__cxa_demangle(name, NULL, NULL, &status);
    if (realname != NULL) {
        retValue = std::string(realname);
        free(realname);
    }
    return retValue;
}

template<class T>
std::string demangle() { return demangle(typeid(T).name()); }

a big thanks to soon who got me 98% of the way there.

nickdmax
  • 539
  • 2
  • 4
  • 11