将嵌套类型传递给父级 class 而无需在 C++20 中重复自己 (DRY)
Pass nested types to a parent class without repeating yourself (DRY) in C++20
如下例所示,我需要我的 Component
结构能够访问子结构中定义的所有“字段”结构。但我有一些限制:
- 字段必须声明为继承结构的嵌套类型。
- 我不能使用像 BOOST 这样的库来解决这个问题
- 理论上可以有无限多个字段
- 每个字段都需要声明为一个独立的结构,以便稍后引用它并避免与另一个布尔字段混淆,例如
Component
和 Field
结构的模板参数可以随意修改,只要 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,达到硬编码限制,您可以执行如下操作:
#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));
};
如下例所示,我需要我的 Component
结构能够访问子结构中定义的所有“字段”结构。但我有一些限制:
- 字段必须声明为继承结构的嵌套类型。
- 我不能使用像 BOOST 这样的库来解决这个问题
- 理论上可以有无限多个字段
- 每个字段都需要声明为一个独立的结构,以便稍后引用它并避免与另一个布尔字段混淆,例如
Component
和 Field
结构的模板参数可以随意修改,只要 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,达到硬编码限制,您可以执行如下操作:
#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));
};