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.
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.