用LuaBridge为Lua绑定C/C++对象
最近为了总结Lua绑定C/C++对象的各种方法、第三方库和原理,学习了LuaBridge库为Lua绑定C/C++对象,下面是学习笔记,实质是对该库的Reference Manual基本上翻译了一遍,学习过程中测试代码,放在我的github上。 LuaBridge设计原则 在Lua访问C++ Namespaces 在CODE上查看代码片派生到我的代码片 getGlobalNamespace (L); 上面的调用会返回一个对象(实质是table)可用来进一步注册,比如: getGlobalNamespace (L) .beginNamespace ("test") 上面的调用就会在Lua的_G中创建一个名为”test”的table,现在这个table还是空的。LuaBridge保留所有以双下划线开头命名的标识,因此__test是无效的命名,尽管这样命名LuaBridge不会报错。我们可以进一步扩展上面的注册: getGlobalNamespace (L) .beginNamespace ("test") .beginNamespace ("detail") .endNamespace () .beginNamespace ("utility") .endNamespace () .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可以多次使用增加更多的成员。比如下面两段代码是等价的: getGlobalNamespace (L) .beginNamespace ("test") .addFunction ("foo",foo) .endNamespace (); getGlobalNamespace (L) .beginNamespace ("test") .addFunction ("bar",bar) .endNamespace (); 和 getGlobalNamespace (L) .beginNamespace ("test") .addFunction ("foo",foo) .addFunction ("bar",bar) .endNamespace (); Data,Properties,Functions,and CFunctions int globalVar; static float staticVar; std::string stringProperty; std::string getString () { return stringProperty; } void setString (std::string s) { stringProperty = s; } int foo () { return 42; } void bar (char const*) { } int cFunc (lua_State* L) { return 0; } 为了在Lua使用这些变量和函数,我们可以按以下方式注册它们: getGlobalNamespace (L) .beginNamespace ("test") .addVariable ("var1",&globalVar) .addVariable ("var2",&staticVar,false) // read-only .addProperty ("prop1",getString,setString) .addProperty ("prop2",getString) // read only .addFunction ("foo",bar) .addCFunction ("cfunc",cFunc) .endNamespace (); Variables在注册时,可以通过传递第二个参数为false,确保Variables不会在Lua被修改,默认第二个参数是true。Properties在注册时,若不传递set函数,则在脚本中是read-only。 test -- a namespace,实质就是一个table,下面都是table中的成员 test.var1 -- a lua_Number variable test.var2 -- a read-only lua_Number variable test.prop1 -- a lua_String property test.prop2 -- a read-only lua_String property test.foo -- a function returning a lua_Number test.bar -- a function taking a lua_String as a parameter 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按以下方式使用: test.var1 = 5 -- okay test.var2 = 6 -- error: var2 is not writable test.prop1 = "Hello" -- okay test.prop1 = 68 -- okay,Lua converts the number to a string. test.prop2 = "bar" -- error: prop2 is not writable test.foo () -- calls foo and discards the return value test.var1 = foo () -- calls foo and stores the result in var1 test.bar ("Employee") -- calls bar with a string test.bar (test) -- error: bar expects a string not a table Class Objects class A { public: A() { printf("A constructorn");} static int staticData; static int getStaticData() {return staticData;} static float staticProperty; static float getStaticProperty () { return staticProperty; } static void setStaticProperty (float f) { staticProperty = f; } static int staticCFunc (lua_State *L) { return 0; } std::string dataMember; char dataProperty; char getProperty () const { return dataProperty; } void setProperty (char v) { dataProperty = v; } void func1 () {printf("func1 In Class An"); } virtual void virtualFunc () {printf("virtualFunc In Class An"); } int cfunc (lua_State* L) { printf("cfunc In Class An"); return 0; } }; class B : public A { public: B() { printf("B constructorn");} double dataMember2; void func1 () {printf("func1 In Class Bn"); } void func2 () { printf("func2 In Class Bn"); } void virtualFunc () {printf("virtualFunc In Class Bn"); } }; int A::staticData = 3; float A::staticProperty = 0.5; 按下面方式注册: getGlobalNamespace (L) .beginNamespace ("test") .beginClass<A>("A") .addConstructor <void (*) (void)> () .addStaticData ("staticData",&A::staticData) .addStaticProperty ("staticProperty",&A::getStaticData) .addStaticFunction ("getStaticProperty",&A::getStaticProperty) //read-only .addStaticCFunction ("staticCFunc",&A::staticCFunc) .addData ("data",&A::dataMember) .addProperty ("prop",&A::getProperty,&A::setProperty) .addFunction ("func1",&A::func1) .addFunction ("virtualFunc",&A::virtualFunc) .addCFunction ("cfunc",&A::cfunc) .endClass () .deriveClass<B,A>("B") .addConstructor <void (*) (void)> () .addData ("data",&B::dataMember2) .addFunction ("func1",&B::func1) .addFunction ("func2",&B::func2) .endClass () .endNamespace (); 注册后,可以再Lua脚本中按一下方式使用: local AClassObj = test.A () --create class A instance print("before:",test.A.staticData) -- access class A static member test.A.staticData = 8 -- modify class A static member print("after:",test.A.staticData) print("before:",test.A.getStaticProperty()) --test.A.staticProperty = 1.2 --error:can not modify print("staticCFunc") test.A.staticCFunc() AClassObj.data = "sting" print("dataMember:",AClassObj.data) AClassObj.prop = 'a' print("property:",AClassObj.prop) AClassObj:func1() AClassObj:virtualFunc() AClassObj:cfunc() BClassObj = test.B() BClassObj:func1() BClassObj:func2() BClassObj:virtualFunc() 其输出结果为: A constructor before: 3 after: 8 before: 0.5 staticCFunc dataMember: sting property: a func1 In Class A virtualFunc In Class A cfunc In Class A A constructor B constructor func1 In Class B func2 In Class B 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 struct A { A (); }; struct B { explicit B (char const* s,int nChars); }; getGlobalNamespace (L) .beginNamespace ("test") .beginClass <A> ("A") .addConstructor <void (*) (void)> () .endClass () .beginClass <B> ("B") .addConstructor <void (*) (char const*,int)> () .endClass (); .endNamespace () 在Lua中,就可以一些方式,创建A和B的实例: a = test.A () -- Create a new A. b = test.B ("hello",5) -- Create a new B. b = test.B () -- Error: expected string in argument 1 lua_State* void useStateAndArgs (int i,std::string s,lua_State* L); getGlobalNamespace (L).addFunction ("useStateAndArgs",&useStateAndArgs); 在Lua中,就可按以下方式使用: useStateAndArgs(42,"hello") 在脚本中,只需传递前面两个参数即可。注意 lua_State*类型的参数就放在定义的函数最后,否则结果是未定义的。 Class Object Types `T*` or `T&`: Passed by reference,with _C++ lifetime_. `T const*` or `T const&`: Passed by const reference,with _C++ lifetime_. `T` or `T const`: Passed by value (a copy),with _Lua lifetime_. C++ Lifetime A a; push (L,&a); // pointer to 'a',C++ lifetime lua_setglobal (L,"a"); push (L,(A const*)&a); // pointer to 'a const',"ac"); push <A const*> (L,&a); // equivalent to push (L,(A const*)&a) lua_setglobal (L,"ac2"); push (L,new A); // compiles,but will leak memory lua_setglobal (L,"ap"); Lua Lifetime B b; push (L,b); // Copy of b passed,Lua lifetime. lua_setglobal (L,"b"); 当在Lua中调用注册的构造函数创建一个对象时,该对象同样是Lua lifetime的,当该对象不在被引用时,GC会自动回收该对象。当然你可以把这个对象引用作为参数传递给C++,但需要保证C++在通过引用使用该对象时, Pointers,References,and Pass by Value void func0 (A a); void func1 (A* a); void func2 (A const* a); void func3 (A& a); void func4 (A const& a); 则在Lua中,就可以按以下方式调用上面的函数: func0 (a) -- Passes a copy of a,using A's copy constructor. func1 (a) -- Passes a pointer to a. func2 (a) -- Passes a pointer to a const a. func3 (a) -- Passes a reference to a. func4 (a) -- Passes a reference to a const a. 上面所有函数,都可以通过a访问对象的成员以及方法。并且通常的C++的继承和指针传递规则也使用。比如: void func5 (B b); void func6 (B* b); 在lua中调用: func5 (b) - Passes a copy of b,using B's copy constructor. func6 (b) - Passes a pointer to b. func6 (a) - Error: Pointer to B expected. func1 (b) - Okay,b is a subclass of a. 当C++给Lua传递的指针是NULL时,LuaBridge会自动转换为nil代替。反之,当Lua给C++传递的nil,相当于给C++传递了一个NULL指针。 本文转载自http://www.voidcn.com/article/p-otddjrwn-bag.html 作者:MaximusZhou (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |