C++ type registration at compile time trick

2019-01-11 02:25发布

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::vector. Do you know any trick to do so nicely?

My desire is to have hpp file which implements functor type and registration file, where a macro brings in type into compilation.

For example

// registered.hpp
REGISTER("functor1.hpp") // implementation
REGISTER("functor2.hpp")
...
boost::mpl::vector<...> types; // full registration vector

Hopefully it makes sense. Thank you

3条回答
叼着烟拽天下
2楼-- · 2019-01-11 02:25

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.

查看更多
SAY GOODBYE
3楼-- · 2019-01-11 02:29

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, "");
查看更多
beautiful°
4楼-- · 2019-01-11 02:36

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

查看更多
登录 后发表回答