tolua++初探(六)

前端之家收集整理的这篇文章主要介绍了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()

    

    
void ShowMessage(){printf("BaseClass );}

    
staticchar* ClassName()returnCBase; }

    

}
;

 CDerived1 :  public  CBase

:

    CDerived1()

    
CDerived1()

    

    
 ShowMessage()Derived1Class

    
 ShowDerived1()show derived11111

}
 CDerived2 :   CBase

:

    CDerived2()
{m_nNumber=0;}

    
CDerived2()

    
 ShowMessage()Derived2Class

    
 ShowDerived2()show derived22222

    
 SetNumber(int num){ m_nNumber  num; }

    
  GetNumber()  m_nNumber; }

protected:

    
 m_nNumber;

}
extern  CDerived1  *  toDerived1( void    p);

 CDerived2   toDerived2( #endif
        tlclass.pkg如下:
$#include  " tlclass.h

 CBase

:

    
 ShowMessage();

    
 ClassName();

    

}
 CBase

:

    

    
 ShowDerived1();

}
 CBase

 ShowDerived2();

    
 num);

    
  GetNumber();

    

}
;

CDerived1 
 p);

CDerived2 
 p);
    这次多定义了两个函数toDerived1和toDerived2,全局的。我们也可以把他们直接放在类中,或者一个MODULE中。module大概是类似的namespace的东西,把一堆杂七杂八的家什如变量、常量、函数、类实例等放在一起,在lua中通过"."来访问。下面是手册中的例子:
module mod

{

 
#define N

 
extern var;

 
 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 

CDerived1 
 p)

{

    
 dynamic_cast<CDerived1*>((CBase)p);

}

CDerived2 
 p)

CDerived2int  tolua_calllua_open(lua_State );

 _tmain(  argc, _TCHAR  argv[])

@H_404_658@

{

    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  CDerived2();

    
call Derived1Test    lua_getglobal(L,0);">Derived1Test);

    lua_pushlightuserdata(L, p1);

    
if( lua_pcall(L,0);">1,0);">) != )

    
{

        fprintf(stderr,0);">call Derived1Test Failed:%s
-));

    }

    
call Derived2TestDerived2Test

    
 )

    
call Derived2Test Failed:%s

    printf(
This info is print in C++! CDerived2.GetNumber()=%d )p2)->GetNumber());

    delete p1;

    delete p2;

    lua_close(L);

    
;

}

    这次驱动文件有了点新的变化:1)两个全局导出函数;2)调用lua函数代码。分开来看。     导出函数toDerived*很简单,只是调用dynamic_cast来向下转换而已。如果转换失败,dynamic_cast会返回null。当我们要从基类指针转换到派生类指针时,最好用dynamci_cast,直接强制转换是危险的,除非你明确的知道某个指针指向的对象是什么。     在C++中调用lua脚本的函数大概分为三步:     a..找到函数并入栈;(这里是 lua_getglobal(L,0);">);)     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); 

    
 d2 then

        d2:ShowMessage(); 

        d2:ShowDerived2(); 

        d2:SetNumber(
180 );

        print(d2:GetNumber());

    
invalid d2(nil) );

    end

end
    lua中定义了函数Derived1Test和Derived2Test。上面的版本已经不会导致出错信息了,原始的Derived*Test函数如下:
 toDerived1(e);

    d1:ShowMessage(); 

    d1:ShowDerived1(); 

end

function Derived2Test(e) 

    d2   toDerived2(e); 

    d2:ShowMessage(); 

    d2:ShowDerived2(); 

    d2:SetNumber(
);

    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)

    
CDerived2 )

end

$]

    $[和$]结合,用来直接插入lua代码。我生成一个名为testHelper的table,给testHelper添加两个转换函数。     重新用tolua++编译,再编译工程。就可以在脚本中调用testHelper.toDerived*来转换了。     下面是更改后的 callluafunc.lua文件
)

function Derived1Test(e) 

    d1 
 testHelper.toDerived1(e);

    
 d1 then 

        d1:ShowMessage(); 

        d1:ShowDerived1(); 

    

        print(
);

    end

end

function Derived2Test(e) 

        d2 
 testHelper.toDerived2(e); 

    
 d2 then

        d2:ShowMessage(); 

        d2:ShowDerived2(); 

        d2:SetNumber(
);

        print(d2:GetNumber());

    
);

    end

end
    仅仅是将toDerived*调用转换成了testHelper.toDerived*。运行了一下,结果是正常的。          好啦好啦,就到这里啦。     通过两天的学习,我已经确定可以在项目中使用tolua++了,它是"AS IS"的,可以用于任何目的。到目前位置所演示的一些特性,可以满足我的需要。     嗯,有些未完成的东西,比如UNICODE、多线程环境下对lua的调用等,慢慢用到了再说吧。    

猜你在找的Lua相关文章