C++编译期类型注册技巧

13
我有以下情况:假设我有一堆类型(函子),我想在编译期间进行注册/编译,最好是编译成类似于boost::mpl::vector的东西。您知道任何技巧可以很好地完成这个任务吗?
我的愿望是拥有一个hpp文件来实现函子类型和注册文件,在其中使用宏将类型引入编译过程中。
例如:
// registered.hpp
REGISTER("functor1.hpp") // implementation
REGISTER("functor2.hpp")
...
boost::mpl::vector<...> types; // full registration vector
希望它有意义。 谢谢。
3个回答

25

有一种逐个注册类型的方法,然后以mpl::vector或类似形式检索所有类型。我在boost邮件列表上学到了这个技巧(也许是来自Dave Abrahams,但我不确定)。

编辑:我从第28页幻灯片上学到了这个技巧:https://github.com/boostcon/2011_presentations/raw/master/thu/Boost.Generic.pdf

我不会在代码中使用MPL使其自包含。

// 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, "")

使用示例:

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
任何有状态模板效果都可能在后续标准中被删除,因为这是一种语言缺陷。本质上,您在这里拥有的是一个编译时计数器(已注册类型的数量)。即使它可以在特定编译器上工作,也不合理指望它能在其他编译器上工作。 - Cheers and hth. - Alf
4
你认为为什么呢?这段代码实际上只是声明了一个新的函数重载,我没有看到任何状态被修改。你不能认为添加一个新的函数重载就是修改状态,否则每个可能的程序都会修改某些状态。需要思考的最小代码:https://wandbox.org/permlink/ALpFkVQycMfEZNoG。 - barsdeveloper

0

我不会使用宏。通常的技术是定义一些对象,其初始化会进行注册。陷阱:您需要在编译单元中引用某些内容,例如调用函数,以便将其链接。

祝好!


理想情况下,我希望用一行代码将所有这些类型放入容器中。我知道我可以用2-3行代码做到,但我想知道是否有什么诀窍。 - Anycorn

-4

你永远解决不了mpl::vector的想法。你不能改变模板“变量”。请记住,模板元编程是一种纯函数式语言。没有任何副作用。

至于注册...宏的方法很好用。定义宏,以便它声明并初始化某些小的全局变量与注册过程。或者你可以像我在这里做的那样:

如何强制包含库中“未使用”的对象定义

如果你试图在库中实现它,请注意修复方法。


我的想法是:首先进行注册 -> 类型1,然后追加第二个以形成类型2,以此类推... 最后将typedef typesN为types。 - Anycorn
1
mpl::for_each 在运行时会产生副作用。你可以构建一个类型的 mpl::vector,并对其进行一些有用的操作;比如一些策略链?无论如何,for_eachfold 家族都允许你构造可以具有运行时效果的结构。我最喜欢的是拥有一个 cons car/cdr 类型模板,它针对 mpl::contraint 进行测试,并调用 car 的成员或扩展并调用 cdr 的 car。 - KitsuneYMG
@ymg - 是的,但是没有办法从多个独立的位置构建一个mpl序列。你唯一可能做的事情就是在某个地方定义一个序列并使用它。所以不会有"注册"的主题。实际上,这样做比直接子类化工厂并在构造函数中硬编码类型还要低效。 - Edward Strange
@CrazyEddie 嗯,有人真的做到了。但是标准认为它应该是不合法的 - Guillaume Racicot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接