У меня есть указатель функции и параметры, и я хотел бы сохранить их и, возможно, изменить их и вызвать с их помощью функцию.
Я видел части этого ответа, однако я не уверен, как будет выглядеть полное решение, прошу прощения, но я не совсем понимаю пакеты параметров и то, как они связаны с кортежем.
Вот фрагменты моего кода, которые я пытаюсь заставить работать вместе:
У меня есть это, по сути, просто вызов функции, я думаю, что это можно использовать для сохранения вызова в «вызываемой» структуре, чтобы создать интерфейс.
из этого вопроса: Вызов универсальной функции C++ с параметром varargs
template<typename R, typename... Args>
auto call(R(*function)(Args...), Args... args) -> typename std::enable_if<!std::is_same<R, void>::value, R>::type
{
return function(args...);
}
template<typename... Args>
void call(void (*function)(Args...), Args... args)
{
function(args...);
}
Эта структура должна хранить параметры и указатель на функцию. (спасибо RaymondChen за подсказку, как должно быть правильно)
template<typename R, typename... Args>
struct callable
{
R(*function)(Args...);
std::tuple<Args...> params;
callable(Args... argv):
params(std::make_tuple(argv...))
{}
// .....
};
Я до сих пор не знаю, как вызвать функцию с помощью std::tuple
. Насколько я понимаю, мне нужно каким-то образом вернуть кортеж обратно в Args...
, а затем вызвать его.
**Чего я пытаюсь достичь:
Как вы хотите использовать callable
? Рэймонд показал, как хранить argv
в params
. Как вы собираетесь использовать вызываемую функцию?
@NathanOliver Я бы хотел просто вызвать функцию, которая возвращает тип функции, но не имеет параметров, как в callable
, например auto call()
, или, возможно, с void* в качестве типа возвращаемого значения, которая затем будет вызывать callable->function
. Я не знаю, как бы я применил параметры.
Я понял это, это буквально std::apply(function, params)
последний кусочек головоломки - создать конструктор, который принимает только указатель на функцию, чтобы я мог самостоятельно предоставить параметры. Или, возможно, мне следует разделить параметры и указатель функции и сохранять параметры только тогда, когда это действительно необходимо.
Разве std::function достаточно/проще?
@Jarod42 нет, хотя здесь все находится в небольшой структуре, в моем окончательном коде указатели и параметры fn хранятся отдельно в векторах. Это сделано для целей согласованности кэша, а также для того, чтобы я мог читать/повторно использовать/изменять параметры для оптимизации «точно в срок».
Если вы начали хранить функцию и необходимые для нее параметры в Callable
, я бы предложил просто предоставить перегрузку operator()
для вызова Callable
с ее сохраненными параметрами.
Что-то в этом роде:
// Functor to store the function and its parameters
template<typename R, typename... Args>
class Callable /* final */
{
R(*mFuncPtr)(Args...) { nullptr }; // function pointer.
std::tuple<Args...> mParams{}; // store the parameters as std::tuple
public:
explicit constexpr Callable(R(*func)(Args...))
: mFuncPtr{ func } {}
explicit constexpr Callable(R(*func)(Args...), Args&&... argv)
: mFuncPtr{ func }
, mParams{ std::make_tuple(std::forward<Args>(argv)...) } {}
// optional setter for mParams
void set(Args&&... argv)
{
mParams = std::make_tuple(std::forward<Args>(argv)...);
}
auto operator()() -> std::conditional_t<std::is_void_v<R>, void*, R>
{
if constexpr (std::is_void_v<R>)
{
// invoke the function pointer with tuple pack.
std::apply(mFuncPtr, mParams);
return nullptr; // Use nullptr for void return type
}
else
{
return std::apply(mFuncPtr, mParams);;
}
}
// Print the stored parameters
void printParams() const
{
std::cout << "(";
printParamsImpl(std::index_sequence_for<Args...>{});
std::cout << ")\n";
}
private:
template<std::size_t... I>
void printParamsImpl(std::index_sequence<I...>) const
{
(..., (std::cout << (I == 0 ? "" : ", ") << std::get<I>(mParams)));
}
};
(Посмотрите демо-версию живого примера)
Мне нравится, что вам не нужно явно указывать типы шаблонов, однако это не работает, когда функция void fn(void)
Я изменил пример, чтобы выделить его: пример Я думаю, что type_traits может помочь с этим, но я не конечно, как я мог бы это использовать, возможно, может быть специализация с нулевым параметром?
@LemonJumps В этом случае вам следует запретить создание экземпляра параметризованного конструктора, который имеет Args...
от SFINE: gcc.godbolt.org/z/sjh5r3rPY
почему вы возвращаете ноль void*
в случае void
? Вы можете return std::apply(mFuncPtr, mParams);
безоговорочно: «В функции, возвращающей (возможно, с указанием cv) void, можно использовать оператор return с выражением, если тип выражения (возможно, с указанием cv) void». en.cppreference.com/w/cpp/language/return
@Caleth Как упомянул ОП в комментарии под сообщением: «[...] например auto call()
или, возможно, с void*
в качестве возвращаемого типа, который затем будет вызывать callable->function
[...]». Я предполагаю, что тогда он/она имел в виду то, что я написал, что, кстати, тоже было принято. Почему он/она так хочет... это другой вопрос!
Опять же, я должен сказать, что меньше значит больше. Библиотечная функция std::bind уже довольно эффективно решает эту проблему:
template<typename... Args>
//requires sizeof...(Args) > 1
using Callable = decltype(std::bind(std::declval<Args>()...);
Callable c = std::bind(&foo::x,y,z,w);
auto res = c();
Чтобы еще больше усилить дизайн, добавьте std::function в свой микс:
template<typename R,typename... Args>
using Call_in = Callable<std::function<R(Args...)>, Args...>;
Call_in ci=std::bind(std::function<foo(bar)>{&bar::x}, bar{});
foo resi = ci();
Этот более динамичен; он может ввести стереть аргумент функции [объект], чтобы все функции с похожей сигнатурой вызова могли быть сохранены с использованием одной и той же специализации.
Как это позволит мне считывать определенные значения из параметров? Как это позволяет мне отделить указатель функции от параметров? Для моего варианта использования мне нужен доступ к этим данным, насколько я понимаю, std::bind не позволяет вам это сделать.
@LemonJumps, если это так, зачем беспокоиться об инкапсуляции и конструкторах? template<typename R,typename ...A> struct voidfn{ std::function<R(A...)> fn; std::tuple<A...> args; auto operator() { return std::apply(fn,args); }; };
Теперь его можно просто инициализировать как агрегат. И все в открытом доступе.
Все еще неясно, что вы пытаетесь сделать, поскольку
std::apply(argv, params)
не имеет смысла. Членparams
пока ничего не содержит.) Создается ли вызываемая поддержка для вызова функции или просто для сохранения параметров? Если экономите, тоcallable(Args...argv) : params(std::make_tuple(argv...)) {}