如何将参数的::std::向量绑定到函子
How do I bind a ::std::vector of arguments to a functor?
我正在努力使这个程序正确编译:
#include <vector>
#include <iostream>
int f(int a, int b)
{
::std::cout << "f(" << a << ", " << b << ") == " << (a + b) << 'n';
return a + b;
}
template <typename R, typename V>
R bind_vec(R (*f)(), const V &vec, int idx=0)
{
return f();
}
template <typename R, typename V, typename Arg1, typename... ArgT>
R bind_vec(R (*f)(Arg1, ArgT...), const V &vec, int idx=0)
{
const Arg1 &arg = vec[idx];
auto call = [arg, f](ArgT... args) -> R {
return (*f)(arg, args...);
};
return bind_vec(call, vec, idx+1);
}
int foo()
{
::std::vector<int> x = {1, 2};
return bind_vec(f, x);
}
理想情况下,我希望bind_vec
将任意函子作为参数,而不仅仅是函数指针。其思想是在编译时从::std::vector
中提取函数参数。
这不是它的最终用途,但它是我想去的地方的垫脚石。我真正要做的是生成包装函数,在编译时从future/promise类型系统中的promise中打开它们的参数。这些包装器函数本身就是承诺。
在我的最终用例中,我可以指望函子是::std::function
s。但如果能知道它应该如何适用于更通用的函子,那就太好了,因为我认为这是一个广泛有趣的问题。
好的,首先,可以检测函子的arity,但这有点复杂,最好留给单独的问题。假设您将在调用中指定函子的arity。类似地,有一些方法可以获得可调用对象的返回类型,但这也超出了这个问题的范围。现在我们假设返回类型为void
。
所以我们想说,
call(F f, C v);
也就是说f(v[0], v[1], ..., v[n-1])
,其中f
具有arityn
。
这里有一种方法:
template <unsigned int N, typename Functor, typename Container>
void call(Functor const & f, Container const & c)
{
call_helper<N == 0, Functor, Container, N>::engage(f, c);
}
我们需要帮助:
#include <functional>
#include <cassert>
template <bool Done, typename Functor, typename Container,
unsigned int N, unsigned int ...I>
struct call_helper
{
static void engage(Functor const & f, Container const & c)
{
call_helper<sizeof...(I) + 1 == N, Functor, Container,
N, I..., sizeof...(I)>::engage(f, c);
}
};
template <typename Functor, typename Container,
unsigned int N, unsigned int ...I>
struct call_helper<true, Functor, Container, N, I...>
{
static void engage(Functor const & f, Container const & c)
{
assert(c.size() >= N);
f(c[I]...);
}
};
示例:
#include <vector>
#include <iostream>
void f(int a, int b) { std::cout << "You said: " << a << ", " << b << "n"; }
struct Func
{
void operator()(int a, int b) const
{ std::cout << "Functor: " << a << "::" << b << "n"; }
};
int main()
{
std::vector<int> v { 20, 30 };
call<2>(f, v);
call<2>(Func(), v);
}
注意:在更高级的版本中,我会用一些更多的模板机制来推导可调用对象的arity,我还会推导返回类型。然而,要想做到这一点,你需要对免费函数和各种CV限定的类成员函数进行几个专业化,所以这对这个问题来说太大了。
对于(成员)函数指针来说,这样的事情很容易实现,但对于可能过载operator()
的函数来说,这会变得困难得多。如果我们假设你有一种方法来判断一个函数需要多少个参数(并假设容器实际上有那么多元素),你可以使用索引技巧将向量扩展到一个参数列表中,例如使用std::next
和begin()
迭代器:
#include <utility>
#include <iterator>
template<class F, class Args, unsigned... Is>
auto invoke(F&& f, Args& cont, seq<Is...>)
-> decltype(std::forward<F>(f)(*std::next(cont.begin(), Is)...))
{
return std::forward<F>(f)(*std::next(cont.begin(), Is)...);
}
template<unsigned ArgC, class F, class Args>
auto invoke(F&& f, Args& cont)
-> decltype(invoke(std::forward<F>(f), cont, gen_seq<ArgC>{}))
{
return invoke(std::forward<F>(f), cont, gen_seq<ArgC>{});
}
这种实现对于随机访问容器来说非常好,但对于前向容器,尤其是输入容器来说就不那么好了。为了使这些功能以性能化的方式工作,您可能会尝试在每一个扩展步骤中递增迭代器,但您会遇到一个问题:函数参数的求值顺序未指定,因此您很可能会以错误的顺序传递参数。
幸运的是,有一种方法可以强制从左到右进行求值:列表初始化语法。现在,我们只需要一个可以用来传递参数的上下文,一种可能的方法是构造一个对象,通过构造函数传递函数和参数,并调用其中的函数。但是,您将失去检索返回值的能力,因为构造函数不能返回值。
我想到的是创建一个指向正确元素的迭代器数组,并在第二步中再次扩展它们,在那里它们被取消引用。
#include <utility>
template<class T> using Alias = T; // for temporary arrays
template<class F, class It, unsigned N, unsigned... Is>
auto invoke_2(F&& f, It (&&args)[N], seq<Is...>)
-> decltype(std::forward<F>(f)(*args[Is]...))
{
return std::forward<F>(f)(*args[Is]...);
}
template<class F, class Args, unsigned... Is>
auto invoke_1(F&& f, Args& cont, seq<Is...> s)
-> decltype(invoke_2(std::forward<F>(f), std::declval<decltype(cont.begin())[sizeof...(Is)]>(), s))
{
auto it = cont.begin();
return invoke_2(std::forward<F>(f), Alias<decltype(it)[]>{(void(Is), ++it)...}, s);
}
template<unsigned ArgC, class F, class Args>
auto invoke(F&& f, Args& cont)
-> decltype(invoke_1(std::forward<F>(f), cont, gen_seq<ArgC>{}))
{
return invoke_1(std::forward<F>(f), cont, gen_seq<ArgC>{});
}
该代码已根据GCC 4.7.2进行了测试,并按广告中的方式工作。
既然你说你要传递的函子是std::function
s,那么获取它们的参数数量真的很容易:
template<class F> struct function_arity;
// if you have the 'Signature' of a 'std::function' handy
template<class R, class... Args>
struct function_arity<R(Args...)>
: std::integral_constant<std::size_t, sizeof...(Args)>{};
// if you only have the 'std::function' available
template<class R, class... Args>
struct function_arity<std::function<R(Args...)>>
: function_arity<R(Args...)>{};
请注意,您甚至不需要function_arity
就可以使上面的invoke
适用于std::function
:
template<class R, class... Ts, class Args>
R invoke(std::function<R(Ts...)> const& f, Args& cont){
return invoke_1(f, cont, gen_seq<sizeof...(Ts)>{})
}
我设法做到了你想要的。最简单的解释是,如果我一开始没有推导出正确的返回类型,我稍后会展示如何添加它:
#include <vector>
#include <type_traits>
namespace {
int f(int a, int b) { return 0; }
}
template <typename ...Args>
constexpr unsigned nb_args(int (*)(Args...)) {
return sizeof...(Args);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V&, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) == nb_args(F()),void>::type
{
f(std::forward<Args>(args)...);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V& v, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) < nb_args(F()),void>::type
{
bind_vec(f, v, std::forward<Args>(args)..., v.at(sizeof...(Args)));
}
int main() {
bind_vec(&f, std::vector<int>(), 1);
return 0;
}
此bind_vec
有两个版本-如果参数包大小适合该函数,则启用一个版本。如果它仍然太小,则启用另一个。第一个版本只是使用参数包调度调用,而第二个版本则获取下一个元素(由参数包的大小决定)并递归。
SFINAE是在函数的返回类型上进行的,这样它就不会干扰类型的推导,但这意味着它需要在函数之后进行,因为它需要知道F
。有一个helper函数,它可以查找调用函数指针所需的参数数量。
为了推导返回类型,我们还可以使用decltype
和函数指针:
#include <vector>
#include <type_traits>
namespace {
int f(int a, int b) { return 0; }
}
template <typename ...Args>
constexpr unsigned nb_args(int (*)(Args...)) {
return sizeof...(Args);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V&, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) == nb_args(F()),decltype(f(std::forward<Args>(args)...))>::type
{
return f(std::forward<Args>(args)...);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V& v, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) < nb_args(F()),decltype(bind_vec(f, v, std::forward<Args>(args)..., v.at(sizeof...(Args))))>::type
{
return bind_vec(f, v, std::forward<Args>(args)..., v.at(sizeof...(Args)));
}
int main() {
bind_vec(&f, std::vector<int>(), 1);
return 0;
}
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 在基于范围的for循环中使用结构化绑定声明
- 使用 LuaBridge 将 LuaJIT 绑定到C++会导致"PANIC: unprotected error"
- 尝试通过OCI例程从Oracle获取blob数据,但出现错误:ORA-01008:并非所有变量都绑定
- 在使用GPU支持编译Tensorflow时,会遇到CUDA_TOOLKIT_PATH未绑定变量
- 视觉studo 2019中的漫画和静态/动态绑定
- 将自由函数绑定为类成员函数
- 将常量指针引用绑定到非常量指针
- 在派生类中绑定非静态模板化成员函数
- 绑定派生类方法C++从实例范围之外的分隔 std::function 变量调用
- 在 openGL 中多次绑定缓冲区
- 定义有趣的宏和正则表达式在Z3 C++绑定
- 创建一个函数的 Python 绑定,返回指向带有 boost 的向量的指针
- 结构化绑定,无需复制即可获取子向量的连续元素
- 值未正确绑定到向量中
- 如何将参数的::std::向量绑定到函子
- Boost绑定和Boost函数,将带有参数的函数存储在向量中,然后执行它们
- 类的Boost Python绑定向量
- 提升与向量成员函数的绑定
- c++正则化向量;错误:无法绑定