Можно ли автоматически сопоставлять и перенаправлять типы в контексте структуры на параметры функции с помощью шаблонов 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?

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

463035818_is_not_a_number 15.02.2023 11:04

являются ли функции вариативными? Или ... просто заполнитель в вашем примере, чтобы проиллюстрировать, что параметров больше?

463035818_is_not_a_number 15.02.2023 11:08

Можно ли заменить/преобразовать структуру Ctx в пару? std::apply может быть решением, если это так.

Ignacio Gaviglio 15.02.2023 12:12

Первый вопрос: нет, я не могу изменить TestFunc, кроме как обернув его, это библиотека C. Я просто хочу избежать ручной упаковки, потому что есть сотни функций, которые принимают эту форму. Второй вопрос: Да, это также вариативно, поэтому я пересылаю все конечные параметры.

JimmyStack 15.02.2023 12:12

Игнасио, я не слишком внимательно изучал кортежи, это может быть вариантом. Я проверю это, спасибо!

JimmyStack 15.02.2023 12:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
54
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не совсем уверен, чего вы хотите достичь.

Но, возможно, вариативное struct поможет. Вы можете получить элементы структуры из значений инициализации. Конечно, возможны и другие вычеты.

Мы можем построить вариативную структуру на std::tuple. И мы можем перегрузить оператор вызова функции и использовать std::apply для вызова функции с элементами вариативного struct.

См., например, следующее решение:

#include <iostream>
#include <functional>
#include <tuple>

// Variadic struct
template <typename...Args>
struct Ctx {
    // Variadic data
    std::tuple<Args...> data{};  
    // Variadic Constructor. Will use CTAD
    Ctx(Args...args) : data(std::forward<decltype(args)>(args)...) {};
    // Calling the function with the data of the struct
    void operator()(std::function<void(Args...)> f) {
        std::apply(f,data);
    }
};

// Your library function
void print(int i, char c, double d) {
    std::cout << i << ' ' << c << ' ' << d << '\n';
}
// Test code
int main() {

    Ctx v(1, 'a', 1.1);
    
    v (print);
}

Конечно, возможны и другие реализации. Пожалуйста, прокомментируйте, если вам нужно что-то еще.

Чего я хочу добиться, так это того, что у меня есть контекст, который содержит кэшированные дескрипторы разных типов. Существуют сотни функций, которые могут работать с различными элементами. Я хочу обернуть их все, поэтому я просто передаю контекст, и он перенаправляет соответствующие члены (по типу) в функцию, чтобы упростить использование библиотеки. Таким образом, мне не нужно писать сотни оболочек функций, выбирая подходящие члены для передачи вручную. Предложения, которые я видел до сих пор, похоже, требуют передачи ВСЕХ членов, я хочу передать только те, которые соответствуют сигнатуре типа функции.

JimmyStack 15.02.2023 15:21

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

JimmyStack 15.02.2023 15:28
Ответ принят как подходящий

Как я понимаю, вам нужно 2 вещи:

  • одна (или две с const) функция для преобразования вашей структуры в кортеж:
struct Ctx {
   auto as_tuple() const { return std::tie(a, b, c, d, e); }
   auto as_tuple() { return std::tie(a, b, c, d, e); }

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

тогда, предполагая, что типы различны, функция (или 2 для поддержки C-многоточия) для фильтрации из кортежа только нужного аргумента:

template <typename Tuple, typename Ret, typename... Ts, typename... Args>
Ret call(Ret(*func)(Ts...), Tuple&& tuple, Args&&... args)
{
    return func(std::get<Ts&>(tuple)..., std::forward<Args>(args)...);
}

template <typename Tuple, typename Ret, typename... Ts, typename... Args>
Ret call(Ret(*func)(Ts..., ...), Tuple&& tuple, Args&&... args)
{
    return func(std::get<Ts&>(tuple)..., std::forward<Args>(args)...);
}

Демо

Кажется, это именно то, что я ищу. Спасибо!

JimmyStack 15.02.2023 21:03

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