在模板推导中保留函数指针参数的完整类型

时间:2014-06-02 02:54:29

标签: c++ arrays templates c++11

我正试图在传递给模板函数时保留函数指针参数的完整类型。

这是我想做的一个例子:

#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类型中(该部分已经可以工作,假设我可以防止数组衰减成指针)。

希望这有助于更好地解释我想要做的事情。

1 个答案:

答案 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}}

Live demo