Question

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

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top