Можно ли автоматически сопоставлять и перенаправлять типы в контексте структуры на параметры функции с помощью шаблонов C++?

Скажем, у меня есть структура как контекст данных, определенный следующим образом:

      struct Ctx {
   TypeA a;
   TypeB b;
   TypeC c;
   TypeD d;
   TypeE e;
};

auto TestFunc(TypeA a, TypeB b, TypeC c, args...) -> result;

и вызов будет иметь форму:

      TestFunc(ctx.a, ctx.b, ctx.c, args...);

Поскольку ctx избыточен, мне нужна новая оболочка:

      auto TestFunc(Ctx& ctx, args...) -> result {
    return TestFunc(ctx.a, ctx.b, ctx.c, args...);
}

Есть ли способ сопоставить сигнатуру типа функции с сигнатурой типа структуры, чтобы я мог создать какую-то комбинацию макросов и универсальных шаблонов, которая будет работать без необходимости вручную писать оболочку для каждой функции, которая обращается к членам в этом Ctx?

1 ответ

вступление

Это старый вопрос, но с тех пор, как он был задан впервые, ситуация в C++ улучшилась (немного) , и свежий взгляд может принести пользу.

Фон

Я имею в виду библиотеку и доклад «Уничтожение накладных расходов и сложности сериализации C++ — Эяль Зедака — CppCon 2022», который ее автор выступил на CppCon 2022.

Эта библиотека использует структурированные привязки (c++17) для автоматической распаковки членов классов и использует магию шаблонов для точного определения количества членов класса. Этот процесс не очень приятен, поскольку структурированные привязки еще не поддерживают вариативный синтаксис, поэтому он основан на старом приеме ручной попытки выбора одного члена, затем 2, затем 3 и т. д. Это ограничивает эту библиотеку 50 классами-членами для момент. Посмотрите эту строку , чтобы понять, что я имею в виду.

Существуют и другие ограничения, например, при работе с частными членами, но для них я отсылаю вас к документации библиотеки, поскольку в этом конкретном случае у нас есть простая структура со всеми открытыми членами.

Поэтому я думаю, что для этой проблемы вы могли бы использовать тот же механизм, который используется для распаковки класса, а затем внедрить его в список аргументов функции шаблона с переменным числом вариантов...

Из того, что я видел до сих пор, вы можете использоватьvisit_members()функция, которая делает именно то, что вам нужно, как это делает этот код в библиотеке.

То же самое решение можно применить и к другому вопросу SO . Точно так же здесь можно использовать библиотеку AggregatesToTuples, использованную в этом вопросе, но я предпочитаю этот подход.

Рабочий пример с

      #include <iostream>
#include <https://raw.githubusercontent.com/eyalz800/zpp_bits/main/zpp_bits.h>

// Example types that can be passed in
struct Ctx1 {
    int i = 1;
};

struct Ctx2 {
    std::string s = "string";
    double d = 6.28;
};

// The test function OP requested to be called
template <typename... CtxItems>
auto TestFunc(CtxItems&&... ctx_items) -> void {
    std::cout << "TestFunc called with " << sizeof...(ctx_items) << " args:" << std::endl;
    ((std::cout << " - arg: " << ctx_items << std::endl), ...); // print the args
}

int main() {
    Ctx1 ctx1;
    zpp::bits::access::visit_members(ctx1, [](auto &&... items) constexpr {
            return TestFunc(items...);
        });
    
    Ctx2 ctx2;
    zpp::bits::access::visit_members(ctx2, [](auto &&... items) constexpr {
            return TestFunc(items...);
        });

    return 0;
}

Богболт Демо

Выход:

      Program returned: 0
TestFunc called with 1 args:
 - arg: 1
TestFunc called with 2 args:
 - arg: string
 - arg: 6.28

Будущее

Если на бумаге появится документ «Структурированные привязки могут ввести пакет (P1061R2)» , этот код станет намного проще, и мы, вероятно, сможем реализовать что-то напрямую, а не полагаться на внутренние компоненты.

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