卢宾德如何发挥作用?
我感兴趣的是 Luabind 包装器如何能够在没有 lua_State *L 且不使用 Lua 堆栈的情况下传递函数。
Luabind: 如何
- 计算函数参数?
- 将函数参数链接到Lua堆栈?
- 链接这些类
我并不想创建另一个像 Luabind 这样的绑定到其他库。我只是想知道他们是怎么做到的。只是一个好奇的人。
I'm interested on how Luabind wrapper make it possible to pass a function without the lua_State *L
and not using the Lua stack.
How does Luabind:
- count the function parameters?
- link the function parameters to Lua stack?
- link those classes
I'm not trying to create another binding like Luabind to other libraries. I'm just wondering how did they do that. Just a curious man.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
好问题。我对 luabind 的作用有一些模糊的想法,但我没有足够的知识来完整和准确地回答。有了 IDE 和调试器,我开始剖析以下非常简单的部分:
首先要注意的是
L
被传递给 luabind 很多,对open
的调用创建了一些Lua状态下的全局变量:userdata类型的__luabind_classes
和两个函数class
和property
。 Luabind 似乎不使用全局变量——它需要的一切都保存在 lua 环境中。现在我们进入
module(L)[...]
。原始代码是最好的解释,首先这里是module
:很简单,这里是
module_
:所以我们的小程序所做的是在
module_<上调用operator [] /code> 类有一些定义(即
scope
参数),但是module_
类知道要在哪个 Lua 状态下操作。scope
类也很有趣(有些部分被省略,有些部分被稍微简化):scope
正在构建一个detail::registration 节点,该列表来自使用
operator,
。因此,当执行module(L) [class_<...>..., class_<...>...]
时,class_
继承自scope
使用detail::registration
实例初始化其基类,然后scope
的逗号运算符构建所有注册的链接列表,这是通过到module_::operator[]
,它调用scope::register_
,后者依次枚举链并在所有这些细节上调用
对象。register_
::注册lua_State
始终传递给register_
。唷。现在让我们看看执行
class_("C").def("f", &C::f)
时会发生什么。这将构造一个具有特定名称的class_
实例,该名称位于class_
的detail::registration
成员中。调用class_::def
方法会写入 reg 结构等内容,但在def
调用链的更深处,有一条非常有趣的行:哦,
deduce_signature< /code>,我真的很想看到这一点。现在我想看不到它,但它的工作方式是这样的:通过 boost 辅助的黑暗预处理器魔法 (
BOOST_PP_ITERATE
和其他一些实用程序)为 1 和 LUABIND_MAX_ARITY 之间的每个 N 生成以下内容:同样,为所有 N 介于 1 和 LUABIND_MAX_ARITY 之间(默认为 10)。有几个重载可以处理 const 方法、虚拟包装器和自由函数等,这意味着大约有 50 个 deduce_signature 函数在预处理器之后和编译开始之前出现在您的源代码中。从这里开始,编译器的工作就是为传递给
def
的函数选择正确的deduce_signature
重载,并返回正确的boost::mpl::vectorX
类型。从那里make_function
可以做任何事情 - 它有一个[编译时]参数类型列表,并通过一些更多的模板魔法对这些参数进行计数、与 Lua 值之间的转换等。这就是我要停下来的地方。调查基于 Luabind 0.8.1。请随意浏览/调试 Luabind 的代码以获得更多答案 - 这需要一些时间,但在你习惯了这种风格后并不难:)祝你好运。
TL;DR:魔法...黑魔法
Good question. I had some vague idea of how luabind does what it does, but I didn't know enough to answer fully and accurately. Armed with an IDE and debugger I started dissecting the following very simple piece:
First thing to notice is that
L
is passed to luabind a lot, the call toopen
creates a few globals in the Lua state: the__luabind_classes
of type userdata and two functionsclass
andproperty
. Luabind doesn't seem to use global variables - everything it needs is saved in the lua environment.Now we get to
module(L)[...]
. The original code is the best explanation, first here'smodule
:Simple enough, here's
module_
:So what our little program does is call operator [] on the
module_
class with some definitions (that's thescope
parameter), but themodule_
class knows in which Lua state to operate. Thescope
class is also interesting to look at (some parts are omitted and some slightly simplified):scope
is building a linked list ofdetail::registration
nodes, that list comes from usingoperator,
. So when one doesmodule(L) [class_<...>..., class_<...>...]
,class_
which inherits fromscope
initializes its base with adetail::registration
instance, then the comma operator ofscope
builds a linked list of all registrations, this is passed tomodule_::operator[]
which callsscope::register_
which in turn enumerates the chain and callsregister_
on all thosedetail::registration
objects. Thelua_State
is always passed toregister_
.Phew. Now let's see what happens when one does
class_<C>("C").def("f", &C::f)
. This constructs an instance ofclass_<C>
with a certain name which goes in thedetail::registration
member inclass_
. Calling theclass_::def
method writes in the reg structure and whatnot, but here's a very interesting line deeper in the call chain fromdef
:Oooh,
deduce_signature
, I really wanted to see that. Now I want to unsee it, but the way it works is this: through dark preprocessor sorcery aided by boost (BOOST_PP_ITERATE
and some other utilities) the following is generated for each N between one and LUABIND_MAX_ARITY:Again, a function like this is generated for all N between 1 and LUABIND_MAX_ARITY which is 10 by default. There are a couple of overloads to handle const methods, virtual wrappers and free functions and such, which means that there are around 50
deduce_signature
functions that end up in your sources just after the preprocessor and before compilation has started. From there, it's compiler's job to choose the rightdeduce_signature
overload for the functions you pass todef
and that will return the correctboost::mpl::vectorX
type. From theremake_function
can do anything - it has a [compile time] list of parameter types and through some more template magic these are counted, converted to and from Lua values and so on.This is where I will stop. Investigation is based on Luabind 0.8.1. Feel free to browse/debug Luabind's code for more answers - it takes some time but it is not that hard after you get used to the style :) Good luck.
TL;DR: Magic... black magic
luabind
具有 C API 接受的熟悉的int luafunction(lua_State* L)
原型的模板化包装函数。本质上,lua_CFunction 是为你创建的。要调用的实际 C 或 C++ 函数可以作为包装器的上值存储。对于 C++ 成员函数,this
指针可以从第一个参数中获取。使用 upvalues 包装 C 函数的示例代码:(
luaToC
模板函数将专门用于库打算支持的每种 C 和 C++ 类型。push
函数将类似地重载.)您会注意到,上述一对函数仅适用于一种特定类型的 C 函数;具有非 void 返回值和单个参数的函数。通过将返回值操作分解到专门用于 void 的第三个模板中,可以轻松处理 void 返回,但为了支持其他数量的参数,您需要一堆重载。 luabind 是这样做的:它对它支持的每一数量的参数都有一个重载,包括 0 个参数(最大数量是他们选择的任意数字)。
(请注意,在 C++0x 中,您可以使用可变参数模板来支持具有相同模板的任意数量的参数)
luabind
has templated wrapper functions for the familiarint luafunction(lua_State* L)
prototype which the C API accepts. In essence, the lua_CFunction is created for you. The actual C or C++ function to call can be stored as an upvalue to the wrapper. In the case of a C++ member function, thethis
pointer can be taken from the first argument.Example code wrapping a C function using upvalues:
(The
luaToC
templated function would be specialized for every C and C++ type the library intends to support. Thepush
function would be overloaded similarily.)You will notice that the above pair of functions will work for only one particular kind of C function; functions with a non-void return value and a single parameter. Void returns can be easily handled by factoring the return value operations into a third template specialized for void, but to support other amounts of parameters, you need a bunch of overloads. luabind does this: it has one overload for every amount of parameters it supports, including one for 0 parameters (the maximum amount is some arbitrary number they chose).
(note that in C++0x you can use variadic templates to support any amount of parameters with the same template)
我不熟悉 luabind,但“包装器”的整体思想是它构建在一些较低级别的抽象之上,并将其封装起来。
luabind
几乎可以肯定确实在内部使用lua_State
。I'm not familiar with
luabind
, but the whole idea of a "wrapper" is that it's built on top of some lower level abstraction, encapsulating it.luabind
almost certainly does uselua_State
internally.来自手册:
所以我(有点受过教育)的猜测是,您执行了 luabind::open 操作,它会缓存状态以供其余函数使用。 (线索在于“初始化一些状态全局结构”。)
From the manual:
So my (somewhat educated) guess is that you do the
luabind::open
thing and it caches the state for use in the remaining functions. (The clue lies in "initialize some state-global structures".)