13

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

Anycorn
  • 50,217
  • 42
  • 167
  • 261

3 Answers3

25

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, "");
Roman Perepelitsa
  • 2,148
  • 19
  • 14
  • Unfortunately I can't find the original thread from which I learned the trick. – Roman Perepelitsa Feb 07 '14 at 13:08
  • **−1** Any stateful-template effect is likely to be removed in later standards as a language defect. Essentially what you have here is a compile time counter (the number of types registered). Even if it works with one particular compiler it's not reasonable to expect it to work with others. – Cheers and hth. - Alf Jul 19 '18 at 18:37
  • 4
    @Cheersandhth.-Alf Why do you think so? What this code actually does is ONLY declaring a NEW overload of a function. I don't see any state being modified. You can't consider adding a new overload as modifying a state, that would mean every possible program is mofiying some state. Minimum code to reason about: https://wandbox.org/permlink/ALpFkVQycMfEZNoG – barsdeveloper Feb 08 '20 at 11:00
0

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

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Ideally I'd like to put all those types into containers with one line. I know I can do it otherwise with 2-3 lines, but I want to know if there is a trick. – Anycorn Jan 25 '11 at 06:59
-4

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.

Community
  • 1
  • 1
Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • my idea was like: first registration -> types1, append second to make types2, etc ... Finaly typedef typesN to types. – Anycorn Jan 25 '11 at 07:06
  • 1
    `mpl::for_each` gives you side effects at run time. Presumably you can build an `mpl::vector` of types which you may do SOMEthing useful with; some sort of policy chain perhaps? Anyway, both `for_each` and the `fold` family allow you to construct constructs that can have runtime effects. My favorite is to have a cons car/cdr type template that tests against an mpl::contraint and either calls a member of car or expands and calls the car of cdr. – KitsuneYMG Jan 25 '11 at 07:22
  • @ymg - Yes, but there's no way to construct an mpl sequence from multiple, independent locations. The only thing you can possibly do is define a sequence somewhere and use it. So there'd be no "registration" theme going on. It'd be no more efficient than simply subclassing the factory and hard-coding the types into its constructor. Less so actually. – Edward Strange Jan 25 '11 at 17:28
  • @CrazyEddie well, [someone actually did it](http://b.atch.se/posts/constexpr-meta-container/). But the [standard think it should be ill-formed](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118). – Guillaume Racicot Sep 25 '17 at 20:01