Lua脚本在C++下的舞步
我是一名C++程序员,所以在很多时候,不想过多的使用Lua的特性,因为个人感觉,Lua的语法要比C++的更加灵活。而我更希望,在函数调用的某些习惯上,遵循一些C++的规则。
好了,废话少说,我们先来看一个类(头文件)。假设我们要把这个对象,传输给Lua进行调用。
#define _TEST_H
class CTest
{
public:
CTest(void);
~CTest(void);
char* GetData();
void SetData(const char* pData);
private:
char m_szData[200];
};
#endif
这个类里面有两个函数,一个是GetData(),一个是SetData(),之所以这么写,我要让Lua不仅能使用我的类,还可以给这个类使用参数。
那么,cpp文件,我们姑且这样写。(当然,你可以进行修改,按照你喜欢的方式写一个方法,呵呵)
{
printf(“[CTest::GetData]%s./n”, m_szData);
return m_szData;
}
void CTest::SetData(const char* pData)
{
sprintf(m_szData, “%s”, pData);
}
这是一个标准的类,我需要这个类在Lua里面可以创造出来,并赋予数值,甚至我可以把CTest作为一个Lua函数参数,传给Lua函数让它去给我
处理。让我们来看看怎么做。如果使用标准的Lua语法,有点多,所以我就借用一下上次提到的tolua来做到这一切,我一句句的解释。姑且我们把这些代码
放在LuaFn.cpp里面。
{
CTest* pTest = new CTest();
tolua_pushusertype(pState, pTest, “CTest”);
return 1;
}
static int tolua_delete_CTest(lua_State* pState)
{
CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);
if(NULL != pTest)
{
delete pTest;
}
return 1;
}
static int tolua_SetData_CTest(lua_State* pState)
{
CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);
const char* pData = tolua_tostring(pState, 2, 0);
if(pData != NULL && pTest != NULL)
{
pTest->SetData(pData);
}
return 1;
}
static int tolua_GetData_CTest(lua_State* pState)
{
CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);
if(pTest != NULL)
{
char* pData = pTest->GetData();
tolua_pushstring(pState, pData);
}
return 1;
}
看看这几个静态函数在干什么。
我要在Lua里面使用CTest,必须让Lua里这个CTest对象能够顺利的创造和销毁。tolua_new_CTest()和tolua_delete_CTest()就是干这个的。
tolua_pushusertype(pState, pTest, “CTest”); 这句话的意思是,将一个已经在Lua注册的”CTest”对象指针,压入数据栈。
同理,CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);是将数据栈下的对象以(CTest* )的指针形式弹出来。
tolua_SetData_CTest()
函数和tolua_GetData_CTest分别对应CTest的SetData方法和GetData()方法。因为我们的SetData方法里面存在
变量,那么同样,我们需要使用const char* pData = tolua_tostring(pState, 2,
0);将参数弹出来,然后输入到pTest->SetData(pData);对象中去,当然,你可以有更多若干个参数。随你的喜好。这里只做一个
举例。
好了,你一定会问,这么多的静态函数,用在哪里?呵呵,当然是给Lua注册,当你把这些数据注册到Lua里面,你就可以轻松的在Lua中使用它们。
让我们看看,注册是怎么做到的。
还是在CLuaFn类里面,我们增加一个函数。比如叫做bool InitClass();
{
if(NULL == m_pState)
{
printf(“[CLuaFn::InitClass]m_pState is NULL./n”);
return false;
}
tolua_open(m_pState);
tolua_module(m_pState, NULL, 0);
tolua_beginmodule(m_pState, NULL);
tolua_usertype(m_pState, “CTest”);
tolua_cclass(m_pState, “CTest”, “CTest”, “”, tolua_delete_CTest);
tolua_beginmodule(m_pState, “CTest”);
tolua_function(m_pState, “new”, tolua_new_CTest);
tolua_function(m_pState, “SetData”, tolua_SetData_CTest);
tolua_function(m_pState, “GetData”, tolua_GetData_CTest);
tolua_endmodule(m_pState);
tolua_endmodule(m_pState);
return true;
}
上面的代码,就是我把上面的几个静态函数,绑定到Lua的基础对象中去。
tolua_beginmodule(m_pState,
“CTest”);是只注册一个模块,比如,我们管CTest叫做”CTest”,保持和C++的名称一样。这样在Lua的对象库中就会多了一个
CTest的对象描述,等同于string,number等等基本类型,同理,你也可以用同样的方法,注册你的MFC类。是不是有点明白了?这里要注
意,tolua_beginmodule()和tolua_endmodule()对象必须成对出现,如果出现不成对的,你注册的C++类型将会失败。
tolua_function(m_pState, “SetData”, tolua_SetData_CTest);指的是将Lua里面CTest对象的”SetData”绑定到你的tolua_SetData_CTest()函数中去。
好的,让我们来点激动人心的东西。还记得我们的Simple.lua的文件么。我们来改一下它。
local test = CTest:new();
test:SetData(“I’m freeeyes!”);
test:GetData();
return x..y;
end
我在这个函数里面,New了一个CTest对象,并进行赋值操作,最后把结果打印在屏幕上。你或许会问,最后一句不是x+y么,怎么变成了
x..y,呵呵,在Lua中,..表示联合的意思,就好比在C++里面, string strName +=
“freeeyes”。原来觉得x+y有点土,索性返回一个两个字符串的联合吧。
好了,我们已经把我们的这个CTest类注册到了Lua里面,让我们来调用一下吧。修改一下Main函数。变成以下的样子。
{
CLuaFn LuaFn;
LuaFn.InitClass();
LuaFn.LoadLuaFile(“Sample.lua”);
CParamGroup ParamIn;
CParamGroup ParamOut;
char szData1[20] = {‘/0′};
sprintf(szData1, “[freeeyes]“);
_ParamData* pParam1 = new _ParamData(szData1, “string”, (int)strlen(szData1));
ParamIn.Push(pParam1);
char szData2[20] = {‘/0′};
sprintf(szData2, “[shiqiang]“);
_ParamData* pParam2 = new _ParamData(szData2, “string”, (int)strlen(szData2));
ParamIn.Push(pParam2);
char szData3[40] = {‘/0′};
_ParamData* pParam3 = new _ParamData(szData3, “string”, 40);
ParamOut.Push(pParam3);
LuaFn.CallFileFn(“func_Add”, ParamIn, ParamOut);
char* pData = (char* )ParamOut.GetParam(0)->GetParam();
printf(“[Main]Sum = %s./n”, pData);
getchar();
return 0;
}
如果你完全按照我的,你就可以编译你的工程了,运行一下,看看是啥结果?
[CTest::GetData]I’m freeeyes!.
[Main]Sum = [freeeyes][shiqiang].
看看,是不是和我输出的一样?
呵呵,有意思吧,你已经可以在Lua里面用C++的函数了,那么咱们再增加一点难度,比如,我有一个CTest对象,要作为一个参数,传输给func_Add()执行,怎么办?
很简单,如果你对上面的代码仔细阅读,你会发现下面的代码一样简洁。为了支持刚才要说的需求,我们需要把Sample.lua再做一点修改。
f:SetData(“I’m freeeyes!”);
f:GetData();
return x..y;
end
f假设就是我们要传入的CTest对象。我们要在Lua里面使用它。(我们的CLuaFn都不用改,把main函数稍微改一下即可,来看看怎么写。)
// LuaSample.cpp : 定义控制台应用程序的入口点。
//
#include “LuaFn.h”
int _tmain(int argc, _TCHAR* argv[])
{
CLuaFn LuaFn;
LuaFn.InitClass();
LuaFn.LoadLuaFile(“Sample.lua”);
CParamGroup ParamIn;
CParamGroup ParamOut;
char szData1[20] = {‘/0′};
sprintf(szData1, “[freeeyes]“);
_ParamData* pParam1 = new _ParamData(szData1, “string”, (int)strlen(szData1));
ParamIn.Push(pParam1);
char szData2[20] = {‘/0′};
sprintf(szData2, “[shiqiang]“);
_ParamData* pParam2 = new _ParamData(szData2, “string”, (int)strlen(szData2));
ParamIn.Push(pParam2);
//只追加了这里
CTest* pTest = new CTest();
_ParamData* pParam3 = new _ParamData(pTest, “CTest”, sizeof(CTest));
ParamIn.Push(pParam3);
//追加结束
char szData4[40] = {‘/0′};
_ParamData* pParam4 = new _ParamData(szData4, “string”, 40);
ParamOut.Push(pParam4);
LuaFn.CallFileFn(“func_Add”, ParamIn, ParamOut);
char* pData = (char* )ParamOut.GetParam(0)->GetParam();
printf(“[Main]Sum = %s./n”, pData);
getchar();
return 0;
}
好了,就这么点代码,改好了,我们再Build一下,然后点击运行。看看输出结果,是不是和以前的一样?
恩,是不是有点兴奋了?你成功的让Lua开始调用你的C++对象了!并且按照你要的方式执行!还记得我曾在第一篇文章里面许诺过,我会让你画出一个MFC窗体么?呵呵,如果你到现在依然觉得很清晰的话,说明你的距离已经不远了。
既然已经到了这里,我们索性再加点难度,如果我要把CTest作为一个对象返回回来怎么做?很简单,且看。
{
CLuaFn LuaFn;
LuaFn.InitClass();
LuaFn.LoadLuaFile(“Sample.lua”);
CParamGroup ParamIn;
CParamGroup ParamOut;
char szData1[20] = {‘/0′};
sprintf(szData1, “[freeeyes]“);
_ParamData* pParam1 = new _ParamData(szData1, “string”, (int)strlen(szData1));
ParamIn.Push(pParam1);
char szData2[20] = {‘/0′};
sprintf(szData2, “[shiqiang]“);
_ParamData* pParam2 = new _ParamData(szData2, “string”, (int)strlen(szData2));
ParamIn.Push(pParam2);
CTest* pTest = new CTest();
_ParamData* pParam3 = new _ParamData(pTest, “CTest”, sizeof(CTest));
ParamIn.Push(pParam3);
CTest* pTestRsult = NULL;
_ParamData* pParam4 = new _ParamData(pTestRsult, “CTest”, sizeof(pTestRsult));
ParamOut.Push(pParam4);
LuaFn.CallFileFn(“func_Add”, ParamIn, ParamOut);
//接受Lua返回参数为CTest类型,并调用其中的方法。
pTestRsult = (CTest* )ParamOut.GetParam(0)->GetParam();
pTestRsult->GetData();
getchar();
return 0;
}
好,编译,执行。呵呵,看到了吧。
看到这里,如果你能看的明白,说明你已经对Lua如何调用C++接口,以及C++如何调用Lua有了一定的理解。当然,我写的这个类也不是很完善,不过做一半的Lua开发,应该是够用了。以以上的方式,你可以使用Lua驾驭你的C++代码。
好了,咱们既然已经说到这里了,再深一步,如果我的类是继承的,怎么办?呵呵,很好的问题。
比如,我的CTest继承了一个CBase,我的CBase又继承了一个。。。
在Lua里面,一样简单,我拿MFC的例子来举例吧,想必大家更喜欢看。
比如 CCmdTarget继承自CObject。
那么我在注册的时候可以这么写。
tolua_cclass(tolua_S, “CCmdTarget”, ”CCmdTarget”, ”CObject”, NULL);
这个表示CCmdTarget继承自CObject对象。
当然,MFC里面还会有很多类型,比如常数,Lua一样能处理。
举个例子说。
tolua_constant(tolua_S, “ES_AUTOHSCROLL”, ES_AUTOHSCROLL);
这样注册,你就可以在 Lua里面使用ES_AUTOHSCROLL这个常数,它会自动绑定ES_AUTOHSCROLL这个C++常数对象。
呵呵,说了这么多,让我们来点实际的。我给大家一个我以前写的MFC封装类(由于代码太多,我变成附件给大家),你们可以调用,当然,如果你有兴趣,就用我的MFC类,来做一个你喜欢的窗体吧,当然,你必须要用Lua脚本把它画出来,作为最后的考验,呵呵。
Lua脚本在C++下的舞步的更多相关文章
- Win32下 Qt与Lua交互使用(二):在Lua脚本中使用Qt类
话接上篇.成功配置好Qt+Lua+toLua后,我们可以实现在Lua脚本中使用各个Qt的类.直接看代码吧. #include "include/lua.hpp" #include ...
- Learning Lua Programming (3) iMac下搭建Lua脚本最好的编码环境(代码补全,编译运行)
这篇文章参考自http://blog.sina.com.cn/s/blog_991afe570101rdgf.html,十分感谢原作者的伟大创造,本人亲测可行. 这篇文章记录一下如何在MAC系统环境下 ...
- Redis进阶实践之八Lua的Cjson在Linux下安装、使用和用C#调用Lua脚本
一.引言 学习Redis也有一段时间了,感触还是颇多的,但是自己很清楚,路还很长,还要继续.上一篇文章简要的介绍了如何在Linux环境下安装Lua,并介绍了在Linux环境下如何编写L ...
- Windows下为Lua脚本进行加密处理
缘由 想对Lua脚本进行安全性处理,可惜一直没有想到很好的解决方案,考虑过用原生Lua将脚本编译成二进制代码,也考虑过用zlib将文件进行加密压缩处理,但是感觉都不是最佳方案,今天忽然想到有个东西叫L ...
- Win32下 Qt与Lua交互使用(四):在Lua脚本中自由执行Qt类中的函数
话接上篇.通过前几篇博客,我们实现在Lua脚本中执行Qt类中函数的方法,以及在Lua脚本中连接Qt对象的信号与槽. 但是,我们也能发现,如果希望在Lua脚本中执行Qt类的函数,就必须绑定一个真正实现功 ...
- Win32下 Qt与Lua交互使用(三):在Lua脚本中connect Qt 对象
话接上文.笔者为了方便使用Lua,自己编写了一个Lua的类.主要代码如下: QLua.h #ifndef QLUA_H #define QLUA_H // own #include "inc ...
- 在redis中使用lua脚本让你的灵活性提高5个逼格
在redis的官网上洋洋洒洒的大概提供了200多个命令,貌似看起来很多,但是这些都是别人预先给你定义好的,但你却不能按照自己的意图进行定制, 所以是不是感觉自己还是有一种被束缚的感觉,有这个感觉就对了 ...
- StackExchange.Redis加载Lua脚本进行模糊查询的批量删除和修改
前言 使用StackExchange.Redis没有直接相关的方法进行模糊查询的批量删除和修改操作,虽然可以通过Scan相关的方法进行模糊查询,例如:HashScan("hashkey&qu ...
- redis原子性读写操作之LUA脚本和watch机制
最近在开发电商平台的子系统--储值卡系统,系统核心业务涉及到金额消费以及库存控制,因此为了解决建立在内存上高并发情况下的事务控制,使用了spring封装的RedisTemplate执行lua脚本进行原 ...
随机推荐
- iOS 记录近期遇到的几个bug
1. actionSheet与pickerView 不兼容 发生环境:ios 9以上,其他无测试. actionSheet与pickerView在一起使用时,当actionSheet弹出后,紧接着再弹 ...
- JavaScript适配器模式
适配模式可用来在现有接口和不兼容的类之间进行适配,使用这种模式的对象又叫包装器(wrapper),因为它们是在用一个新的接口包装另一个对象. 基本理论 适配器模式:将一个接口转换成客户端需要的接口而不 ...
- win10 UWP 显示地图
微软自带的地图很简单 引用地图xmlns:Map="using:Windows.UI.Xaml.Controls.Maps" 写在<Page> 然后在Grid 用Map ...
- 什么是git?window下安装git
一:Git是什么? Git是目前世界上最先进的分布式版本控制系统. 二:SVN与Git的最主要的区别? SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以 ...
- 【深度学习系列】PaddlePaddle之手写数字识别
上周在搜索关于深度学习分布式运行方式的资料时,无意间搜到了paddlepaddle,发现这个框架的分布式训练方案做的还挺不错的,想跟大家分享一下.不过呢,这块内容太复杂了,所以就简单的介绍一下padd ...
- Mybatis基本用法--下
Mybatis基本用法--下 第七部分 mybatis-spring-boot-starter 官网:http://www.mybatis.org/spring-boot-starter/mybati ...
- Arduino.最小系统面包板搭建
最早试过用万用板做过最小系统,主要用来烧录芯片 后来为了方便,用面包板也搭了一个最小系统, 但不采用杜邦线,因为飞来飞去的线太乱了 因此就有了这个简洁的版本,先上个成品图 用个烧录器就可以很方便的烧写 ...
- Ajax.Nodejs.跨域访问
使用环境: 客户端: jQuery 服务器: Node.js 在通过Ajax调用非本域的链接/接口时, 一般是不能成功的, 就算是同一个IP下不同的端口也被认作跨域访问 解决办法记录如下: 客户端: ...
- Yii2之事件
众所周知,yii的三大特性是:属性.事件.行为,上一篇博文简单讲解了yii中的属性,本文接着讲讲yii的事件. 事件是代码解耦的一种方式,设计业务流程的一种模式.在yii2.0中,通过Yii\base ...
- (2017浙江省赛E)Seven Segment Display
Seven Segment Display Time Limit: 2 Seconds Memory Limit: 65536 KB A seven segment display, or ...