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脚本的更多相关文章

  1. SpringBoot第二十四篇:应用监控之Admin

    作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/11457867.html 版权声明:本文为博主原创文章,转载请附上博文链接! 引言   前一章(S ...

  2. Android UI开发第二十四篇——Action Bar

    Action bar是一个标识应用程序和用户位置的窗口功能,并且给用户提供操作和导航模式.在大多数的情况下,当你需要突出展现用户行为或全局导航的activity中使用action bar,因为acti ...

  3. 【转】Android UI开发第二十四篇——Action Bar

    Action bar是一个标识应用程序和用户位置的窗口功能,并且给用户提供操作和导航模式.在大多数的情况下,当你需要突出展现用户行为或全局导航的activity中使用action bar,因为acti ...

  4. 第二十四篇configparser(**)

    configparser模块 config:配置,parser:解析.字面意思理解configparser模块就是配置文件的解析模块. 来看一个好多软件的常见文档格式如下: [DEFAULT] # 标 ...

  5. Python之路【第二十四篇】Python算法排序一

    什么是算法 1.什么是算法 算法(algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出.简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果. ...

  6. 第二十八篇:SOUI中自定义控件开发过程

    在SOUI中已经提供了大部分常用的控件,但是内置控件不可能满足用户的所有要求,因此一个真实的应用少不得还要做一些自定义控件. 学习一个新东西,最简单的办法就是依葫芦画瓢.事实上在SOUI系统中内置控件 ...

  7. JavaScript(第二十四天)【事件对象】

    JavaScript事件的一个重要方面是它们拥有一些相对一致的特点,可以给你的开发提供更多的强大功能.最方便和强大的就是事件对象,他们可以帮你处理鼠标事件和键盘敲击方面的情况,此外还可以修改一般事件的 ...

  8. 第二十四篇-用VideoView制作一个简单的视频播放器

    使用VideoView播放视频,视频路径有三种: 1. SD卡中 2. Android的资源文件中 3. 网络视频 第一种,SD卡中的方法. 路径写绝对路径,如果不能播放,可以赋予读取权限. 效果图: ...

  9. Python之路(第二十四篇) 面向对象初级:多态、封装

    一.多态 多态 多态:一类事物有多种形态,同一种事物的多种形态,动物分为鸡类,猪类.狗类 例子 import abc class H2o(metaclass=abc.ABCMeta): ​ def _ ...

随机推荐

  1. 回调函数中使用MFC类的成员或对话框控件的简单方法

    在MFC的很多程序中,常常需要在回调函数中调用MFC类的类成员变量.类成员函数,亦或者对话框控件的句柄.由于回调函数是基于C编程的Windows SDK的技术,而类成员又有this指针客观条件限制.. ...

  2. 3. javacript高级程序设计-基本概念

    1.1 语法 ECMAScript借鉴了C和其他类C语言的语法 1.1.1 区分大小写 ECMAScript中的一切(变量,函数和操作符)都是区分大小写的,变量test和Test是不同的变量 1.1. ...

  3. 安装VisualSvn Server时遇到的问题

    安装标准版VisualSvnserver,端口443,启用https//,安装过程中报服务启动失败,后用命令行 msiexec /i VisualSVN-Server-2.7.3.msi NO_STA ...

  4. Django~Models2

    Generally, each model maps to a single database table. Each attribute of the model represents a data ...

  5. FIX_前缀后缀_未提交

    问题 B: FIX 时间限制: 1 Sec  内存限制: 64 MB提交: 38  解决: 11[提交][状态][讨论版] 题目描述 如果单词 X 由单词 Y 的前若干个字母构成,我们称 X 是 Y ...

  6. Valentine's Day Round 1001.Ferries Wheel(hdu 5174)解题报告

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5174 题目意思:给出 n 个人坐的缆车值,假设有 k 个缆车,缆车值 A[i] 需要满足:A[i−1] ...

  7. 【leetcode】Largest Number ★

    Given a list of non negative integers, arrange them such that they form the largest number. For exam ...

  8. python基础——装饰器

    python基础——装饰器 由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数. >>> def now(): ... print('2015-3-25 ...

  9. 有关Oracle数据库

    创建数据库(DCA):http://jingyan.baidu.com/article/cbcede07cf42ef02f40b4dc2.html 创建表(连接数据库,sql创建数据表):http:/ ...

  10. PHP常用函数大全。

    php usleep() 函数延迟代码执行若干微秒. unpack() 函数从二进制字符串对数据进行解包. uniqid() 函数基于以微秒计的当前时间,生成一个唯一的 ID. time_sleep_ ...