【问题标题】:C++14 Metaprogramming: Automagically build a list of types at compile / init timeC++14 元编程:在编译/初始化时自动构建类型列表
【发布时间】:2017-06-23 22:36:39
【问题描述】:

使用 C++14 和 Curiously Recurring Template Pattern (CRTP) 和Boost.Hana(或boost::mpl,如果你愿意)的某种组合,我可以在编译时(或静态初始化时)构建类型列表吗? ) 没有明确的声明?

举个例子,我有这样的东西(见 Coliru):

#include <iostream>
#include <boost/hana/tuple.hpp>
#include <boost/hana/for_each.hpp>

namespace
{
    struct D1 { static constexpr auto val = 10; };
    struct D2 { static constexpr auto val = 20; };
    struct D3 { static constexpr auto val = 30; };
}

int main()
{
    // How to avoid explicitly defining this?
    const auto list = boost::hana::tuple< D1, D2, D3 >{}; 

    // Do something with list
    boost::hana::for_each( list, []( auto t ) { std::cout << t.val << '\n'; } );
}

我想在创建list 时避免明确的类型列表——D1D2D3,因为这意味着我必须在看起来像我的时候手动维护该列表应该能够在类声明中或周围告诉编译器,“将此类添加到您的运行列表中”。 (我的最终目标是自动化工厂注册,这是缺少的机制。)

我可以使用一些继承和/或元编程技巧在编译时或静态初始化时组成列表吗?

【问题讨论】:

  • b.atch.se 浮现在脑海中——但委员会正试图修补允许此类技巧的漏洞,因此您可能不应该使用它。

标签: c++ c++14 template-meta-programming crtp boost-hana


【解决方案1】:

在编译时执行此操作将需要“有状态”元编程。在这篇文章here 中,Filip Roséen 解释了如何使用极其先进的 C++14 实现以下功能:

LX::push<void, void, void, void> ();
LX::set<0, class Hello> ();
LX::set<2, class World> ();
LX::pop ();

LX::value<> x; // type_list<class Hello, void, class World>

此外,Matt Calabrese 使用类似的技术在 C++11 中实现基于语义的概念,请参阅幻灯片 #28 中的 videoslides

当然,这些技术依赖于支持一致的两阶段名称查找的编译器。

或者,您可以重新构建代码以支持运行时注册,这更加简单,并且可以在 MSVC 等编译器之间移植。这就是 Proveargs 等库使用的。它使用通用的auto_register 类:

template<class T, class F>
int auto_register_factory()
{
    F::template apply<T>();
    return 0;
}

template<class T, class F>
struct auto_register
{
    static int static_register_;
    // This typedef ensures that the static member will be instantiated if
    // the class itself is instantiated
    typedef std::integral_constant<decltype(&static_register_), &static_register_> static_register_type_;
};

template<class T, class F>
int auto_register<T, F>::static_register_ = auto_register_factory<T, F>();

然后你就可以编写自己的 CRTP 类了:

struct foo_register
{
    template<class T>
    static void apply()
    {
        // Do code when it encounters `T`
    }
};

template<class Derived>
struct fooable : auto_register<Derived, foo_register>
{};

【讨论】:

  • 这里是this technique in action的完整演示。由于static_register_ 正在被优化掉,所以我无法让这个自动注册按原样工作,所以对此进行了轻微修改。它适用于 Clang 和 GCC。
  • @metal 问题是模板类可能永远不会被实例化。所以添加默认构造函数或引用基类中的函数(即this-&gt;register_)是另一种强制方法。
  • @metal 所以我发现通过将它放在模板参数中,编译器将始终实例化静态变量。我已经更新了代码。
  • @PaulFultzII 感谢您使用 typedef 找到这个技巧!我花了一天多的时间尝试做一些与工作非常相似的事情,这是让一切最终正常工作的关键。
  • @PaulFultzII 你为什么使用integral_constant。
【解决方案2】:

听起来你想在一个命名空间或其他范围内获得一个所有类型的编译时元组。为此,您需要静态反射,它尚未添加到 C++ 中(但正如您所发现的那样非常有用)。您可以阅读one proposal for static reflection hereN4428 proposal here

作为一种解决方法,您可以编写一个宏来同时定义类型并在静态初始化期间将其隐式添加到注册表中。

【讨论】:

  • 编译器不需要自己自动发现列表。我愿意通过使用一些触发器标记每个触发器(例如,从策略类或某些 CRTP 继承)来帮助它。我要避免的事情是手动维护自动注册的类列表。
  • 我可以做类似this with Boost.Preprocessor 的事情。唉,我希望有一些更优雅的东西。
  • 是的,您可以从将您的类添加到静态列表的 CRTP 类继承,但您很快就会遇到初始化顺序问题和实例化深度问题。
【解决方案3】:

我现在知道的唯一方法是有状态元编程,如here 所述。但这很棘手,难以实施,委员会正试图将其排除为无效。

【讨论】:

  • Here's a stab at what I was trying to do with CRTP,但它不适用于更新的编译器。我希望更多的魔法和 C++ 奥术能够弥合差距。
  • CRTP 将允许您在静态初始化时注册类型,而不是在编译时。这对你来说可能还不够。
  • 好电话:我没有区分静态初始化和编译时间,但我应该有。在我的情况下,静态初始化时间应该足够了,但我仍然无法找到精确的机制。我澄清了我的问题中的措辞。
猜你喜欢
  • 2012-05-30
  • 1970-01-01
  • 1970-01-01
  • 2021-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多