我对Luabind包装器如何使无需lua_State *L
且不使用Lua堆栈的函数传递成为可能很感兴趣。
Luabind如何:
我不是在尝试创建另一个像Luabind的绑定(bind)到其他库。我只是想知道他们是如何做到的。只是一个好奇的人。
最佳答案
好问题。我对luabind的工作方式有一个模糊的想法,但是我不知道该如何完全准确地回答。配备了IDE和调试器后,我开始剖析以下非常简单的部分:
struct C
{
int i;
int f(int x, const char* s)
};
lua_State* L = luaL_newstate();
open(L);
module(L)
[
class_<C>("C")
.def_readwrite("index", &C::i)
.def("f", &C::f)
];
首先要注意的是,
L
被传递给luabind很多,对open
的调用在Lua状态下创建了一些全局变量:userdata类型的__luabind_classes
以及两个函数class
和property
。 Luabind似乎没有使用全局变量-它需要的所有内容都保存在lua环境中。现在我们到
module(L)[...]
。原始代码是最好的解释,首先是module
:inline module_ module(lua_State* L, char const* name = 0)
{
return module_(L, name);
}
很简单,这是
module_
:class LUABIND_API module_
{
public:
module_(lua_State* L_, char const* name);
void operator[](scope s);
private:
lua_State* m_state;
char const* m_name;
};
因此,我们的小程序要做的是调用
module_
类上的操作符[],并带有一些定义(即scope
参数),但是module_
类知道在哪种Lua状态下进行操作。 scope
类也很有趣(省略了某些部分,并对其进行了一些简化):struct LUABIND_API scope
{
//...
explicit scope(detail::registration* reg);
scope& operator,(scope s);
void register_(lua_State* L) const;
private:
detail::registration* m_chain;
};
scope
正在构建detail::registration
节点的链接列表,该列表来自使用operator,
。因此,当有人执行module(L) [class_<...>..., class_<...>...]
时,从class_
继承的scope
用detail::registration
实例初始化其基数,然后scope
的逗号运算符将构建所有注册的链接列表,该列表将传递给module_::operator[]
,后者调用scope::register_
并依次枚举链并调用register_
在所有这些detail::registration
对象上。 lua_State
始终传递给register_
。ew现在,让我们看看当执行
class_<C>("C").def("f", &C::f)
时会发生什么。这将构造一个具有特定名称的class_<C>
实例,该实例会出现在detail::registration
的class_
成员中。调用class_::def
方法会在reg结构中写入内容,但在def
的调用链中更深一层是非常有趣的一行: object fn = make_function(
L, f, deduce_signature(f, (Class*)0), policies);
哦,
deduce_signature
,我真的很想看看。现在,我不想看到它,但是它的工作方式是这样的:通过在boost( BOOST_PP_ITERATE
和一些其他实用程序)辅助下的黑暗预处理器魔术,为N和LUABIND_MAX_ARITY之间的每个N生成以下内容:template <class R, class T, class A1, classA2, ..., classAN>
boost::mpl::vectorN_PLUS_2<R, T, A1, A2, ..., AN> // type of return value
deduce_signature(R(T::*)(A1, A2, ..., AN))
{
return boost::mpl::vectorN_PLUS_2<R, T, A1, A2, ..., AN>()
}
同样,将为1和LUABIND_MAX_ARITY之间的所有N(默认值为10)生成这样的函数。有很多重载可以处理const方法,虚拟包装程序和自由函数等,这意味着大约有50个
deduce_signature
函数最终会在预处理器之后和编译开始之前在您的源代码中结束。从那里开始,编译器的工作是为传递给deduce_signature
的函数选择正确的def
重载,这将返回正确的 boost::mpl::vectorX
类型。从那里make_function
可以做任何事情-它具有[compile time]参数类型列表,并且通过更多的模板魔术可以对这些参数进行计数,在Lua值之间进行转换,等等。这是我要停止的地方。调查基于Luabind 0.8.1。随意浏览/调试Luabind的代码以获取更多答案-这需要花费一些时间,但习惯了这种风格之后并不难:)祝您好运。
TL; DR: Magic ...黑魔法