tolua++初探(六)

    这是学习tolua++的最后一篇了。在这一篇里完成一个稍微复杂一点的例子(^_^其实还是很简单)。     导出三个类CBase、CDerived1、CDerived2到lua,导出两个函数toDerived1、toDerived2。lua脚本中声明两个函数Derived1Test和Derived2Test,我们在C++中调用。Derived1Test和Derived2Test会调用toDerived*对其参数进行向下转换(从CBase转到CDerived*),然后调用派生类的方法做一些测试。     基本上和前面几个例子类似,新增加的部分是在C++中调用Lua脚本里定义的函数。这牵涉到虚拟栈的操作,后面会解释一下。     还是老样子,先把实际的头文件列出来。tlclass.h如下:
#ifndef __TOLUACLASS_h #define  __TOLUACLASS_h class  CBase { public:     CBase(){}     virtual ~CBase(){}          virtual void ShowMessage(){printf("BaseClass ");}     static char * ClassName()return "CBase"; }      } ; class  CDerived1 :  public  CBase { public:     CDerived1(){}     ~CDerived1(){}          void ShowMessage(){printf("Derived1Class ");}     void ShowDerived1(){printf("show derived11111 ");} } ; class  CDerived2 :  public  CBase { public:     CDerived2(){m_nNumber=0;}     ~CDerived2(){}     void ShowMessage(){printf("Derived2Class ");}     void ShowDerived2(){printf("show derived22222 ");}     void SetNumber(int num){ m_nNumber = num; }     int  GetNumber() return m_nNumber; } protected:     int m_nNumber; } ; extern  CDerived1  *  toDerived1( void   *  p); extern  CDerived2  *  toDerived2( void   *  p); #endif
        tlclass.pkg如下:
$#include  " tlclass.h " class  CBase { public:     virtual void ShowMessage();     static char * ClassName();      } ; class  CDerived1 :  public  CBase { public:          void ShowMessage();     void ShowDerived1(); } ; class  CDerived2 :  public  CBase { public:     void ShowMessage();     void ShowDerived2();     void SetNumber(int num);     int  GetNumber();      } ; CDerived1  *  toDerived1( void   *  p); CDerived2  *  toDerived2( void   *  p);
    这次多定义了两个函数toDerived1和toDerived2,全局的。我们也可以把他们直接放在类中,或者一个MODULE中。module大概是类似的namespace的东西,把一堆杂七杂八的家什如变量、常量、函数、类实例等放在一起,在lua中通过"."来访问。下面是手册中的例子:
module mod {  #define N  extern int var;  int func (...): }  
    这样我们可以在lua中用mod.N,mod.var,mod.func来访问其成员。     原本toDerived*的参数是CBase*,但是从C++向Lua函数传参数的时候我调用了lua_pushlightuserdata,结果在脚本中报错,说toDerived*应当接受CBase*而非userdata。于是干脆把参数修改成void*,这下lua不再叫唤了。     好了,到了列出驱动文件的时候了。CallLuaFunc.cpp:
#include  " lua.hpp " #include  " tlclass.h " CDerived1  *  toDerived1( void   *  p) {     return dynamic_cast<CDerived1*>((CBase*)p); } CDerived2  *  toDerived2( void   *  p) {     return dynamic_cast<CDerived2*>((CBase*)p); } int  tolua_calllua_open(lua_State * ); int  _tmain( int  argc, _TCHAR *  argv[]) {     lua_State * L = luaL_newstate();     luaopen_base(L);     tolua_calllua_open(L);     luaL_dofile(L, "../scripts/CallLuaFunc.lua");     //call lua function     CBase * p1 = new CDerived1();     CBase * p2 = new CDerived2();     //call Derived1Test     lua_getglobal(L, "Derived1Test");     lua_pushlightuserdata(L, p1);     if( lua_pcall(L, 100!= 0 )     {         fprintf(stderr, "call Derived1Test failed:%s ", lua_tostring(L, -1));     }     //call Derived2Test     lua_getglobal(L, "Derived2Test");     lua_pushlightuserdata(L, p2);     if( lua_pcall(L, 100!= 0 )     {         fprintf(stderr, "call Derived2Test failed:%s ", lua_tostring(L, -1));     }     printf("This info is print in C++! CDerived2.GetNumber()=%d ", ((CDerived2*)p2)->GetNumber());     delete p1;     delete p2;     lua_close(L);     return 0; }
    这次驱动文件有了点新的变化:1)两个全局导出函数;2)调用lua函数的代码。分开来看。     导出函数toDerived*很简单,只是调用dynamic_cast来向下转换而已。如果转换失败,dynamic_cast会返回null。当我们要从基类指针转换到派生类指针时,最好用dynamci_cast,直接强制转换是危险的,除非你明确的知道某个指针指向的对象是什么。     在C++中调用lua脚本的函数大概分为三步:     a..找到函数并入栈;(这里是 lua_getglobal(L, "Derived1Test");)     b..参数入栈;(这里是 lua_pushlightuserdata(L, p1);)     c..调用lua_pcall进行实际调用     第一步不必说了;第二步可以传递任意个任意类型的参数,lua_pushnumber,lua_pushstring,lua_pushboolean等等可以调用;第三步是调用lua_pcall,lua_pcall第一个参数是lua_State*,这是我们的工作环境了。第二参数是要传递的参数个数,我们这里是1;第三个参数是lua函数返回的结果个数,我们的lua函数不返回结果,设为0。第四个参数是比较复杂,为0时指lua_pcall会在调用失败时把原始错误信息放到栈上;其它值代表栈的索引,该索引处放了一个错误处理函数,lua_pcall失败时会根据这个索引去调用该函数。     调用失败的时候我只是简单地打印一条出错信息,这个错误码放在栈顶,我们用lua_tostring(L,-1)访问并转换为字符串。可以修改下驱动代码,比如把第二次调用传入p1,这样就可以看见错误信息。     最后我还是在C++代码中打印了下CDerived2对象的值,以验证lua和C++中访问的是同一个对象。    Lua和C++的交互都是通过栈,所以要写交互部分的代码就要不停的出栈入栈,烦死个人。不过这也是Lua灵活的地方。牛人啊,顶礼膜拜吧。     有时间要好好研究下《Programming Lua》,CSDN上有中文版的,lua官方网站上有英文的。虽然这个是针对5.0版本的lua,但绝大部分东西还是有用的。针对5.1.3的第二版已经出了,可惜我在网上没有找到链接,哪位看到分享一下。     下面看看lua文件callluafunc.lua吧:
print( " now in CallLuaFunc.lua! " ) -- lua function to test CDerived1, CDerived2, they ' ll be called from C++ function Derived1Test(e)      d1  =  toDerived1(e);      if  d1 then          d1:ShowMessage();          d1:ShowDerived1();       else         print( " invalid d1(nil)! " );     end end function Derived2Test(e)          d2  =  toDerived2(e);       if  d2 then         d2:ShowMessage();          d2:ShowDerived2();          d2:SetNumber( 180 );         print(d2:GetNumber());      else         print( " invalid d2(nil) " );     end end
    lua中定义了函数Derived1Test和Derived2Test。上面的版本已经不会导致出错信息了,原始的Derived*Test函数如下:
function Derived1Test(e)      d1  =  toDerived1(e);     d1:ShowMessage();      d1:ShowDerived1();  end function Derived2Test(e)      d2  =  toDerived2(e);      d2:ShowMessage();      d2:ShowDerived2();      d2:SetNumber( 180 );     print(d2:GetNumber()); end
    原始的Derived*Test函数没有错误检查,所以从C++中用lua_pcall调用时可能会产生错误信息。     又想了下,这样的类型转换太复杂了,tolua++提供了转换机制,可以用的。CEGUI用的就是。     tolua++生成了一些工具函数,在tolua为名的module中。其中tolua.cast就是用来做类型转换的。只需要改动tlclass.pkg文件,加入下面的代码:
$[ testHelper = {} function testHelper.toDerived1(e)      return  tolua.cast(e,  " CDerived1 " ) end function testHelper.toDerived2(e)      return  tolua.cast(e,  " CDerived2 " ) end $]
    $[和$]结合,用来直接插入lua代码。我生成一个名为testHelper的table,给testHelper添加两个转换函数。     重新用tolua++编译,再编译工程。就可以在脚本中调用testHelper.toDerived*来转换了。     下面是更改后的
callluafunc.lua文件:
print( " now in CallLuaFunc.lua! " ) -- lua function to test CDerived1, CDerived2, they ' ll be called from C++ function Derived1Test(e)      d1  =  testHelper.toDerived1(e);      if  d1 then          d1:ShowMessage();          d1:ShowDerived1();       else         print( " invalid d1(nil)! " );     end end function Derived2Test(e)          d2  =  testHelper.toDerived2(e);       if  d2 then         d2:ShowMessage();          d2:ShowDerived2();          d2:SetNumber( 180 );         print(d2:GetNumber());      else         print( " invalid d2(nil) " );     end end
    仅仅是将toDerived*调用转换成了testHelper.toDerived*。运行了一下,结果是正常的。
         好啦好啦,就到这里啦。     通过两天的学习,我已经确定可以在项目中使用tolua++了,它是"AS IS"的,可以用于任何目的。到目前位置所演示的一些特性,可以满足我的需要。     嗯,有些未完成的东西,比如UNICODE、多线程环境下对lua的调用等,慢慢用到了再说吧。    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

foruok

你可以选择打赏

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

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

打赏作者

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

抵扣说明:

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

余额充值