当前位置: BOLT界面引擎 > 知识库文章 > HelloBolt系列教程 > HelloBolt7:与C++代码交互

HelloBolt7:与C++代码交互

作者:Tsukasa

     在控制层与C/C++定义的代码交互。

       点击HelloBolt6中加入的自定义按钮时,调用在C++中定义的加法方法;C++定义的加法方法中回调lua语言定义的回调函数,弹出对话框。

      因为lua语言内建的功能包非常有限,要直接用lua实现程序所有的逻辑功能显然不现实,但是
lua言语提供了完善的与C/C++交互的机制。
     HelloBolt7中,加入core工程,工程设置为生成动态链接库dll。
     MyClass类有一个方法int Add(int lhs,int rhs)简单的将两个数相加。另一个方法int AttachResultListener(DWORD dwUserData1,DWORD dwUserData2,funcResultCallBack pfnCallBack)添加一个回调函数,当调用Add方法时,执行回调函数。
     为了能在lua脚本中与MyClass实例交互,加入对MyClass的lua封装类LuaMyClass以及创建LuaMyClass实例的工厂类LuaMyClassFactory。
     LuaMyClassFactory类中有static int CreateInstance(lua_State* luaState)方法,依托lua本身与C/C++交互的机制,在lua中可以调用的C/C++方法,其原型须为static int function(lua_State。在LuaMyClass.cpp中该方法的定义如下:
         MyClass* pResult = new MyClass();
         XLLRT_PushXLObject(luaState,MY_CLASS_LUA_CLASS,pResult);
         return 1;
以上代码创建一个MyClass实例, 通过XLLuaRuntime的接口PushXLObject将该实例返回到lua虚拟机中,之后可以在C/C++代码中通过如下调用:
MyClass** ppMyClass= reinterpret_cast<MyClass**>(luaL_checkudata(luaState,1,MY_CLASS_LUA_CLASS))
来获取在lua环境中的MyClass实例。
     LuaMyClassFactory的RegisterObj方法定义中:
XLLRT_RegisterGlobalObj(hEnv,theObject)
以上一行代码会向XLLuaRuntime的虚拟机环境中添加一个全局对象,参数XLLRTObject的ClassName字段指定该全局对象的类名,也就是当通过luaL_checkudata在C/C++代码中获取该对象时第三个参数要传入的类名;LuaMyClassFactoryMemberFunctions字段指向一个XLLRTGlobalAPI数组, 以{NULL,NULL}成员为结尾, 每一个元素声明一个该对象在lua中的成员方法,XLLRTGlobalAPI的数组定义如下:
{"CreateInstance",LuaMyClassFactory::CreateInstance},
以上代码声明在lua中在LuaMyClassFactory实例上可以做如下调用:
factory:CreateInstance(),
这样lua虚拟机就会调用到LuaMyClassFactory::CreateInstance的C/C++函数上。调用XLLRT_RegisterGlobalObj之后, 在lua中就可以通过以XLLRTObject的ObjName为参数调用XLGetGlobalObject来获取lua对象的实例了, 此时lua虚拟机中会调用XLLRTObject的pfnGetObject指向的方法来创建该实例。
     LuaMyClass类定义中,MyClass中定义的方法,要使之可以在lua中被调用,在LuaMyClass中对应加入了如下原型为 int function(lua_State*)的静态函数声明:   
static int Add(lua_State* luaState);
static int AttachResultListener(lua_State* luaState);
int LuaMyClass::Add(lua_State* luaState)定义中,首先通过luaL_checkudata获取C/C++对象的实例,之后通过lua的lua_toxxx lua_checkxxx的函数族获取参数,然后在对象实例上以获取的参数调用对象的方法,之后通过lua_pushxxx函数族将返回值返回到lua环境中。
     int LuaMyClass::AttachResultListener(lua_State* luaState)定义中,接受以lua语言定义的函数作为回调函数,通过luaL_ref调用将传入的lua函数存入lua环境的register中,通过void LuaMyClass::LuaListener(DWORD dwUserData1,DWORD dwUserData2,int nResult)调用来调用lua语言定义的函数。在LuaListener的定义中,lua_rawgeti(luaState,LUA_REGISTRYINDEX,dwUserData2 )调用从lua环境的register中获取保存的lua函数,将参数通过lua_pushxxx方法压入lua栈,再通过调用XLLRT_LuaCall完成对lua函数的调用。
     void LuaMyClass::RegisterClass(XL_LRT_ENV_HANDLE hEnv)的定义中,通过调用XLLRT_RegisterClass向LuaMyClass类型上添加LuaMyClassMemberFunctions数组指定的lua方法。
需要注意的一点是,在LuaMyClass中除了声明MyClass中定义的两个方法之外,还额外声明了另一个成员函数如下:
     static int DeleteSelf(lua_State* luaState);
在DeleteSelf方法的定义中,将lua栈上返回的MyClass实例删除;同时在LuaMyClassMemberFunctions
数组中,将DeleteSelf方法声明为名为__gc的lua方法,代码如下:
     {"__gc",LuaMyClass::DeleteSelf},
在lua语言中,以__开头的方法一般为内建方法或成员,__gc方法是lua 中引用类型对象的资源回收方法,因为lua语言是基于垃圾回收的,当一个引用类型对象不再使用时,垃圾回收程序会将该对象从lua环境中释放,并且调用其上的__gc方法来释放该对象占用的资源(参见lua黄书)。当在lua环境中创建LuaMyClass的实例时,在堆上创建了一个MyClass的实例,对称的,当LuaMyClass实例在乱环境中被回收时,需要在__gc方法中从堆上释放对应的MyClass的实例。
     最后在Core.cpp中:
XL_LRT_ENV_HANDLE hEnv = XLLRT_GetEnv(NULL);
        LuaMyClass::RegisterClass(hEnv);
        LuaMyClassFactory::RegisterObj(hEnv);
以上三行代码调用LuaMyClass和LuaMyClassFactory的Register方法向lua环境中注册这两个类型,这样在HelloBolt.cpp中调用LoadLibrary(TEXT("core.dll"))加载core模块之后,就可以在lua脚本中创建MyClass的lua实例并调用其方法了。
     在XAR中MainWnd.xml.lua中的userdefine_btn_OnClick(self)定义中,通过XLGetObject方法获取通过XLLRT_RegisterGlobalObj注册的LuaMyClass全局对象,在其上调用CreateInstance成员方法,创建了LuaMyClass实例,在该实例上调用通过XLLRT_RegisterClass注册的成员方法。
     编译运行,点击按钮,就会执行add方法并且依回调函数的定义弹出对话框。

迅雷公司 版权所有 Copyright 2003-2010 Thunder Inc.All Rights Reserved. 意见反馈:xl7doc@xunlei.com