问题
I am trying to create a interface between user defined function and data. Let's say I need to create a function called MapFun()
, input of MapFun()
includes user defined function (UDF) handle and UDF inputs.
void userFun1(Data data, int in1, int in2){
// user defined function 1;
}
void userFun2(Data data, int in1, int in2, std::string s){
// user defined function 2;
}
// ...
// apply user function 1 on data
MapFun(@userFun1, data, 3, 4);
// apply user function 2 on data
MapFun(@userFun2, data, 1, 2, "algorithm");
User will write userFun
and apply it with MapFun()
. So, how to design MapFun()
? User function may have different inputs and the signature can't be predicted. In addition, MapFun()
won't evaluate userFun
immediately, instead, it stores all userFun
and do a lazy evaluation.
Any suggestions are greatly appreciated.
回答1:
User function may have different inputs and the signature can't be predicted.
It seems a typical works for variadic templates
In addition,
MapFun()
won't evaluateuserFun
immediately, instead, it stores alluserFun
and do a lazy evaluation.
Not sure to understand but I suppose you can obtain what do you want using std::bind()
or, maybe better, with lambda functions.
I propose the following C++14 variadic template MapFun()
function that return a lambda function that capture both user-function and argument. That function can be executed later.
template <typename F, typename ... Args>
auto MapFun (F const & f, Args const & ... args)
{ return [=]{ f(args...); }; }
The following is a full working example
#include <iostream>
template <typename F, typename ... Args>
auto MapFun (F const & f, Args const & ... args)
{ return [=]{ f(args...); }; }
void userFun1 (int i1, int i2)
{ std::cout << "uf1, " << i1 << ", " << i2 << std::endl; }
void userFun2 (int i1, int i2, std::string const & s)
{ std::cout << "uf2, " << i1 << ", " << i2 << ", " << s << std::endl; }
int main ()
{
auto l1 = MapFun(userFun1, 1, 2);
auto l2 = MapFun(userFun2, 3, 4, "five");
l2();
l1();
}
回答2:
If I understand right, you just want to store a function and a set of arguments so that they can be called later. This is exactly what std::bind does (anyway, the simplest thing it does). In fact, you could possibly just use std::bind
directly in place of your MapFun
:
auto func1 = std::bind(userFun1, data, 3, 4);
auto func2 = std::bind(userFun2, data, 1, 2, "algorithm");
// ... and later,
func1();
func2();
Note the auto
in those: you can't (portably) write the type of any object returned by std::bind
, and those two example objects will have different types. But both of those would implicitly convert to the common type std::function<void()>
. You can store a bunch of std::function<void()>
objects in nearly any container type you want, to be used later:
std::map<unsigned int, std::function<void()>> func_map;
func_map[0] = std::bind(userFun1, data, 3, 4);
func_map[1] = std::bind(userFun2, data, 1, 2, "algorithm");
// ...
auto func_iter = func_map.find(key);
if (func_iter != func_map.end() && func_iter->second) {
(func_iter->second)();
}
Of course, if you want to use your name of the function, and/or if you need the explicit common return type to work well with other templates or something, you can just wrap std::bind
:
template <class F, class... Args>
std::function<void()> MapFun(F&& f, Args&&... args) {
return std::bind<void>(std::forward<F>(f), std::forward<Args>(args)...);
}
Or if your MapFun
is supposed to store the callable object inside some class member container or something, not just create and return one (it's not clear), it can of course do that by using std::bind
and then adding the result to whatever container you need.
回答3:
If you only need to backport std::invoke
from C++17, well, its simplest form is quite straightforward
:
template<class F, class... Args> decltype(auto) invoke(F &&f, Args &&...args) {
return ::std::forward<F>(f)(::std::forward<Args>(args)...);
}
来源:https://stackoverflow.com/questions/53272335/c-need-a-container-to-store-user-defined-function