这几天研究了一下lua,主要关注的是lua和vc之间的整合,把代码都写好放在VC宿主程序里,然后在lua里调用宿主程序的这些代码(或者叫接口、组件,随便你怎么叫),希望能用脚本来控制主程序的行为。这实际上也是一种把业务分离,用脚本控制的架构,可能有些人把这种脚本叫做业务引擎,工作流等。http://hovertree.com/

为什么选择lua?

因为它是一个能和C/C++结合得很紧的脚本语言,而我们的程序是用VC++ 写的;另外一点是因为它的名气,连WOW都用lua来提供API让玩家修改其游戏行为,那我是找不到什么理由拒绝它了。

Lua是什么?在哪里获取LUA?

详细的不说了,在网上一搜大把,只说一下它的官网吧:www.lua.org,在这里可以查到lua的应用,lua发布的版本,我用的是5.1.4,下载的是源代码的版本。

LUA和VC MFC的整合?

  • 1、 包含LUA:要使用LUA,当然要先把它包含进我们的工程里,可以有lib/dll方式、也可以用静态lib方式,当然也可以把整个lua的代码放进我们的工程,然后编译,因为lua只有几百K,很小。。。包含整个代码的方法我说一下:
  • a) 在VC MFC新建一个工程(例如Dialog base工程)
  • b) 去到工程里的文件tab页,新建一个文件夹,然后把所有lua里的.c、.h文件包含进来,注意有几个不用包含,lua.c、wmain.c、luac.c,包含进来之后,选中这个文件夹下面的所有.c文件,然后右键选setting,选择Not using precompiler file,具体自己找找哈。这步做完就马上编译一下,应该是没问题的了!
  • c) 还有动态库和静态lib两种方式把lua包含进工程里的,自己可以尝试一下。
  • 2、 在某个地方(我是在MFCLua1Dlg.cpp文件开始处)包含lua的头文件,并声明一个lua_state指针,如下:

extern "C"

{

#include "lua.h"

#include "lualib.h"

#include "lauxlib.h"

}

lua_State *lua;

在某个适当的地方(我是在OnInitDialog里)调用下面一段代码,这段代码的作用是打开一些必要的库:

lua = lua_open ();

if(lua)

{

luaopen_base (lua);

luaopen_table (lua);

luaopen_string (lua);

luaopen_math (lua);

luaopen_debug (lua);

//luaopen_io (lua);

}

用完lua的时候,调用下面一句来关闭lua库:

lua_close (lua);

好了,到现在为止,lua已经完全变成我们程序的一部分了,试着编译一下,看看能不能顺利通过。。。

LUA和MFC的交互?

Lua变成我们程序的一部分之后,我们还要使用它,要记住我们的目标是用脚本程序控制我们宿主程序的执行流程,那我们就要完成两步,一是用mfc程序调用lua的函数,二是用lua调用mfc的函数,下面的内容对于初学者可能会开始有点难理解了,请打醒十二分精神,我会尽量简单的说。。。

1、 mfc调用lua的函数,这里用到一个StackDump的函数,是关于主程序和lua的交互栈的问题,下面会对交互栈的问题专门说明。

首先我们用记事本建立一个test.lua,内容是一个相加函数:

function add ( x, y )

return x + y;

end

然后再VC里调用它,如下的一段代码,看这段代码的时候,先把StackDump函数忽略,只需要知道它是一个输出lua和vc交互栈内容的函数,对了,你可以新建一个button的click函数,然后把这段代码放进去:

StackDump(lua);

luaL_dofile(lua, "test.lua");     // 解释分析lua文件

StackDump(lua);

lua_getglobal(lua, "add");       // 取到一个全局标号add,取的同时会把add函数压栈

StackDump(lua);

lua_pushnumber(lua, 1);        // 把第一个参数压入栈里

StackDump(lua);

lua_pushnumber(lua, 2);        // 第二个参数压栈

StackDump(lua);

//lua_call(lua, 2, 1);

if(lua_pcall(lua, 2, 1, 0) != 0)        // 执行add函数

{

AfxMessageBox("lua_pcall error!");

return;

}

StackDump(lua);

int d = (int)lua_tonumber(lua, -1);        // 函数执行完了,执行结果被压栈,所以取得最顶端的一个数就是结果值,-1就是指取栈顶的值

CString str;

str.Format("%d", d);

AfxMessageBox(str);

StackDump(lua);

lua_pop(lua, 1);      // 把值从栈里清除,pop(弹出)一个值

StackDump(lua);

好好分析一下这段代码,我们大概知道调用lua函数的一个过程是:dofile--〉函数名压栈--〉参数依序压栈--〉lua_pcall执行(执行结果压栈)--〉取出执行结果(如果有多个,就从栈里取出多个。。。),这样我们就能很轻松的调用到lua里的函数,其实就是要知道栈里发生了什么。。。

2、 lua调用MFC函数,比如我们想在lua里调用一个Msg函数,能弹出一个窗口来显示我们想显示的字串,然后返回值是1个"MsgOK!"字串。

lua文件是这样的,第一句是调用Msg函数,第二句是测试返回的字串是不是"MsgOK!":

c = Msg ("123");

Msg(c);

MFC程序里是这样的:

首先写一个将要被导出的函数,很多文章叫它为粘合函数(glue function):

static int Msg(lua_State* L)

{

const char *s1 = luaL_checkstring(L, 1);     // 测试第一个参数是否为字串形式,并取得这个字串

StackDump(L);

MessageBox(NULL, s1, "caption", MB_OK);

lua_pop(lua, 1);      // 清除栈里的这个字串

StackDump(L);

lua_pushlstring(L, "MsgOK!", 6);  // 把返回值压进栈里

// 这个返回是指返回值的个数

return 1;

}

然后就导出这个函数,如下:

lua_pushcfunction(lua, Msg);

lua_setglobal(lua, "Msg");

接着就执行刚才的lua文件就行了,记得执行之前要先lua_open () 哦:

luaL_dofile(lua, "test.lua");

运行的结果就是连续跳出两个messagebox,第一个是123,第二个是"MsgOK!",说明我们返回的字串被lua接收到了,lua的第二行我们没有接收它的返回值,则这个返回值会自动被抛弃了。

如果需要多返回值,则我们要把下面一句:

lua_pushlstring(L, "MsgOK!", 6);  // 把返回值压进栈里 何问起

// 这个返回是指返回值的个数

return 1;

改为:

lua_pushlstring(L, "MsgOK!", 6);  // 把返回值压进栈里

lua_pushlstring(L, "haha!", 5);      // 把返回值压进栈里

// 这个返回是指返回值的个数

return 2;

这样我们在lua文件里就可以像下面一样取得两个返回值了:

c,d = Msg("123");

那c和d就分别是"MsgOK!"和"haha!"两个字串了。 这种自动机制用起来还是比较方便的。

3、交互栈

上面两个调用其实都是对lua栈的实用,那我们就要好好理解一个概念,lua和vc的交互栈(栈是什么?请参考数据结构的书哈。。。)lua和vc就是通过这个栈来实现交互的,这个栈的访问函数有lua_gettop,lua_settop,lua_tostring,lua_toXXX等等的函数,我们要清楚当一个函数调用发生的时候,栈里是发生了什么。上面我用了一个StackDump函数,当我们调用的时候,能很清楚的看到栈里发生了什么。

首先我们要知道从栈顶往下数就是-1、-2,从栈底往上数就是1、2。

如果使用lua_gettop(L, 1),就是取得栈底第一个元素。lua_gettop(L, -1)就是取得栈顶的第一个元素。lua_pop() (L, 1)就是把栈顶的一个元素弹出来,lua_pop()(L, 2)就是把栈顶的两个元素弹出。

好了,写了一通,最后是这个StackDump函数的实现:

int StackDump(lua_State* L)

{

int nTop = lua_gettop(L); //得到栈的元素个数。栈顶的位置。

OutputDebugString("The Length of stack is %d/n", nTop); //输出栈顶位置

for (int i = 1; i <= nTop; ++i)

{

int t = lua_type(L, i);

OutputDebugString("%s:", lua_typename(L, t)); //这里的typename是把类型的枚举变成字符串,是类型名。不是栈中的位置。

switch(t)

{

case LUA_TNUMBER:

OutputDebugString("%f", lua_tonumber(L, i));

break;

case LUA_TSTRING:

OutputDebugString("%s", lua_tostring(L, i));

break;

case LUA_TTABLE:

//OutputDebugString("%s/n", lua_tostring(L,i));

break;

case LUA_TFUNCTION:

//OutputDebugString("%s/n", lua_tostring(L,i));

break;

case LUA_TNIL:

OutputDebugString("Is NULL");

break;

case LUA_TBOOLEAN:

OutputDebugString("%s", lua_toboolean(L, i) ? "true" : "false");

break;

default:

break;

}

OutputDebugString("/n");

}

return 0;

}

本篇文章主要讲了lua和VC的整合、把LUA源代码和VC工程一起编译,VC调用LUA的代码,LUA调用VC的代码,返回值以及多个返回值、交互栈、输出交互栈里的元素信息等内容,下一篇将会说说如何避免阻塞的脚本,lua和多线程的使用等内容。

http://www.cnblogs.com/roucheng/

Lua使用心得(1)的更多相关文章

  1. <转> Lua使用心得(2)

    在lua脚本调用中,如果我们碰到一种不好的脚本,例如: do do end 那我们的程序主线程也会被阻塞住.那我们如何防止这种问题呢?下面就给出一个解决的办法. 首先为了不阻塞主线程,那我们就要开一个 ...

  2. Lua使用心得(2)

    在lua脚本调用中,如果我们碰到一种不好的脚本,例如: while 1 do do end 那我们的程序主线程也会被阻塞住.那我们如何防止这种问题呢?下面就给出一个解决的办法. 首先为了不阻塞主线程, ...

  3. (转) Lua使用心得一 LUA和VC整合

    这几天研究了一下lua,主要关注的是lua和vc之间的整合,把代码都写好放在VC宿主程序里,然后在lua里调用宿主程序的这些代码(或者叫接口.组件,随便你怎么叫),希望能用脚本来控制主程序的行为.这实 ...

  4. JS在与lua的交互心得

    最近在写一个项目,前端技术使用的是Vue,在与lua的交互过程,是通过一个公共JS,前端调用公共js的方法给lua发送命令,lua接到命令,去执行一些方法,然后又通过回调返回到了前端,由于是第一次写这 ...

  5. lua学习项目笔记

    这几天草草的浏览了一下电子版的<lua程序设计>,没有懂的地方就自动忽略了,挑拣了一些可以理解的部分一直在推进.推进至后面的时候已经浑浑噩噩的了,有种想看完这本书的强迫症的感觉.推进CAP ...

  6. C++与Lua交互(一)

    引言 之前做手游项目时,客户端用lua做脚本,基本所有游戏逻辑都用它完成,玩起来有点不爽,感觉"太重"了.而我又比较偏服务端这边(仅有C++),所以热情不高.最近,加入了一个端游项 ...

  7. 我的2016年终总结(PF项目框架设计心得分享 2.0rc)

    在无数的日夜里,熬出了多少的黑眼圈,致勤勤恳恳工作的各位朋友与自己.每到了年末的时候总想写的什么,主要是为了回顾以往一年里到底做了什么,这便是年终总结的主要意义.在此我将要总结的是和我在技术层面上成长 ...

  8. lua 函数调用1 -- 闭包详解和C调用

    这里, 简单的记录一下lua中闭包的知识和C闭包调用 前提知识: 在lua api小记2中已经分析了lua中值的结构, 是一个 TValue{value, tt}组合, 如果有疑问, 可以去看一下 一 ...

  9. tolua++实现lua层调用c++技术分析

    tolua++技术分析 cocos2dx+lua 前言 一直都使用 cocos2dx + lua 进行游戏开发,用 Lua 开发可以专注于游戏逻辑的实现,另外一方面可以实现热更新:而且 lua 是一个 ...

随机推荐

  1. NanoProfiler - 适合生产环境的性能监控类库 之 实践ELK篇

    上期回顾 上一期:NanoProfiler - 适合生产环境的性能监控类库 之 大数据篇 上次介绍了NanoProfiler的大数据分析理念,一晃已经时隔一年多了,真是罪过! 有朋友问到何时开源的问题 ...

  2. 消息队列-rabbitMQ

    消息队列两个用处:服务间解耦,缓解压力(削峰平谷),以前用过ZMQ.狼厂内部的NMQ,现在接触了java开源的kafka和RabbitMQ.目前先不求甚解,有个大概的认识. RabbitMQ的安装和入 ...

  3. js模仿新浪微博限制字数输入

    功能:实现新浪微博输入字数提醒功能:最多输入140个字,当输入字时,上面提示部分字数发生变化,如果字数小于25,字体颜色变红:当可输入字数为0时,强制不能输入,如果用中文输入法 一次性输入很多字,那么 ...

  4. java 线程协作 wait(等待)与 notiy(通知)

    一.wait().notify()和notifyAll() 为了更好的支持多线程之间的协作,JDK提供了三个重要的本地方法 //调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对 ...

  5. 将数据转化成字符串时:用字符串的链接 还是 StringBuilder

    /* 目的:将数据转化成字符串时:用字符串的链接 还是 StringBuilder呢? */ public class Test{ public static void main(String[] a ...

  6. 【原创】C#玩高频数字彩快3的一点体会

    购彩风险非常高,本人纯属很久以前对数字高频彩的一点研究.目前已经远离数字彩,重点研究足球篮球比赛资料库和赛果预测. 这是一篇在草稿箱保存了1年多的文章,一直没发现,顺便修改修改分享给大家.以后会有更多 ...

  7. MongoDB的学习--聚合

    最近要去的新项目使用mysql,趁着还没忘记,总结记录以下MongoDB的聚合. 聚合是泛指各种可以处理批量记录并返回计算结果的操作.MongoDB提供了丰富的聚合操作,用于对数据集执行计算操作.在  ...

  8. Unity中 动态加载 Resources.Load()和Asset Bundle 的区别

    版权声明:本文为博主原创文章,未经博主允许不得转载. 初学Unity的过程中,会发现打包发布程序后,unity会自动将场景需要引用到的资源打包到安装包里,没有到的不会跟进去.我们在编辑器里看到的Ass ...

  9. javascript学习6

    JavaScript  Boolean(逻辑)对象 Boolean(逻辑)对象用于将非逻辑值转换为逻辑值(true 或者 false). 实例 检查逻辑值 检查逻辑对象是 true 还是 false. ...

  10. javascript学习5

    JavaScript Array(数组)对象 数组对象的作用是:使用单独的变量名来存储一系列的值. 实例 创建数组 创建数组,为其赋值,然后输出这些值. For...In 声明 使用 for...in ...