【问题标题】:Pass nested types to a parent class without repeating yourself (DRY) in C++20将嵌套类型传递给父类而不在 C++20 中重复自己(DRY)
【发布时间】:2021-03-02 13:08:38
【问题描述】:

如下例所示,我需要我的Component 结构能够访问子结构中定义的所有“字段”结构。但我有一些限制:

  • 字段必须声明为继承结构的嵌套类型。
  • 我无法使用任何像 BOOST 这样的库来解决这个问题
  • 理论上可以有无限多个字段
  • 每个字段都需要声明为独立结构,以便以后可以引用它并避免与其他布尔字段混淆

ComponentField 结构的模板参数可以随意修改,只要Field 有其TType 参数即可。重要提示,我使用的是 C++20。

// * CRTP stands for Curiously recurring template pattern
template <typename TCrtp>
struct Component
{
    template <typename TType>
    struct Field
    {
        using Type      = TType;
        using Component = TCrtp;
    };

    using ComponentType = TCrtp;

    // Because TCrtp is the inheriting class, the 'TCrtp::Fields' aka 'TestComponent::Fields' type
    // can be accessed from here to do anything I need to
};

struct TestComponent : Component<TestComponent>
{
    struct Field1: Field<bool>  {};
    struct Field2: Field<float> {};

    // Problem: Can we find a way to fill this tuple automatically
    // either from this class or the parent one without using header tool
    // or even macros if this is possible

    // The goal here is to avoid the programer that is creating this class to repeat itself
    // by having to fill manually this tuple and thus potentially forgetting a field, that would cause him
    // some troubles (bugs) later on...
    using Fields = std::tuple<Field1, Field2>;
};

不幸的是,C++ 不允许在模板参数中声明类型。
我也已经尝试使用和修改此answer 来生成我的代码,但这里的问题是宏需要考虑 2 个参数而不是一个(一个用于字段名称,一个用于类型),这使得它非常棘手,因为需要相当多的逻辑才能实现我所需要的。

【问题讨论】:

  • 你对MACRO((Field1, bool), (Field2, float))MACRO((Field1, Field2), (bool, float))还好吗?
  • 这是一个无法解决的问题。在定义子类之前必须定义超类是 C++ 的基础。因此,您不能在超类之前在 CRTP 中定义子类,在 CRTP 声明中定义父类。这就是 C++ 的工作原理。你必须找到其他方法来设计你的类结构。
  • @Jarod42 是的!只要我不必重复自己并且尊重我的约束,这完全没问题。不过,您的 marco 的第一个版本看起来比第二个更好。

标签: c++ templates nested c++20 variadic


【解决方案1】:

使用 MACRO,直到硬编码限制,您可能会执行以下操作:

#define COUNT_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...)    N
#define COUNT(...)   COUNT_N(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

#define CONCAT(A, B) A ## B

#define FIELDS_VAR(N) CONCAT(FIELDS_, N)
#define USING_VAR(N) CONCAT(USING_, N)

#define TAKE_FIRST(_1, ...) _1

#define FIELD(name, type) struct name : Field<type>{};

#define FIELDS_1(_1) FIELD _1
#define FIELDS_2(_1, _2) FIELD _1 FIELD _2
#define FIELDS_3(_1, _2, _3) FIELD _1 FIELD _2 FIELD _3

#define USING_1(_1) using Fields = std::tuple<TAKE_FIRST _1>
#define USING_2(_1, _2) using Fields = std::tuple<TAKE_FIRST _1, TAKE_FIRST _2>
#define USING_3(_1, _2, _3) using Fields = std::tuple<TAKE_FIRST _1, TAKE_FIRST _2, TAKE_FIRST _3>

#define FIELDS(...) FIELDS_VAR(COUNT(__VA_ARGS__)) (__VA_ARGS__) \
                    USING_VAR(COUNT(__VA_ARGS__)) (__VA_ARGS__) 

struct TestComponent : Component<TestComponent>
{
    FIELDS((Field1, bool), (Field2, float));
};

Demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-10
    • 2014-10-04
    • 2013-03-07
    • 1970-01-01
    • 2022-11-23
    • 2012-12-26
    • 2021-09-26
    相关资源
    最近更新 更多