我正试图在传递给模板函数时保留函数指针参数的完整类型。
这是我想做的一个例子:
#include <stdio.h>
#include <utility>
template<typename _Ret, typename... Args>
static _Ret call(_Ret (*fp)(Args&&...), Args &&... args)
{
return fp(std::forward<Args>(args)...);
}
int foo(int arr[4])
{
printf("arr: %i,%i,%i,%i\n", arr[0], arr[1], arr[2], arr[3]);
return 0;
}
int main(int, char**)
{
int arr[4] = { 1, 2, 3, 4 };
int (*foo_ptr)(int arr[4]) = &foo;
call<int>(foo_ptr, arr);
return 0;
}
不幸的是,调用的类型实际上将foo_ptr视为int (*)(int*)
而不是int (*)(int [4])
或int (*)(int (&)[4])
。
rvalue引用的原因是它是保持数组类型的唯一方法,但它似乎不适用于函数指针参数。
至于为什么我要这样做,我有一个几乎神奇的lua绑定库,它是100%基于模板的,并且不需要代码链接到。为了使它能够支持绑定到属性和数组本身的变量,需要rvalue引用。 int*
和int (&)[4]
不兼容。
我们的想法是允许尽可能透明和自动地绑定现有的apis,甚至是C apis,同时尽可能多地在编译时进行类型检查。
这有可能吗?
编辑:
好的,我的例子可能有点过于简化了。我实际上无法更改传递给“call”的函数的定义,因为这个库旨在允许绑定到用户可能想要的任何随机函数。
这里的例子是我的静态函数(非方法)绑定方法:
template<typename _Ret, typename... _Args>
LuaGlue &func(const std::string &name, _Ret (*fn)(_Args...))
{
auto new_func = new LuaGlueFunction<_Ret, _Args...>(this, name, fn);
functions.addSymbol(name.c_str(), new_func);
return *this;
}
LuaGlueFunction看起来像这样:
template<typename _Ret, typename... _Args>
class LuaGlueFunction : public LuaGlueFunctionBase
{
public:
typedef _Ret ReturnType;
typedef _Ret (*MethodType)( _Args... );
LuaGlueFunction(LuaGlueBase *lg, const std::string &n, MethodType fn) :
g(lg), name_(n), fn_(std::forward<decltype(fn)>(fn))
{ }
~LuaGlueFunction() {}
std::string name() { return name_; }
bool glue(LuaGlueBase *luaGlue)
{
lua_pushlightuserdata(luaGlue->state(), this);
lua_pushcclosure(luaGlue->state(), &lua_call_func, 1);
//printf("add function: %s\n", name_.c_str());
lua_setglobal(luaGlue->state(), name_.c_str());
return true;
}
int invoke(lua_State *state)
{
ReturnType ret = applyTuple(g, state, fn_, args);
lua_pop(state, Arg_Count_);
stack<_Ret>::put(g, state, ret);
return 1;
}
private:
LuaGlueBase *g;
std::string name_;
MethodType fn_;
std::tuple<_Args...> args;
static const unsigned int Arg_Count_ = sizeof...(_Args);
static int lua_call_func(lua_State *state)
{
auto mimp = (LuaGlueFunction<_Ret, _Args...> *)lua_touserdata(state, lua_upvalueindex(1));
return mimp->invoke(state);
}
};
我试图允许检测一个数组,然后在内部将它自动放在LuaGlueStaticArray类型中(该部分已经可以工作,假设我可以防止数组衰减成指针)。
希望这有助于更好地解释我想要做的事情。
答案 0 :(得分:4)
call
功能模板存在问题。它需要定义为
template<typename _Ret, typename... Args>
static _Ret call(_Ret (*fp)(Args...), Args &&... args)
// ^^^ no &&
您想要传递的参数完美转发,但您不希望强制函数参数的类型引用类型。
接下来,当您将数组传递给函数时,将衰减指向第一个元素的地址的指针。所以你的函数foo
实际上等同于
int foo(int *arr) // size information is lost
记住这一点,当您将arr
传递给int (&)[4]
时,您不希望call
推断为call<int>(foo_ptr, &arr[0]); // pass a pointer to the beginning
,因此您必须传递指针。
foo
进行这些更改后,您的代码就可以运行了。 Live demo
如果要在将数组传递给int foo(int (&arr)[4])
{ ... }
时保留数组的大小,请将该函数更改为
decltype(foo)* foo_ptr = &foo;
call<int>(foo_ptr, arr);
然后将其称为
call
正如mpark在注释中指出的那样,在上面的两个示例中,没有必要明确提及foo_ptr
的返回类型,或者形成函数指针call(foo, &arr[0]); // with int foo(int *arr)
。电话可以简单地是
call(foo, arr); // with int foo(int (&arr)[4])
或
{{1}}