卢宾德如何发挥作用?

发布于 2024-11-09 11:51:36 字数 217 浏览 0 评论 0原文

我感兴趣的是 Luabind 包装器如何能够在没有 lua_State *L 且不使用 Lua 堆栈的情况下传递函数。

Luabind: 如何

  1. 计算函数参数?
  2. 将函数参数链接到Lua堆栈?
  3. 链接这些类

我并不想创建另一个像 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:

  1. count the function parameters?
  2. link the function parameters to Lua stack?
  3. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

分开我的手 2024-11-16 11:51:36

好问题。我对 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和两个函数classproperty。 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_<上调用operator [] /code> 类有一些定义(即 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_ ::注册对象。 lua_State 始终传递给 register_

唷。现在让我们看看执行 class_("C").def("f", &C::f) 时会发生什么。这将构造一个具有特定名称的 class_ 实例,该名称位于 class_detail::registration 成员中。调用 class_::def 方法会写入 reg 结构等内容,但在 def 调用链的更深处,有一条非常有趣的行:

            object fn = make_function(
                L, f, deduce_signature(f, (Class*)0), policies);

哦,deduce_signature< /code>,我真的很想看到这一点。现在我想看不到它,但它的工作方式是这样的:通过 boost 辅助的黑暗预处理器魔法 (BOOST_PP_ITERATE 和其他一些实用程序)为 1 和 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>()
     }

同样,为所有 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:

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)
];

First thing to notice is that L is passed to luabind a lot, the call to open creates a few globals in the Lua state: the __luabind_classes of type userdata and two functions class and property. 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's module:

inline module_ module(lua_State* L, char const* name = 0)
{
    return module_(L, name);
}

Simple enough, here's 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;
};

So what our little program does is call operator [] on the module_ class with some definitions (that's the scope parameter), but the module_ class knows in which Lua state to operate. The scope class is also interesting to look at (some parts are omitted and some slightly simplified):

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 is building a linked list of detail::registration nodes, that list comes from using operator,. So when one does module(L) [class_<...>..., class_<...>...], class_ which inherits from scope initializes its base with a detail::registration instance, then the comma operator of scope builds a linked list of all registrations, this is passed to module_::operator[] which calls scope::register_ which in turn enumerates the chain and calls register_ on all those detail::registration objects. The lua_State is always passed to register_.

Phew. Now let's see what happens when one does class_<C>("C").def("f", &C::f). This constructs an instance of class_<C> with a certain name which goes in the detail::registration member in class_. Calling the class_::def method writes in the reg structure and whatnot, but here's a very interesting line deeper in the call chain from def:

            object fn = make_function(
                L, f, deduce_signature(f, (Class*)0), policies);

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:

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>()
     }

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 right deduce_signature overload for the functions you pass to def and that will return the correct boost::mpl::vectorX type. From there make_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

纵情客 2024-11-16 11:51:36

luabind 具有 C API 接受的熟悉的 int luafunction(lua_State* L) 原型的模板化包装函数。本质上,lua_CFunction 是为你创建的。要调用的实际 C 或 C++ 函数可以作为包装器的上值存储。对于 C++ 成员函数,this 指针可以从第一个参数中获取。

使用 upvalues 包装 C 函数的示例代码:(

template<typename R, typename T1>
int arg1wrapper(lua_State* L)
{
    typedef R (*F)(T1);
    F func = (F)lua_touserdata(L, lua_upvalueindex(1));
    R retValue = func(luaToC<T1>(L, 1));
    push(L, retValue);
    return 1;
}

// example use
template<typename R, typename T1>
void push(R (*func)(T1))
{
    lua_pushlightuserdata(L, func);
    lua_pushcclosure(L, &arg1wrapper<R, T1>, 1);
}

luaToC 模板函数将专门用于库打算支持的每种 C 和 C++ 类型。push 函数将类似地重载.)

您会注意到,上述一对函数仅适用于一种特定类型的 C 函数;具有非 void 返回值和单个参数的函数。通过将返回值操作分解到专门用于 void 的第三个模板中,可以轻松处理 void 返回,但为了支持其他数量的参数,您需要一堆重载。 luabind 是这样做的:它对它支持的每一数量的参数都有一个重载,包括 0 个参数(最大数量是他们选择的任意数字)。

(请注意,在 C++0x 中,您可以使用可变参数模板来支持具有相同模板的任意数量的参数)

luabind has templated wrapper functions for the familiar int 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, the this pointer can be taken from the first argument.

Example code wrapping a C function using upvalues:

template<typename R, typename T1>
int arg1wrapper(lua_State* L)
{
    typedef R (*F)(T1);
    F func = (F)lua_touserdata(L, lua_upvalueindex(1));
    R retValue = func(luaToC<T1>(L, 1));
    push(L, retValue);
    return 1;
}

// example use
template<typename R, typename T1>
void push(R (*func)(T1))
{
    lua_pushlightuserdata(L, func);
    lua_pushcclosure(L, &arg1wrapper<R, T1>, 1);
}

(The luaToC templated function would be specialized for every C and C++ type the library intends to support. The push 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)

南渊 2024-11-16 11:51:36

我不熟悉 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 use lua_State internally.

太阳公公是暖光 2024-11-16 11:51:36

来自手册

您需要做的第一件事是调用 luabind::open(lua_State*) ,它将注册从 Lua 创建类的函数,并初始化 luabind 使用的一些状态全局结构。

所以我(有点受过教育)的猜测是,您执行了 luabind::open 操作,它会缓存状态以供其余函数使用。 (线索在于“初始化一些状态全局结构”。)

From the manual:

The first thing you need to do is to call luabind::open(lua_State*) which will register the functions to create classes from Lua, and initialize some state-global structures used by luabind.

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".)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文