创建具有特定签名的c++静态包装器函数

Creating a C++ static wrapper function with specific signature

本文关键字:静态 c++ 包装 函数 创建      更新时间:2023-10-16

我在使用模板参数创建静态包装器函数时遇到了一些麻烦。我不想将函数直接传递给包装器函数,因为它需要一个特定的签名int (lua_State *),以便它可以传递到以下函数:

lua_pushcfunction(L, function);

(没错,我要使用自动生成的lua包装器。)

我的第一个想法是创建一个模板函数,用函数指针作为非类型模板参数。

template <void(* f)(void)>
int luaCaller(lua_State * _luaState)
{
    f();
    return 0;
}

到目前为止,这看起来很好。此函数具有正确的签名,并调用我通过模板参数传递的函数。

&(luaCaller<myFunc>)

当我试图将它包装在另一个函数中时,问题就出现了。非类型模板参数必须外部链接,因此以下操作失败:

void pushFunction(lua_State * _luaState, void(* _f)(void))
{
    lua_pushcfunction(_luaState, &(luaCaller<_f>));
}

这是有意义的,因为函数的地址需要在编译时知道。你不能随便扔进一个指针,就指望编译器知道要创建哪些类。不幸的是,如果我添加一个在编译时已知的函数指针,它仍然会失败。函数指针的值被复制到_a中,因此_a在技术上在编译时仍然是未知的。因此,我希望下面的代码能够工作:

void pushFunction(lua_State * _luaState, void(* const _f)(void))
{
    lua_pushcfunction(_luaState, &(luaCaller<_f>));
}

或者

void pushFunction(lua_State * _luaState, void(* & _f)(void))
{
    lua_pushcfunction(_luaState, &(luaCaller<_f>));
}

在第一种情况下,因为值不允许改变,我们知道如果它是外部链接的,它在技术上仍然是外部链接的。在第二种情况下,它作为一个引用被传递进来,这意味着它应该有相同的链接,不是吗?但这两种尝试都没有奏效。为什么?有可能绕过这个吗?我如何能干净地自动生成调用另一个函数的函数?

const限定符意味着您不允许更改某些内容,而不是说它是编译时常量。_a的初始值是在函数调用时确定的,对于* const &a,如果引用的对象不是const,则该值甚至可以在运行时通过某些外部手段(如另一个线程)更改。

要使完全模板化的包装器工作,您需要为编译器提供足够的信息,以便为每个可能的模板参数编译函数,并提供在这些函数之间切换的逻辑。模板系统生成并组织相关的函数,但它不是一个动态调度程序。

如果可以将函数指针添加到lua_State对象并消除模板形参,那将是一个解决方案。

如果将函数指针作为doCaller的模板参数,您的解决方案将有效,但这会破坏其目的。

与其使用非类型函数模板方法来绑定想要在包装器函数内部调用的第二个函数,不如使用struct和静态luaCaller方法。这应该允许您维护将luaCaller传递给lua_pushcfunction所需的函数签名。

例如,你可以有一个像这样的结构体:

template<void (*f) void>
struct wrapper
{   
    static int luaCaller(lua_State * _luaState)
    {
        f();
        return 0;
    }
};
template<typename Functor>
void doCaller(lua_State * _luaState, Functor wrapper)
{
    Functor::luaCaller(_luaState);
}

然后像这样调用它:

doCaller(&luaState, wrapper<my_func>());