1

I have designed a function like this:

template<template<class> class TMapper, class TResult = void, class TUserContext>
static typename TResult mapParam(int index, TUserContext ctx)

It takes a TMapper, which needs to conform to the following concept:

template<class T> struct Mapper { static TResult map(int index, TUserContent ctx) {} }

The Mapper type is instanciated once for each parameter, but the "map" function is only called for the parameter at the given index. The Mapper is parameterized with the parameter's type (T). In a test, we want to be able to do something on a parameter we don't even know exists, and still work with its precise type, but without adding any template metaprogramming into the real testcases...

Is there a way to do this woth Boost.MPL, so that someone reading this code doesn't need to understand it but may just use boost knowledge instead, or at least has more detailed documentationt here?

Here is the full code( T::TParams is a boost::mpl::vector<> of arbitrary function parameter types):

template<template<class> class TMapper, class TResult = void, class TUserContext>
        static typename TResult mapParam(int index, TUserContext ctx) {
            return mapParamInternal<TMapper, TResult, boost::mpl::size<T::TParams>::value - 1>(index, ctx);
        }

template<template<class> class TMapper, class TResult, int CurrentIndex, class TUserContext>
static typename boost::enable_if_c<CurrentIndex >= 0, TResult>::type mapParamInternal(int targetIndex, TUserContext ctx) {
    if(CurrentIndex == targetIndex) {
        return TMapper<
            typename boost::mpl::at<
                T::TParams, 
                boost::mpl::int_<CurrentIndex>
            >::type>::map(targetIndex, ctx);
    } else {
        return mapParamInternal<TMapper, TResult, CurrentIndex - 1>(targetIndex, ctx);
    }
}

template<template<class> class TMapper, class TResult, int CurrentIndex, class TUserContext>
static typename boost::disable_if_c<CurrentIndex >= 0, TResult>::type mapParamInternal(int targetIndex, TUserContext ctx) {
    UNREFERENCED_PARAMETER(targetIndex);
    UNREFERENCED_PARAMETER(ctx);
    return TResult();
}

To give you an example of how this might be useful:

template<class TParam>
struct UpdateParameter {
    static void map(int index, CallSerializerTest<T>* self) {
        self->trace.updateParameter(
            TCallSig::getParamName(index), 
            TypeUpdatedValue<TParam>::get());
    }
};

void updateParameter(int index) {
    updatedParams.push_back(index);
    TCallSig::mapParam<UpdateParameter>(index, this);
}

The above code will call "self->trace.updateParameter" exactly once, for the parameter given by "index", and "TParam" will have the proper type of that parameter. "TCallSig" is an arbitrary call signature for which the "mapParam" function is defined. We get tons of those signatures via Google test type parameterized tests. This is a really cool system I think, and it makes the test cases very concise and easy to read once you got the hang of the meta-programming behind.

thesaint
  • 1,253
  • 1
  • 12
  • 22
  • I have readed the question twice and still I haven't a clear idea of what are you trying. But at least I can give you some notes: **1)** I think you are mixing runtime/compile-time values, which is not possible. Specifically in your mapping function you have a condition comparing the passed (runtime) index with the current (compile-time) index. C++ has no static-if feature, so both branches will be compiled. Are you sure that the return types of both branches are the same? I'm affraid not. – Manu343726 Aug 04 '14 at 09:49
  • Also the reason why I don't understand this is because the lack of more information (What is `TCallSig` in the example? Please try to rephrase the question providing more information about the problem. Thanks – Manu343726 Aug 04 '14 at 09:54
  • @Manu343726: I am not sure what else to say except what's already written. Have you read the example at the bottom too? TCallSig is irrelevant for the algorithm. Its just the context in which mapParam() is defined. And yes I am mixing compile/runtime here, and its also not a try, it works ;). I just wanted to know if boost has a proper MPL algorithm. I just looked at fold, accumulate, for_each etc... and they all seem to lack the features required. – thesaint Aug 04 '14 at 10:18
  • I am actually not sure yet if it works under all circumstances. The return values are not strictly necessary anymore, so you could as well ignore them. I think they might cause some trouble along the road if I try more fancy signatures. The general idea is to evaluate a given Mapper only for the i-th type in a type list. I think especially the title sums it up pretty accurately. The main problem solved here is that "i-th" is runtime, while types are compile time. The above algorithm manages to mix both worlds. – thesaint Aug 04 '14 at 10:20

1 Answers1

2

Check the Boost.Fusion library. This library aims to act as a bridge between the compile-time and the runtime world, providing features and algorithms which use properties of the two worlds.
The library has a functional non-mutable spirit, and provides algorithms similar to those on functional languages such as map, fold, etc. But the point is that Fusion has the ability of using compile-time properties in such constructs (Thanks to MPL). For example (Extracted from the intro):

template <typename Sequence>
void xml_print_pointers(Sequence const& seq)
{
    for_each(filter_if<boost::is_pointer<_> >(seq), print_xml());
}

As you can see this executes an operation on each element of a runtime sequence only if that element is a pointer (Which is a compile-time property of the element type).

I'm sure you could easily implement your algorithm using those constructs.

Manu343726
  • 13,969
  • 4
  • 40
  • 75
  • 1
    I have never looked into Boost.Fusion. I think its possible using "iter_fold" of Boost.Fusion. It gives you a state, which could just be a reference to a return value and allows you to access the current type iterator in each fold operation, which allows you to compare the index with a targetIndex and only do something if that index is equal. I haven't tried it but I think this might solve it. – thesaint Aug 04 '14 at 14:29