LuaPlus学习(三)

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

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

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

    一、C风格函数注册

    Lua提供了C风格的回调函数注册,该函数原型如下:
int  Callback(LuaState *  state);
    无论是全局函数、类非虚函数、类虚函数,只要符合上面的原型,都可以向Lua注册。我们以全局函数为例,下面是我们提供的一个回调函数CStyleAddFunc:
int  CStyleAddFunc(LuaState  *  state) {     LuaStack args(state);     if( args[1].IsNumber() && args[2].IsNumber()  )     {         state->PushNumber(args[1].GetInteger() + args[2].GetInteger());         return 1;     }     return 0; }
    在回调函数中,我们通过栈来访问参数,栈中可以存贮多个参数, LuaStack args(state);语句获取栈对象供后续访问。     接下来判断参数是否是数字,如果两个参数都是数字,那么进行加操作,将结果压入栈中,将压入栈中的数据的个数返回。注意,返回值代表压入栈中的元素的个数,而不是某种计算结果或其它意义的返回值。通过改变返回值来查看程序的输出,这样可以对返回值的含义有个感性的了解。     要注册回到只需调用Register函数即可,这在第一篇中已经用到。下面是测试函数:
void  TestCFunctionCallBack() {     LuaStateOwner state;     //"print" need this      state->OpenLibs();     //register my function CStyleAddFunc to Add     state->GetGlobals().Register("Add", CStyleAddFunc);     //call my function and print the result     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)     {         LuaStack args(state);         printf("In non-virtual member function. no msg. ");         return 0;     }     int virtual VirtualFunc(LuaState *state)     {         LuaStack args(state);         printf("In virtual member function.msg=%s ", args[1].GetString());         return 0;     } } ; void  TestClassMemberFuncReg() {     LuaStateOwner state;     //"print" need this      state->OpenLibs();     LuaObject globalobj = state->GetGlobals();     CTestCallBack tcb;     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;     }     //const is necessary     virtual void SeeMessage(const char *msg)     {         printf("msg=%s ", msg);     } } ; void  TestRegisterDirect() {     LuaStateOwner state;     state->OpenLibs();     LuaObject gobj = state->GetGlobals();     //register global function directly     gobj.RegisterDirect("Add", Add);     state->DoString("print(Add(1.5, 2.3))");     //register memberfunction     CForRegDirect forobj;     gobj.RegisterDirect("MemberSum", forobj, CForRegDirect::Sum);     state->DoString("print(MemberSum(1,2,7))");     gobj.RegisterDirect("VirCMsg", forobj, CForRegDirect::SeeMessage);     state->DoString("print(VirCMsg('haha,Do you see me?'))"); }

    三、注册函子对象

    上面两节的方式可以实现简单的回调注册,注册类的成员函数时需要显式提供类指针,不适合用于映射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() {     LuaStateOwner state;     state->OpenLibs();     //create metaTable     LuaObject metaTableObj = state->GetGlobals().CreateTable("MultiObjectMetaTable");     metaTableObj.SetObject("__index", metaTableObj);     //register functor for multiobject     metaTableObj.RegisterObjectFunctor("Print", CMultiObject::Print);     //get a instances of CMultiObject     CMultiObject obj1(10);     //"clone" a object in lua, the lua object(here is table) has obj1's data     LuaObject obj1Obj = state->BoxPointer(&obj1);     //set lua object's metatable to MetaTableObj     obj1Obj.SetMetaTable(metaTableObj);     //put lua object to Global scope, thus it can be accessed later.     state->GetGlobals().SetObject("obj1", obj1Obj);     CMultiObject obj2(20);     LuaObject obj2Obj = state->BoxPointer(&obj2);     obj2Obj.SetMetaTable(metaTableObj);     state->GetGlobals().SetObject("obj2", obj2Obj);     //now call Print and Print2     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函数。代码如下:
    LuaObject table1Obj  =  state -> GetGlobals().CreateTable( " table1 " );     table1Obj.SetLightUserData( " __object " & obj1);     table1Obj.SetMetaTable(metaTableObj);     LuaObject table2Obj  =  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,转载注明出处!-->
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

foruok

你可以选择打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值