LuaPlus学习(三)

系统 1430 0

<--!版权所有foruok,转载注明出处!-->

从lua调用C++函数和对象

利用LuaPlus可以方便的从C++中调用lua脚本,翻过也一样。通过注册函数或类对象,lua便可以访问C++。

一、C风格函数注册

Lua提供了C风格的回调函数注册,该函数原型如下:
int Callback(LuaState * state);
无论是全局函数、类非虚函数、类虚函数,只要符合上面的原型,都可以向Lua注册。我们以全局函数为例,下面是我们提供的一个回调函数CStyleAddFunc:
int CStyleAddFunc(LuaState * state) ... { LuaStackargs(state); if (args[ 1 ].IsNumber() && args[ 2 ].IsNumber()) ... { state -> PushNumber(args[ 1 ].GetInteger() + args[ 2 ].GetInteger()); return 1 ; } return 0 ; }
在回调函数中,我们通过栈来访问参数,栈中可以存贮多个参数, LuaStackargs(state);语句获取栈对象供后续访问。 接下来判断参数是否是数字,如果两个参数都是数字,那么进行加操作,将结果压入栈中,将压入栈中的数据的个数返回。注意,返回值代表压入栈中的元素的个数,而不是某种计算结果或其它意义的返回值。通过改变返回值来查看程序的输出,这样可以对返回值的含义有个感性的了解。 要注册回到只需调用Register函数即可,这在第一篇中已经用到。下面是测试函数:
void TestCFunctionCallBack() ... { LuaStateOwnerstate; // "print"needthis state -> OpenLibs(); // registermyfunctionCStyleAddFunctoAdd state -> GetGlobals().Register( " Add " ,CStyleAddFunc); // callmyfunctionandprinttheresult state -> DoString( " ret=Add(1,5);print(ret) " ); }
state -> DoString( " ret=Add(1,5);print(ret) " );该句用来从执行Lua命令串。我们先调用Add并将结果赋值给ret变量,然后打印ret的值。 main函数如下:
int _tmain( int argc,_TCHAR * argv[]) ... { TestCFunctionCallBack(); return 0 ; }
编译运行,一切OK。 我们也可以从Lua脚本文件中调用注册的回调函数, 第一篇 中有演示。 要注册类的成员函数,则需要调用Register的另一种形式
Register( const char* funcName, const Callee& callee, int (Callee::*func)(LuaState*), int nupvalues = 0 ); ,提供类实例指针和函数即可完成注册。下面是示例代码:
class CTestCallBack ... { public : int NonVirtualFunc(LuaState * state) ... { LuaStackargs(state); printf( " Innon-virtualmemberfunction.nomsg. " ); return 0 ; } int virtual VirtualFunc(LuaState * state) ... { LuaStackargs(state); printf( " Invirtualmemberfunction.msg=%s " ,args[ 1 ].GetString()); return 0 ; } } ; void TestClassMemberFuncReg() ... { LuaStateOwnerstate; // "print"needthis state -> OpenLibs(); LuaObjectglobalobj = state -> GetGlobals(); CTestCallBacktcb; globalobj.Register( " MemberFunc " ,tcb, & CTestCallBack::NonVirtualFunc); state -> DoString( " MemberFunc() " ); globalobj.Register( " VirMemberFunc " ,tcb, & CTestCallBack::VirtualFunc); state -> DoString( " VirMemberFunc('Hi,myboy') " ); }
修改一下main函数,将
TestClassMemberFuncReg()加进去就可以看效果了。

二、任意形式C++函数注册

LuaPlus提供了 RegisterDirect() 来直接注册任意形式的函数,这样更为直接,不必受限于上述的函数原型,使用起来很方便。同样此函数像Register一样,可以注册类的成员函数(也需要显示指定this指针)。下面是代码:
float Add( float num1, float num2) ... { return num1 + num2; } class CForRegDirect ... { public : int Sum( int a, int b, int c) ... { return a + b + c; } // constisnecessary virtual void SeeMessage( const char * msg) ... { printf( " msg=%s " ,msg); } } ; void TestRegisterDirect() ... { LuaStateOwnerstate; state -> OpenLibs(); LuaObjectgobj = state -> GetGlobals(); // registerglobalfunctiondirectly gobj.RegisterDirect( " Add " ,Add); state -> DoString( " print(Add(1.5,2.3)) " ); // registermemberfunction CForRegDirectforobj; gobj.RegisterDirect( " MemberSum " ,forobj,CForRegDirect::Sum); state -> DoString( " print(MemberSum(1,2,7)) " ); gobj.RegisterDirect( " VirCMsg " ,forobj,CForRegDirect::SeeMessage); state -> DoString( " print(VirCMsg('haha,Doyouseeme?')) " ); }

三、注册函子对象

上面两节的方式可以实现简单的回调注册,注册类的成员函数时需要显式提供类指针,不适合用于映射C++中的类结构。 RegisterObjectFunctor()和元表(metatable)结合,提供了一种新的方法 。我们不需要在注册函数时显式的提供this指针,作为替代,this指针可以从调用者的userdata或__object成员获取。 元表(metatable)是一个普通的表对象,它定义了一些可以被重写的操作,如add,sub,mul,index,call等,这些操作以"__"开头,如__add,__index等。加入你重写了__add,那么在执行add操作时就会调用你自己定义的__add操作。这种特性可以用来模拟C++中的类对象,注册函子对象正是利用了这种特性来实现的。 下面我们将一个C++类映射到Lua中。类代码如下:
class CMultiObject ... { public : CMultiObject( int num):m_num(num) ... { } int Print(LuaState * state) ... { printf( " %d " ,m_num); return 0 ; } protected : int m_num; } ; void TestRegObjectDispatchFunctor() ... { LuaStateOwnerstate; state -> OpenLibs(); // createmetaTable LuaObjectmetaTableObj = state -> GetGlobals().CreateTable( " MultiObjectMetaTable " ); metaTableObj.SetObject( " __index " ,metaTableObj); // registerfunctorformultiobject metaTableObj.RegisterObjectFunctor( " Print " ,CMultiObject::Print); // getainstancesofCMultiObject CMultiObjectobj1( 10 ); // "clone"aobjectinlua,theluaobject(hereistable)hasobj1'sdata LuaObjectobj1Obj = state -> BoxPointer( & obj1); // setluaobject'smetatabletoMetaTableObj obj1Obj.SetMetaTable(metaTableObj); // putluaobjecttoGlobalscope,thusitcanbeaccessedlater. state -> GetGlobals().SetObject( " obj1 " ,obj1Obj); CMultiObjectobj2( 20 ); LuaObjectobj2Obj = state -> BoxPointer( & obj2); obj2Obj.SetMetaTable(metaTableObj); state -> GetGlobals().SetObject( " obj2 " ,obj2Obj); // nowcallPrintandPrint2 state -> DoString( " obj1:Print(); " ); state -> DoString( " obj2:Print(); " ); }
首先我们需要生成一个元表(metatable),将C++类的成员函数注册到该元表中。然后依据CMultiObject的实例生成lua中与其对应的对象(也是表),将该对象的metatable(也即该表的__object成员)设置为之前产生的元表。最后将新生成的lua对象放置到全局作用域中,这样后面就可以直接引用这些对象。 我们可以做这样的近似理解:每个实例的数据元素存放在与已对应的lua table中,而类的成员函数则存放在metatable中(函子对象)。当调用obj1obj:Print()时,会先找到其metatable,然后在metatable中找Print()函数。 这样便实现了类似C++中的类结构。每个实例有自己的数据,而所有实例共享一份方法列表。 另外一种方式是利用表的userdata来实现,需要先创建一个lua表对象,然后将C++对象obj1设置为该表的userdata(也是设置其__object成员),再将该表对象的metatable设置为我们之前创建的元表。最后就可以用表明来调用Print函数。代码如下:
LuaObjecttable1Obj = state -> GetGlobals().CreateTable( " table1 " ); table1Obj.SetLightUserData( " __object " , & obj1); table1Obj.SetMetaTable(metaTableObj); LuaObjecttable2Obj = state -> GetGlobals().CreateTable( " table2 " ); table2Obj.SetLightUserData( " __object " , & obj2); table2Obj.SetMetaTable(metaTableObj); state -> DoString( " table1:Print() " ); state -> DoString( " table2:Print() " );
注册函子对象(RegisterObjectFunctor)这种方式的限制在于:要注册的函数必须符合原型( int Callback(LuaState * state); )。为了打破这种限制,LuaPlus提供了另外一种方式。

四、直接注册函子对象

直接注册函子对象(RegisterObjectDirect)和 RegisterDirect类似,不考虑函数原型,可以直接向元表注册任意形式的函数 。 为CMultiObject添加新的成员函数:
void Print2( int num) ... { printf( " %d%d /n" ,m_num,num); }
调用RegisterObjectDirect方法:
metaTableObj.RegisterObjectDirect( " Print2 " ,(CMultiObject * ) 0 , & CMultiObject::Print2);
第二个参数 (CMultiObject * ) 0有点奇怪,这是模板参数的需要。 最后:
state -> DoString( " obj1:Print2(5) " ); state -> DoString( " obj2:Print2(15) " ); state -> DoString( " table1:Print2(5) " ); state -> DoString( " table2:Print2(15) " );

五、注销回调

注销回调是件简单的事情,调用SetNil("yourCallBack")即可,如:
gobj.SetNil( " Add " ); metaTableObj.SetNil( " Print2 " );
好了,迄今为止最长的一篇,看着像是LuaPlus文档的翻译(?),不过还是加入了一些自己的理解。文档我看了下,琢磨了半天才明白。希望能快点将LuaPlus用起来。 资料: (1) Lua5.1参考手册 (2) Lua入门wiki (3)LuaPlus.html,源码包中带的。 <--!版权所有foruok,转载注明出处!-->

LuaPlus学习(三)


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论