【问题标题】:Creating a C++ static wrapper function with specific signature创建具有特定签名的 C++ 静态包装函数
【发布时间】:2013-05-16 15:49:09
【问题描述】:

我在使用模板参数创建静态包装函数时遇到了一些问题。我不想将函数直接传递给包装函数,因为它需要特定的签名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 并且您提供了编译时间常数,不会使参数成为编译时间常数。跨度>
  • 这对我来说很奇怪。是因为您可以简单地转换为 const 吗?就是这么想的。
  • 是的,这是因为当编译器编译doCaller时,它不知道将来会传递给它什么。其他地方可以并且可能确实传递运行时变量。绝对没有迹象表明编译器该参数是编译时间常数,只有迹象表明一旦函数开始您将不会修改参数。
  • 啊,这就是我的意思。有没有办法指定某些东西是编译时间常数?我现在可以看到,出于同样的原因,按引用传递也不起作用。
  • doCaller(_luaState, a)luaCaller&lt;_a&gt;(_luaState) 有什么优势?只需使用第二个。

标签: c++ templates function-pointers


【解决方案1】:

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

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

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

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

【讨论】:

  • 我实际上已经按照您的建议做了,并将函数指针添加到 lua_State。然而,我很快意识到我需要一种独立于 lua_State 来存储这些函数的方法,并且在我的实现中它们实际上都是不同的签名。拥有一个 int(lua_State*) 的列表是一种方便的存储方式。 (我有很多可变参数模板;我的例子是一个简化的案例)
  • @roadkillguy 如果它们都有不同的签名,模板如何知道如何调用它们?
  • 虽然,我也许可以使用 std::bind 绑定到具有相同签名的简化就绪调用函数列表:void(lua_State *)。推送后,这些函数将通过 upvalues 与通用 lua 包装函数相关联。不过,目前看来,这个问题是无法解决的。
【解决方案2】:

您可以使用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>());

【讨论】:

  • T 是做什么的?这里的模板参数从不使用。
  • 是的,那个解决方案不是很好......我想我想出了一个更好的解决方案。
  • 啊,我明白了,您将 Functor 作为参数传递,因此您无需在 doCaller 中显式指定模板参数。 doCaller&lt;wrapper&lt;my_func&gt;&gt;(luaState) 也可以,是吗?
  • 我不明白为什么不......因为您使用的是静态函数,您始终可以通过类型和范围运算符(即wrapper&lt;my_function&gt;::luaCaller())访问该函数。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多