I have the following situation: suppose I have a bunch of types (functors) which I want to register/compile in during compilation, preferably into something like boost::mpl:
You'll never solve the mpl::vector idea. You can't alter template "variables". Remember that template metaprogramming is a PURE functional language. No side effects at all.
As for registering...the macro thing works fine. Define the macro so that it declares and initializes some small global variable with the registration process. Alternatively you can go down the road I did here:
How to force inclusion of "unused" object definitions in a library
Note the fix if you're trying to do it in a library.
There is a way to register types one by one and then retrieve all of them in the form of mpl::vector or similar. I've learned this trick on the boost mailing lists (perhaps from Dave Abrahams, although I can't recall for sure).
Edit: I learned it from slide 28 on https://github.com/boostcon/2011_presentations/raw/master/thu/Boost.Generic.pdf.
I won't use MPL in the code to make it self contained.
// The maximum number of types that can be registered with the same tag.
enum { kMaxRegisteredTypes = 10 };
template <int N>
struct Rank : Rank<N - 1> {};
template <>
struct Rank<0> {};
// Poor man's MPL vector.
template <class... Ts>
struct TypeList {
static const int size = sizeof...(Ts);
};
template <class List, class T>
struct Append;
template <class... Ts, class T>
struct Append<TypeList<Ts...>, T> {
typedef TypeList<Ts..., T> type;
};
template <class Tag>
TypeList<> GetTypes(Tag*, Rank<0>) { return {}; }
// Evaluates to TypeList of all types previously registered with
// REGISTER_TYPE macro with the same tag.
#define GET_REGISTERED_TYPES(Tag) \
decltype(GetTypes(static_cast<Tag*>(nullptr), Rank<kMaxRegisteredTypes>()))
// Appends Type to GET_REGISTERED_TYPES(Tag).
#define REGISTER_TYPE(Tag, Type) \
inline Append<GET_REGISTERED_TYPES(Tag), Type>::type \
GetTypes(Tag*, Rank<GET_REGISTERED_TYPES(Tag)::size + 1>) { \
return {}; \
} \
static_assert(true, "")
Usage example:
struct IntegralTypes;
struct FloatingPointTypes;
// Initially both type lists are empty.
static_assert(std::is_same<GET_REGISTERED_TYPES(IntegralTypes), TypeList<>>::value, "");
static_assert(std::is_same<GET_REGISTERED_TYPES(FloatingPointTypes), TypeList<>>::value, "");
// Add something to both lists.
REGISTER_TYPE(IntegralTypes, int);
REGISTER_TYPE(FloatingPointTypes, float);
static_assert(std::is_same<GET_REGISTERED_TYPES(IntegralTypes), TypeList<int>>::value, "");
static_assert(std::is_same<GET_REGISTERED_TYPES(FloatingPointTypes), TypeList<float>>::value, "");
// Add more types.
REGISTER_TYPE(IntegralTypes, long);
REGISTER_TYPE(FloatingPointTypes, double);
static_assert(std::is_same<GET_REGISTERED_TYPES(IntegralTypes), TypeList<int, long>>::value, "");
static_assert(std::is_same<GET_REGISTERED_TYPES(FloatingPointTypes), TypeList<float, double>>::value, "");
I wouldn't use the macros. Usual technique is to just define some object whose initialization does the registration. Pitfall: you need to reference something, e.g. call a function, in the compilation unit, in order to have it linked in.
Cheers & hth.,