• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

luabridge

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

最近为了总结Lua绑定C/C++对象的各种方法、第三方库和原理,学习了LuaBridge库为Lua绑定C/C++对象,下面是学习笔记,实质是对该库的Reference Manual基本上翻译了一遍,学习过程中测试代码,放在我的github上。

LuaBridge的主要特点
     源码只有头文件,没有.cpp文件,没有MakeFile,使用时只需一个#include即可。
     支持不同的对象生命周期管理模式。
     对Lua栈访问方便并且是类型安全的(type-safe)。
     Automatic function parameter type binding.
     Easy access to Lua objects like tables and functions.
     LuaBridge的API是基于C++模板元编程(template metaprogramming)的。在编译时这些模板自动生成各种Lua API调用,从而可以再Lua脚本中使用C++程序中的类和函数。为了能在C++中使用Lua的数据,比如number,string,table以及方便调用Lua的函数,使用LuaBridge中的LuaRef类,可以方便做到。

LuaBridge设计原则
     由于LuaBridge的设计目标尽可能方便使用,比如只有头文件、没有用到高级C++的语法、不需要配置。因此LuaBridge性能虽足够好,但并不是最好的,比如OOLua(https://code.google.com/p/oolua/)执行效率就比它好,并且它也不像LuaBind(http://www.rasterbar.com/products/luabind.html)那样功能全面。LuaBridge不支持下面特性:
     枚举型常量
     不支持8个以上的函数或方法的调用
     重载函数、方法和构造函数(Overloaded functions, methods, or constructors)
     全局变量(变量必须被包装在命名空间里)
     自动地转换STL容器类型和Table
     在Lua中继承C++类(Inheriting Lua classes from C++ classes)。
     Passing nil to a C++ function that expects a pointer or reference
     Standard containers like std::shared_ptr

在Lua访问C++
     为了在Lua中使用C++中的数据和函数,LuaBridge要求任何需要使用的数据的都需要注册。LuaBridge可以注册下面五种类型数据:
     Namespaces  一个Lua table包含了其他注册信息
     Data  全局变量或静态变量、数据成员或静态数据成员
     Functions  一般函数、成员函数或静态成员函数
     CFunctions  A regular function, member function, or static member function that uses the lua_CFunction calling convention
     Properties  Global properties, property members, and static property members. These appear like data to Lua,but are implemented in C++ using functions to get and set the values.
     Data和Properties在注册时被标记为只读(read-only)。这不同于const,这些对象的值能在C++中修改,但不能在Lua脚本中修改。

Namespaces
     LuaBridge索引的注册都是在一个namespace中,namespace是从lua角度来看的,它实质上就是table,注意这里的namespace不是C++中的namespace,C++的namespace不是一定需要的。LuaBridge的namespace是对Lua脚本来说的,它们被作为逻辑组合工具(logical grouping tool)。为了访问Lua的全局命名空间(global namespace),可以在C++中,这样调用:
  1. getGlobalNamespace (L);  
上面的调用会返回一个对象(实质是table)可用来进一步注册,比如:
  1. getGlobalNamespace (L)  
  2.     .beginNamespace ("test")  
上面的调用就会在Lua的_G中创建一个名为"test"的table,现在这个table还是空的。LuaBridge保留所有以双下划线开头命名的标识,因此__test是无效的命名,尽管这样命名LuaBridge不会报错。我们可以进一步扩展上面的注册:
  1. getGlobalNamespace (L)  
  2.        .beginNamespace ("test")  
  3.       .beginNamespace ("detail")  
  4.       .endNamespace ()  
  5.       .beginNamespace ("utility")  
  6.       .endNamespace ()  
  7.  .endNamespace ();  
这样注册后,我们就可以在Lua中使用test, test.detail,和test.utility。这里的引入的endNamespace函数,也会返回一个对象(实质也是table),该对象实质就是上一层namespace,表示当前namespace注册完成。 All LuaBridge functions which create registrations return an object upon which subsequent registrations can be made,allowing for an unlimited number of registrations to be chained together using the dot operator。在一个namespace中,注册相同命名的对象,对于LuaBridge来说是没有定义的行为。一个namespace可以多次使用增加更多的成员。比如下面两段代码是等价的:
  1. getGlobalNamespace (L)  
  2.   .beginNamespace ("test")  
  3.  .addFunction ("foo", foo)  
  4.   .endNamespace ();  
  5.   
  6. getGlobalNamespace (L)  
  7.   .beginNamespace ("test")  
  8.  .addFunction ("bar", bar)  
  9.   .endNamespace ();  

  1. getGlobalNamespace (L)  
  2.   .beginNamespace ("test")  
  3.  .addFunction ("foo", foo)  
  4.  .addFunction ("bar", bar)  
  5.   .endNamespace ();  

Data, Properties, Functions, and CFunctions
     Data, Properties, Functions, and CFunctions可以依次使用addVariable,, addProperty, addFunction, and addCFunction来注册。在Lua脚本中调用注册的函数时,LuaBridge会自动地传入相应的参数,并对参数类型转和检查。同样,函数的返回值也会自动处理。当前LuaBridge最多可处理8个参数。Pointers, references, and objectsof class type as parameters are treated specially。如果我们在C++中有以下定义:
  1. int globalVar;  
  2. static float staticVar;  
  3.   
  4. std::string stringProperty;  
  5. std::string getString () { return stringProperty; }  
  6. void setString (std::string s) { stringProperty = s; }  
  7.   
  8. int foo () { return 42; }  
  9. void bar (char const*) { }  
  10. int cFunc (lua_State* L) { return 0; }  
为了在Lua使用这些变量和函数,我们可以按以下方式注册它们:
  1. getGlobalNamespace (L)  
  2.   .beginNamespace ("test")  
  3.  .addVariable ("var1", &globalVar)  
  4.  .addVariable ("var2", &staticVar, false)     // read-only  
  5.  .addProperty ("prop1", getString, setString)  
  6.  .addProperty ("prop2", getString)            // read only  
  7.  .addFunction ("foo", foo)  
  8.  .addFunction ("bar", bar)  
  9.  .addCFunction ("cfunc", cFunc)  
  10.   .endNamespace ();  
Variables在注册时,可以通过传递第二个参数为false,确保Variables不会在Lua被修改,默认第二个参数是true。Properties在注册时,若不传递set函数,则在脚本中是read-only。
通过上面注册后,则下面表达式在Lua是有效的:
  1. test        -- a namespace,实质就是一个table,下面都是table中的成员  
  2. test.var1   -- a lua_Number variable  
  3. test.var2   -- a read-only lua_Number variable  
  4. test.prop1  -- a lua_String property  
  5. test.prop2  -- a read-only lua_String property  
  6. test.foo    -- a function returning a lua_Number  
  7. test.bar    -- a function taking a lua_String as a parameter  
  8. test.cfunc  -- a function with a variable argument list and multi-return  
注意test.prop1和test.prop2引用的C++中同一个变量,然后test.prop2是read-only,因此在脚本中对test.prop2赋值,会导致运行时错误(run-time error)。在Lua按以下方式使用:
  1. test.var1 = 5         -- okay  
  2. test.var2 = 6         -- error: var2 is not writable  
  3. test.prop1 = "Hello"  -- okay  
  4. test.prop1 = 68       -- okay, Lua converts the number to a string.  
  5. test.prop2 = "bar"    -- error: prop2 is not writable  
  6.   
  7. test.foo ()           -- calls foo and discards the return value  
  8. test.var1 = foo ()    -- calls foo and stores the result in var1  
  9. test.bar ("Employee") -- calls bar with a string  
  10. test.bar (test)       -- error: bar expects a string not a table  

Class Objects
类 的注册是以beginClass或deriveClass开始,以endClass结束。一个类注册完后,还可以使用beginClass重新注册更多的 信息,但是deriveClass只能被使用一次。为了给已经用deriveClass注册的类,注册更多的信息,可以使用beginClass。
  1. class A {   
  2.     public:  
  3.     A() { printf("A constructor\n");}  
  4.   
  5.     static int staticData;  
  6.     static int getStaticData() {return staticData;}  
  7.   
  8.     static float staticProperty;  
  9.     static float getStaticProperty () { return staticProperty; }  
  10.     static void setStaticProperty (float f) { staticProperty = f; }  
  11.   
  12.     static int staticCFunc (lua_State *L) { return 0; }  
  13.   
  14.     std::string dataMember;  
  15.   
  16.     char dataProperty;  
  17.     char getProperty () const { return dataProperty; }  
  18.     void setProperty (char v) { dataProperty = v; }  
  19.   
  20.   
  21.     void func1 () {printf("func1 In Class A\n"); }  
  22.     virtual void virtualFunc () {printf("virtualFunc In Class A\n");  }  
  23.   
  24.     int cfunc (lua_State* L) { printf("cfunc In Class A\n");  return 0; }  
  25. };  
  26.   
  27. class B : public A {  
  28.     public:  
  29.     B() { printf("B constructor\n");}  
  30.   
  31.     double dataMember2;  
  32.   
  33.     void func1 () {printf("func1 In Class B\n"); }  
  34.     void func2 () { printf("func2 In Class B\n"); }  
  35.     void virtualFunc () {printf("virtualFunc In Class B\n");  }  
  36. };  
  37.   
  38. int A::staticData = 3;  
  39. float A::staticProperty = 0.5;  
按下面方式注册:
  1.    getGlobalNamespace (L)  
  2. .beginNamespace ("test")  
  3.     .beginClass<A>("A")  
  4.     .addConstructor <void (*) (void)> ()  
  5.     .addStaticData ("staticData", &A::staticData)  
  6.     .addStaticProperty ("staticProperty", &A::getStaticData)  
  7.     .addStaticFunction ("getStaticProperty", &A::getStaticProperty) //read-only  
  8.     .addStaticCFunction ("staticCFunc", &A::staticCFunc)  
  9.     .addData ("data", &A::dataMember)  
  10.     .addProperty ("prop", &A::getProperty, &A::setProperty)  
  11.     .addFunction ("func1", &A::func1)  
  12.     .addFunction ("virtualFunc", &A::virtualFunc)  
  13.     .addCFunction ("cfunc", &A::cfunc)  
  14.     .endClass ()  
  15.     .deriveClass<B, A>("B")  
  16.     .addConstructor <void (*) (void)> ()  
  17.     .addData ("data", &B::dataMember2)  
  18.     .addFunction ("func1", &B::func1)  
  19.     .addFunction ("func2", &B::func2)  
  20.     .endClass ()  
  21. .endNamespace ();  
注册后,可以再Lua脚本中按一下方式使用:
  1. local AClassObj = test.A ()  --create class A instance  
  2.   
  3. print("before:",test.A.staticData) -- access class A static member                                       
  4. test.A.staticData = 8  -- modify class A static member                                                   
  5. print("after:",test.A.staticData)                                                                        
  6.   
  7. print("before:", test.A.getStaticProperty())                                                             
  8. --test.A.staticProperty = 1.2 --error:can not modify                                                     
  9.       
  10. print("staticCFunc")  
  11. test.A.staticCFunc()  
  12.           
  13. AClassObj.data = "sting"  
  14. print("dataMember:",AClassObj.data)  
  15.           
  16. AClassObj.prop = 'a'  
  17. print("property:",AClassObj.prop)  
  18.           
  19. AClassObj:func1()  
  20.           
  21. AClassObj:virtualFunc()  
  22.           
  23. AClassObj:cfunc()  
  24.           
  25. BClassObj = test.B()  
  26.           
  27. BClassObj:func1()  
  28.       
  29. BClassObj:func2()                                                                                        
  30.                                                          
  31. BClassObj:virtualFunc()  
      
其输出结果为:
  1. A constructor  
  2. before: 3  
  3. after:  8  
  4. before: 0.5  
  5. staticCFunc  
  6. dataMember:     sting  
  7. property:       a  
  8. func1 In Class A  
  9. virtualFunc In Class A  
  10. cfunc In Class A  
  11. A constructor  
  12. B constructor  
  13. func1 In Class B  
  14. func2 In Class B  
  15. virtualFunc In Class B  
类 的方法注册类似于通常的函数注册,虚函数也是类似的,没有特殊的语法。在LuaBridge中,能识别const方法并且在调用时有检测的,因此如果一个 函数返回一个const object或包含指向const object的数据给Lua脚本,则在Lua中这个被引用的对象则被认为是const的,它只能调用const的方法。对于每个类,析构函数自动注册的。 无须在继承类中重新注册已在基类中注册过的方法。If a class has a base class that is **not** registeredwith Lua, there is no need to declare it as a subclass.

Constructors
为 了在Lua中,创建类的对象,必须用addConstructor为改类注册构造函数。并且LuaBridge不能自动检测构造函数的参数个数和类型(这 与注册函数或方法能自动检测是不同的),因此在用注册addConstructor时必须告诉LuaBridge在Lua脚本将用到的构造函数签名,例 如:
  1. struct A {  
  2.   A ();  
  3. };  
  4.   
  5. struct B {  
  6.   explicit B (char const* s, int nChars);  
  7. };  
  8.   
  9. getGlobalNamespace (L)  
  10.   .beginNamespace ("test")  
  11.     .beginClass <A> ("A")  
  12.       .addConstructor <void (*) (void)> ()  
  13.     .endClass ()  
  14.     .beginClass <B> ("B")  
  15.       .addConstructor <void (*) (char const*, int)> ()  
  16.     .endClass ();  
  17.   .endNamespace ()  
在Lua中,就可以一些方式,创建A和B的实例:
  1. a = test.A ()           -- Create a new A.  
  2. b = test.B ("hello", 5) -- Create a new B.  
  3. b = test.B ()           -- Error: expected string in argument 1  
lua_State*
有时候绑定的函数或成员函数,需要lua_State*作为参数来访问栈。使用LuaBridge,只需要在将要绑定的函数最后添加lua_State*类型的参数即可。比如:
  1. void useStateAndArgs (int i, std::string s, lua_State* L);                                         
  2. getGlobalNamespace (L).addFunction ("useStateAndArgs", &useStateAndArgs);  

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
高并发 Nginx+Lua OpenResty系列(8)——Lua模版渲染发布时间:2022-07-22
下一篇:
Redis Lua脚本原理发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap