我对Luabind包装器如何使无需lua_State *L且不使用Lua堆栈的函数传递成为可能很感兴趣。

Luabind如何:

  • 计算函数参数?
  • 将函数参数链接到Lua堆栈吗?
  • 链接这些类

  • 我不是在尝试创建另一个像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以及两个函数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_类上的操作符[],并带有一些定义(即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_继承的scopedetail::registration实例初始化其基数,然后scope的逗号运算符将构建所有注册的链接列表,该列表将传递给module_::operator[],后者调用scope::register_并依次枚举链并调用register_在所有这些detail::registration对象上。 lua_State始终传递给register_

    ew现在,让我们看看当执行class_<C>("C").def("f", &C::f)时会发生什么。这将构造一个具有特定名称的class_<C>实例,该实例会出现在detail::registrationclass_成员中。调用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 ...黑魔法

    10-07 19:37
    查看更多