第二十四篇:导出SOUI对象到LUA脚本
LUA是一种体积小,速度快的脚本语言。脚本语言虽然性能上和C++这样的Naitive语言相比差一点,但是开发速度快,可以方便的更新代码等,近年来受到了越来越多开发者的重视。
在SOUI框架中,我把脚本模块参考CEGUI抽象出一个独立的脚本接口,方便实现各种脚本语言的对接。
下面简单介绍一下在SOUI中实现的LUA脚本模块的实现。
在客户端程序中使用脚本语言一个基本的需求就是C++代码和脚本代码的相互调用,即C++代码可以调用脚本代码,脚本代码也要能够方便的调用C++代码。
LUA脚本原生提供了访问C函数的方法,只需要简单的调用几行代码就可以方便的把C函数注册到LUA函数空间中,但是并没有原生提供访问C++对象的能力。
但是LUA中实现的metatable能够很好的模拟C++的OOP能力,这也为导出C++对象到LUA提供了可能。
目前已经有很多方法可以将C++对象导出到LUA,比如luabind,tolua++, fflua及本文中用到的lua_tinker。
luabind据说体积比较大,tolua++好像已经没人维护了,目前只支持lua 5.1.4,fflua是国内一个大神的作品,使用简单,只是我使用中碰到一点问题,最后还是选择了lua_tinker。
lua_tinker是一个韩国大神的作品,虽然作者本人没有维护了,但是代码相对比较简单易懂,国内有不少高手都对它进行了扩展。
在SOUI中使用的是官方的0.5c版本上结合网友修改的版本,实现对lua 5.2.3的支持。
言归正传,下面说说如何使用lua_tinker导出SOUI对外到LUA。
要使用LUA,首先当然要有一份LUA内核代码,这里用的lua 5.2.3。
为了在SOUI中使用LUA,我们还需要使用LUA内核实现一个SOUI::IScriptModuler接口:
namespace SOUI
{
class SWindow;
/*!
\brief
Abstract interface required for all scripting support modules to be used with
the SOUI system.
*/
struct IScriptModule : public IObjRef
{
/**
* GetScriptEngine
* @brief 获得脚本引擎的指针
* @return void * -- 脚本引擎的指针
* Describe
*/
virtual void * GetScriptEngine () = ; /*************************************************************************
Abstract interface
*************************************************************************/
/*!
\brief
Execute a script file. \param pszScriptFile
String object holding the filename of the script file that is to be executed */
virtual void executeScriptFile(LPCSTR pszScriptFile) = ; /*!
\brief
Execute a script buffer. \param buff
buffer of the script that is to be executed \param sz
size of buffer
*/
virtual void executeScriptBuffer(const char* buff, size_t sz) = ;
/*!
\brief
Execute script code contained in the given String object. \param str
String object holding the valid script code that should be executed. \return
Nothing.
*/
virtual void executeString(LPCSTR str) = ; /*!
\brief
Execute a scripted global 'event handler' function. The function should take some kind of EventArgs like parameter
that the concrete implementation of this function can create from the passed EventArgs based object. \param handler_name
String object holding the name of the scripted handler function. \param EventArgs *pEvt
SWindow based object that should be passed, by any appropriate means, to the scripted function. \return
- true if the event was handled.
- false if the event was not handled.
*/
virtual bool executeScriptedEventHandler(LPCSTR handler_name, EventArgs *pEvt)=; /*!
\brief
Return identification string for the ScriptModule. If the internal id string has not been
set by the ScriptModule creator, a generic string of "Unknown scripting module" will be returned. \return
String object holding a string that identifies the ScriptModule in use.
*/
virtual LPCSTR getIdentifierString() const = ; /*!
\brief
Subscribes or unsubscribe the named Event to a scripted function \param target
The target EventSet for the subscription. \param uEvent
Event ID to subscribe to. \param subscriber_name
String object containing the name of the script function that is to be subscribed to the Event. \return
*/
virtual bool subscribeEvent(SWindow* target, UINT uEvent, LPCSTR subscriber_name) = ; /**
* unsubscribeEvent
* @brief 取消事件订阅
* @param SWindow * target -- 目标窗口
* @param UINT uEvent -- 目标事件
* @param LPCSTR subscriber_name -- 脚本函数名
* @return bool -- true操作成功
* Describe
*/
virtual bool unsubscribeEvent(SWindow* target, UINT uEvent, LPCSTR subscriber_name ) = ; }; struct IScriptFactory : public IObjRef
{
virtual HRESULT CreateScriptModule(IScriptModule ** ppScriptModule) = ;
}; }
实现上述接口后,SOUI就可以用这个接口和脚本交互。
导出SOUI对象通常应该在IScriptModule的实现类的构造中执行。
使用lua_tinker导出C++对象非常简单,下面看一下scriptmodule-lua是如何导出SOUI中使用的几个C++对象的:
//导出基本结构体类型
UINT rgb(int r,int g,int b)
{
return RGBA(r,g,b,);
} UINT rgba(int r,int g, int b, int a)
{
return RGBA(r,g,b,a);
} BOOL ExpLua_Basic(lua_State *L)
{
try{
lua_tinker::def(L,"RGB",rgb);
lua_tinker::def(L,"RGBA",rgba); //POINT
lua_tinker::class_add<POINT>(L,"POINT");
lua_tinker::class_mem<POINT>(L, "x", &POINT::x);
lua_tinker::class_mem<POINT>(L, "y", &POINT::y);
//RECT
lua_tinker::class_add<RECT>(L,"RECT");
lua_tinker::class_mem<RECT>(L, "left", &RECT::left);
lua_tinker::class_mem<RECT>(L, "top", &RECT::top);
lua_tinker::class_mem<RECT>(L, "right", &RECT::right);
lua_tinker::class_mem<RECT>(L, "bottom", &RECT::bottom);
//SIZE
lua_tinker::class_add<SIZE>(L,"SIZE");
lua_tinker::class_mem<SIZE>(L, "cx", &SIZE::cx);
lua_tinker::class_mem<SIZE>(L, "cy", &SIZE::cy); //CPoint
lua_tinker::class_add<CPoint>(L,"CPoint");
lua_tinker::class_inh<CPoint,POINT>(L);
lua_tinker::class_con<CPoint>(L,lua_tinker::constructor<CPoint,LONG,LONG>);
//CRect
lua_tinker::class_add<CRect>(L,"CRect");
lua_tinker::class_inh<CRect,RECT>(L);
lua_tinker::class_con<CRect>(L,lua_tinker::constructor<CRect,LONG,LONG,LONG,LONG>);
lua_tinker::class_def<CRect>(L,"Width",&CRect::Width);
lua_tinker::class_def<CRect>(L,"Height",&CRect::Height);
lua_tinker::class_def<CRect>(L,"Size",&CRect::Size);
lua_tinker::class_def<CRect>(L,"IsRectEmpty",&CRect::IsRectEmpty);
lua_tinker::class_def<CRect>(L,"IsRectNull",&CRect::IsRectNull);
lua_tinker::class_def<CRect>(L,"PtInRect",&CRect::PtInRect);
lua_tinker::class_def<CRect>(L,"SetRectEmpty",&CRect::SetRectEmpty);
lua_tinker::class_def<CRect>(L,"OffsetRect",(void (CRect::*)(int,int))&CRect::OffsetRect); //CSize
lua_tinker::class_add<CSize>(L,"CSize");
lua_tinker::class_inh<CSize,SIZE>(L);
lua_tinker::class_con<CSize>(L,lua_tinker::constructor<CSize,LONG,LONG>); return TRUE;
}catch(...)
{
return FALSE;
} }
#include <core/swnd.h> //定义一个从SObject转换成SWindow的方法
SWindow * toSWindow(SObject * pObj)
{
return sobj_cast<SWindow>(pObj);
} BOOL ExpLua_Window(lua_State *L)
{
try{
lua_tinker::def(L,"toSWindow",toSWindow); lua_tinker::class_add<SWindow>(L,"SWindow");
lua_tinker::class_inh<SWindow,SObject>(L);
lua_tinker::class_con<SWindow>(L,lua_tinker::constructor<SWindow>);
lua_tinker::class_def<SWindow>(L,"GetContainer",&SWindow::GetContainer);
lua_tinker::class_def<SWindow>(L,"GetRoot",&SWindow::GetRoot);
lua_tinker::class_def<SWindow>(L,"GetTopLevelParent",&SWindow::GetTopLevelParent);
lua_tinker::class_def<SWindow>(L,"GetParent",&SWindow::GetParent);
lua_tinker::class_def<SWindow>(L,"DestroyChild",&SWindow::DestroyChild);
lua_tinker::class_def<SWindow>(L,"GetChildrenCount",&SWindow::GetChildrenCount);
lua_tinker::class_def<SWindow>(L,"FindChildByID",&SWindow::FindChildByID);
lua_tinker::class_def<SWindow>(L,"FindChildByNameA",(SWindow* (SWindow::*)(LPCSTR,int))&SWindow::FindChildByName);
lua_tinker::class_def<SWindow>(L,"FindChildByNameW",(SWindow* (SWindow::*)(LPCWSTR,int ))&SWindow::FindChildByName);
lua_tinker::class_def<SWindow>(L,"CreateChildrenFromString",(SWindow* (SWindow::*)(LPCWSTR))&SWindow::CreateChildren);
lua_tinker::class_def<SWindow>(L,"GetTextAlign",&SWindow::GetTextAlign);
lua_tinker::class_def<SWindow>(L,"GetWindowRect",(void (SWindow::*)(LPRECT))&SWindow::GetWindowRect);
lua_tinker::class_def<SWindow>(L,"GetWindowRect2",(CRect (SWindow::*)())&SWindow::GetWindowRect);
lua_tinker::class_def<SWindow>(L,"GetClientRect",(void (SWindow::*)(LPRECT))&SWindow::GetClientRect);
lua_tinker::class_def<SWindow>(L,"GetClientRect2",(CRect (SWindow::*)())&SWindow::GetClientRect);
lua_tinker::class_def<SWindow>(L,"GetWindowText",&SWindow::GetWindowText);
lua_tinker::class_def<SWindow>(L,"SetWindowText",&SWindow::SetWindowText);
lua_tinker::class_def<SWindow>(L,"SendSwndMessage",&SWindow::SSendMessage);
lua_tinker::class_def<SWindow>(L,"GetID",&SWindow::GetID);
lua_tinker::class_def<SWindow>(L,"SetID",&SWindow::SetID);
lua_tinker::class_def<SWindow>(L,"GetUserData",&SWindow::GetUserData);
lua_tinker::class_def<SWindow>(L,"SetUserData",&SWindow::SetUserData);
lua_tinker::class_def<SWindow>(L,"GetName",&SWindow::GetName);
lua_tinker::class_def<SWindow>(L,"GetSwnd",&SWindow::GetSwnd);
lua_tinker::class_def<SWindow>(L,"InsertChild",&SWindow::InsertChild);
lua_tinker::class_def<SWindow>(L,"RemoveChild",&SWindow::RemoveChild);
lua_tinker::class_def<SWindow>(L,"IsChecked",&SWindow::IsChecked);
lua_tinker::class_def<SWindow>(L,"IsDisabled",&SWindow::IsDisabled);
lua_tinker::class_def<SWindow>(L,"IsVisible",&SWindow::IsVisible);
lua_tinker::class_def<SWindow>(L,"SetVisible",&SWindow::SetVisible);
lua_tinker::class_def<SWindow>(L,"EnableWindow",&SWindow::EnableWindow);
lua_tinker::class_def<SWindow>(L,"SetCheck",&SWindow::SetCheck);
lua_tinker::class_def<SWindow>(L,"SetOwner",&SWindow::SetOwner);
lua_tinker::class_def<SWindow>(L,"GetOwner",&SWindow::GetOwner);
lua_tinker::class_def<SWindow>(L,"Invalidate",&SWindow::Invalidate);
lua_tinker::class_def<SWindow>(L,"InvalidateRect",(void (SWindow::*)(LPCRECT))&SWindow::InvalidateRect);
lua_tinker::class_def<SWindow>(L,"AnimateWindow",&SWindow::AnimateWindow);
lua_tinker::class_def<SWindow>(L,"GetScriptModule",&SWindow::GetScriptModule);
lua_tinker::class_def<SWindow>(L,"Move2",(void (SWindow::*)(int,int,int,int))&SWindow::Move);
lua_tinker::class_def<SWindow>(L,"Move",(void (SWindow::*)(LPCRECT))&SWindow::Move);
lua_tinker::class_def<SWindow>(L,"FireCommand",&SWindow::FireCommand);
lua_tinker::class_def<SWindow>(L,"GetDesiredSize",&SWindow::GetDesiredSize);
lua_tinker::class_def<SWindow>(L,"GetWindow",&SWindow::GetWindow); return TRUE;
}catch(...)
{
return FALSE;
}
}
还是很简单吧?!
这里有两点需要注意:
前面的代码里一般是导出全局函数,成员函数及成员变量,但是类的静态成员函数是不能用上面的方法导出的,下面看一下静态函数如何处理:
BOOL ExpLua_App(lua_State *L)
{
try{
lua_tinker::class_add<SApplication>(L,"SApplication");
lua_tinker::class_def<SApplication>(L,"AddResProvider",&SApplication::AddResProvider);
lua_tinker::class_def<SApplication>(L,"RemoveResProvider",&SApplication::RemoveResProvider);
lua_tinker::class_def<SApplication>(L,"Init",&SApplication::Init);
lua_tinker::class_def<SApplication>(L,"GetInstance",&SApplication::GetInstance);
lua_tinker::class_def<SApplication>(L,"CreateScriptModule",&SApplication::CreateScriptModule);
lua_tinker::class_def<SApplication>(L,"SetScriptModule",&SApplication::SetScriptFactory);
lua_tinker::class_def<SApplication>(L,"GetTranslator",&SApplication::GetTranslator);
lua_tinker::class_def<SApplication>(L,"SetTranslator",&SApplication::SetTranslator);
lua_tinker::def(L,"theApp",&SApplication::getSingletonPtr); return TRUE;
}catch(...)
{
return FALSE;
}
}
注意上面导出SApplication::getSingletonPtr使用的方法,实际使用的是和导出全局函数一样的方法,因此在脚本中调用的时候也只能和全局函数一样调用,这一点和C++调用静态函数是不同的。
第二个需要注意的地方就是,使用lua_tinker导出的C++类如果是多继承的,那么只能导出一个基类,而且这个基类必须是第一个基类。
例如SWindow类,它从多个基类继承而来,但只能使用lua_tinker::class_inh来声明第一个基类SObject,如果把SWindow的继承顺序调整一下,在LUA脚本里获得SWindow对象后也访问不了SObject的方法,这一点需要特别注意。
注:上面这个问题是我搜索好长时间才发现的,但也没有完全解决问题,本来想在导出SHostWnd时声明继承自SWindow,尽管把SWindow放到了继承的第一位,但是在LUA脚本中用SHostWnd对象访问SWindow方法仍然失败,不知道什么原因,有兴趣的朋友可以研究一下。
上面介绍了如何导出C++对象到LUA空间,下面介绍一下在LUA脚本中如何使用这些C++对象:
所有的C++对象导出到LUA后都将对应一个metatable,可以使用"."来访问table中的成员变量(映射了C++对象的成员变量),也可以使用“:”来访问table中的成员函数(映射了C++对象函数),全局函数则直接使用函数名调用。
例如上面导出的CRect对象,在LUA脚本中使用如下:
function test(arg)
local rc = CRect(,,,);
local wid = rc:Width(); --访问成员函数Width()
local x1 = rc.left;--访问基类对象RECT的成员变量left
end
更多操作请参考SOUI的demo
第二十四篇:导出SOUI对象到LUA脚本的更多相关文章
- SpringBoot第二十四篇:应用监控之Admin
作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/11457867.html 版权声明:本文为博主原创文章,转载请附上博文链接! 引言 前一章(S ...
- Android UI开发第二十四篇——Action Bar
Action bar是一个标识应用程序和用户位置的窗口功能,并且给用户提供操作和导航模式.在大多数的情况下,当你需要突出展现用户行为或全局导航的activity中使用action bar,因为acti ...
- 【转】Android UI开发第二十四篇——Action Bar
Action bar是一个标识应用程序和用户位置的窗口功能,并且给用户提供操作和导航模式.在大多数的情况下,当你需要突出展现用户行为或全局导航的activity中使用action bar,因为acti ...
- 第二十四篇configparser(**)
configparser模块 config:配置,parser:解析.字面意思理解configparser模块就是配置文件的解析模块. 来看一个好多软件的常见文档格式如下: [DEFAULT] # 标 ...
- Python之路【第二十四篇】Python算法排序一
什么是算法 1.什么是算法 算法(algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出.简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果. ...
- 第二十八篇:SOUI中自定义控件开发过程
在SOUI中已经提供了大部分常用的控件,但是内置控件不可能满足用户的所有要求,因此一个真实的应用少不得还要做一些自定义控件. 学习一个新东西,最简单的办法就是依葫芦画瓢.事实上在SOUI系统中内置控件 ...
- JavaScript(第二十四天)【事件对象】
JavaScript事件的一个重要方面是它们拥有一些相对一致的特点,可以给你的开发提供更多的强大功能.最方便和强大的就是事件对象,他们可以帮你处理鼠标事件和键盘敲击方面的情况,此外还可以修改一般事件的 ...
- 第二十四篇-用VideoView制作一个简单的视频播放器
使用VideoView播放视频,视频路径有三种: 1. SD卡中 2. Android的资源文件中 3. 网络视频 第一种,SD卡中的方法. 路径写绝对路径,如果不能播放,可以赋予读取权限. 效果图: ...
- Python之路(第二十四篇) 面向对象初级:多态、封装
一.多态 多态 多态:一类事物有多种形态,同一种事物的多种形态,动物分为鸡类,猪类.狗类 例子 import abc class H2o(metaclass=abc.ABCMeta): def _ ...
随机推荐
- 转: 认识 HTTPS
HTTPS简介 HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版. ...
- 使用Java的多线程和IO流写一个文件复制功能类
创建一个复制功能类,继承Thread类,重写run()方法,把FileInputStream和FileOutputStream输入输出流写在run()方法内.示例代码如下: import java.i ...
- Effective C++ -----条款48:认识template元编程
Template metaprogramming(TMP,模板元编程)可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率. TMP可被用来生成“基于政策选择组合”(based on ...
- C#实现MD5加密
摘自:http://blog.csdn.net/shenghui188/archive/2010/03/28/5423959.aspx 方法一 首先,先简单介绍一下MD5 MD5的全称是message ...
- 【leetcode】Linked List Cycle II (middle)
Given a linked list, return the node where the cycle begins. If there is no cycle, return null. Foll ...
- 【剑指offer】题目20 顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出 ...
- 字符串与byte数组转换
string weclome=""; byte[] data = new byte[1024]; //字符串转byte数组 data = Encoding.ASCII.GetByt ...
- io流对文件读写操作
public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedRead ...
- Json数据报错
在Json数据解析的时候报错,如下图: 这里的id,code,A1等等,都应该使用双引号,即:"id","code","A1",假如id后面 ...
- Xcode常用代码块
Xcode的代码片段(Code Snippets)创建自定义的代码片段,当你重用这些代码片段时,会给你带来很大的方便. 常用的: 1.strong:@property (nonatomic,stron ...