Передавайте вложенные типы в родительский класс, не повторяясь (DRY) в C++20

Как показано в приведенном ниже примере, мне нужно, чтобы моя структура имела доступ ко всем структурам «полей», определенным в дочерней структуре. Но у меня есть некоторые ограничения:

  • Поля должны быть объявлены как вложенные типы наследующей структуры.
  • Я не могу использовать какую-либо библиотеку, такую ​​​​как BOOST, для решения этой проблемы.
  • Теоретически может быть бесконечное количество полей
  • Каждое поле должно быть объявлено как отдельная структура, чтобы на него можно было ссылаться позже и не смешивать, например, с другим логическим полем.

Параметры шаблона Componentи структуры могут быть изменены как угодно, если 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++ не допускает объявления типов внутри параметров шаблона.
Я также уже пытался использовать и изменить этот ответ для создания своего кода, но проблема здесь в том, что макрос должен учитывать 2 параметра вместо одного ( один для имени поля и один для типа ), что делает это довольно сложно, поскольку для достижения того, что мне нужно, требуется немало логики.

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));
};

Демо

Другие вопросы по тегам