Скажем, у меня есть структура как контекст данных, определенный следующим образом:
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?
являются ли функции вариативными? Или ...
просто заполнитель в вашем примере, чтобы проиллюстрировать, что параметров больше?
Можно ли заменить/преобразовать структуру Ctx в пару? std::apply
может быть решением, если это так.
Первый вопрос: нет, я не могу изменить TestFunc, кроме как обернув его, это библиотека C. Я просто хочу избежать ручной упаковки, потому что есть сотни функций, которые принимают эту форму. Второй вопрос: Да, это также вариативно, поэтому я пересылаю все конечные параметры.
Игнасио, я не слишком внимательно изучал кортежи, это может быть вариантом. Я проверю это, спасибо!
Я не совсем уверен, чего вы хотите достичь.
Но, возможно, вариативное 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);
}
Конечно, возможны и другие реализации. Пожалуйста, прокомментируйте, если вам нужно что-то еще.
Чего я хочу добиться, так это того, что у меня есть контекст, который содержит кэшированные дескрипторы разных типов. Существуют сотни функций, которые могут работать с различными элементами. Я хочу обернуть их все, поэтому я просто передаю контекст, и он перенаправляет соответствующие члены (по типу) в функцию, чтобы упростить использование библиотеки. Таким образом, мне не нужно писать сотни оболочек функций, выбирая подходящие члены для передачи вручную. Предложения, которые я видел до сих пор, похоже, требуют передачи ВСЕХ членов, я хочу передать только те, которые соответствуют сигнатуре типа функции.
Другими словами, если функция принимает члены типа X и типа Z, но не типа Y, я хочу передать X и Z. Я не уверен, что это возможно, поэтому и спросил. Должен уточнить: я знаю, что мне придется выполнить некоторую работу с оболочкой, но если бы я мог преобразовать ее в макрос/шаблон, чтобы для каждой функции был только один вкладыш, тогда я был бы доволен этим.
Как я понимаю, вам нужно 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)...);
}
Кажется, это именно то, что я ищу. Спасибо!
Не можешь изменить
TestFunc
? Я думаю, что вы пытаетесь решить проблему не с той стороны. Если функция имеет много параметров, вам нужно исправить эту функцию.