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

为什么选择lua?

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

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

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

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指针,如下:
  1. extern "C"
  2.  
  3. {
  4.  
  5. #include "lua.h"
  6.  
  7. #include "lualib.h"
  8.  
  9. #include "lauxlib.h"
  10.  
  11. }
  12.  
  13. lua_State *lua;

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

  1. lua = luaL_newstate();
  2.  
  3. if(lua)
  4.  
  5. {
  6.  
  7. luaopen_base (lua);
  8.  
  9. luaopen_table (lua);
  10.  
  11. luaopen_string (lua);
  12.  
  13. luaopen_math (lua);
  14.  
  15. luaopen_debug (lua);
  16.  
  17. //luaopen_io (lua);
  18.  
  19. }

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

lua_close (lua);

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

LUA和MFC的交互?

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

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

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

  1. function add ( x, y )
  2.  
  3. return x + y;
  4.  
  5. end

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

  1. StackDump(lua);
  2.  
  3. luaL_dofile(lua, "test.lua"); // 解释分析lua文件
  4.  
  5. StackDump(lua);
  6.  
  7. lua_getglobal(lua, "add"); // 取到一个全局标号add,取的同时会把add函数压栈
  8.  
  9. StackDump(lua);
  10.  
  11. lua_pushnumber(lua, ); // 把第一个参数压入栈里
  12.  
  13. StackDump(lua);
  14.  
  15. lua_pushnumber(lua, ); // 第二个参数压栈
  16.  
  17. StackDump(lua);
  18.  
  19. //lua_call(lua, 2, 1);
  20.  
  21. if(lua_pcall(lua, , , ) != ) // 执行add函数
  22.  
  23. {
  24.  
  25. AfxMessageBox("lua_pcall error!");
  26.  
  27. return;
  28.  
  29. }
  30.  
  31. StackDump(lua);
  32.  
  33. // 函数执行完了,执行结果被压栈,所以取得最顶端的一个数就是结果值,-1就是指取栈顶的值
  34.  
  35. int d = (int)lua_tonumber(lua, -);
  36.  
  37. CString str;
  38.  
  39. str.Format("%d", d);
  40.  
  41. AfxMessageBox(str);
  42.  
  43. StackDump(lua);
  44.  
  45. lua_pop(lua, ); // 把值从栈里清除,pop(弹出)一个值
  46.  
  47. StackDump(lua);

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

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

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

  1. c = Msg ("123");
  2.  
  3. Msg(c);

MFC程序里是这样的:

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

  1. static int Msg(lua_State* L)
  2.  
  3. {
  4.  
  5. const char *s1 = luaL_checkstring(L, 1); // 测试第一个参数是否为字串形式,并取得这个字串
  6.  
  7. StackDump(L);
  8.  
  9. MessageBox(NULL, s1, "caption", MB_OK);
  10.  
  11. lua_pop(lua, 1); // 清除栈里的这个字串
  12.  
  13. StackDump(L);
  14.  
  15. lua_pushlstring(L, "MsgOK!", 6); // 把返回值压进栈里
  16.  
  17. // 这个返回是指返回值的个数
  18.  
  19. return 1;
  20.  
  21. }

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

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函数的实现:

  1. int StackDump(lua_State* L)
  2.  
  3. {
  4.  
  5. int nTop = lua_gettop(L); //得到栈的元素个数。栈顶的位置。
  6.  
  7. OutputDebugString("The Length of stack is %d/n", nTop); //输出栈顶位置
  8.  
  9. for (int i = ; i <= nTop; ++i)
  10.  
  11. {
  12.  
  13. int t = lua_type(L, i);
  14.  
  15. OutputDebugString("%s:", lua_typename(L, t));
    16         //这里的typename是把类型的枚举变成字符串,是类型名。不是栈中的位置。
  16. switch(t)
  17.  
  18. {
  19.  
  20. case LUA_TNUMBER:
  21.  
  22. OutputDebugString("%f", lua_tonumber(L, i));
  23.  
  24. break;
  25.  
  26. case LUA_TSTRING:
  27.  
  28. OutputDebugString("%s", lua_tostring(L, i));
  29.  
  30. break;
  31.  
  32. case LUA_TTABLE:
  33.  
  34. //OutputDebugString("%s/n", lua_tostring(L,i));
  35.  
  36. break;
  37.  
  38. case LUA_TFUNCTION:
  39.  
  40. //OutputDebugString("%s/n", lua_tostring(L,i));
  41.  
  42. break;
  43.  
  44. case LUA_TNIL:
  45.  
  46. OutputDebugString("Is NULL");
  47.  
  48. break;
  49.  
  50. case LUA_TBOOLEAN:
  51.  
  52. OutputDebugString("%s", lua_toboolean(L, i) ? "true" : "false");
  53.  
  54. break;
  55.  
  56. default:
  57.  
  58. break;
  59.  
  60. }
  61.  
  62. OutputDebugString("/n");
  63.  
  64. }
  65.  
  66. return ;
  67.  
  68. }

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

最后附上demo的代码工程.环境用的是 vs2010的.

地址:http://pan.baidu.com/s/1qXi3Jbe

lua和整合实践的更多相关文章

  1. Redis进阶实践之七Redis和Lua初步整合使用(转载 7)

    Redis进阶实践之七Redis和Lua初步整合使用 一.引言 Redis学了一段时间了,基本的东西都没问题了.从今天开始讲写一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运 ...

  2. 一个快速、完善的Android开发框架整合实践(QuickAndroid)

    https://github.com/alafighting/QuickAndroid QuickAndroid 一个快速.完善的Android开发框架整合实践 QA项目简介 本框架QuickAndr ...

  3. Nginx+Lua+Redis整合实现高性能API接口 - 网站服务器 - LinuxTone | 运维专家网论坛 - 最棒的Linux运维与开源架构技术交流社区! - Powered by Discuz!

    Nginx+Lua+Redis整合实现高性能API接口 - 网站服务器 - LinuxTone | 运维专家网论坛 - 最棒的Linux运维与开源架构技术交流社区! - Powered by Disc ...

  4. lua游戏开发实践指南学习笔记1

    本文是依据lua游戏开发实践指南做的一些学习笔记,仅用于继续自己学习的一些知识. Lua基础 1.  语言定义: 在lua语言中,标识符有非常大的灵活性(变量和函数名),只是用户不呢个以数字作为起始符 ...

  5. Redis进阶实践之七Redis和Lua初步整合使用

    一.引言        Redis学了一段时间了,基本的东西都没问题了.从今天开始讲写一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运行在任何平台上,也可以嵌入到大多数语言当 ...

  6. Kafka+Storm+HDFS整合实践

    在基于Hadoop平台的很多应用场景中,我们需要对数据进行离线和实时分析,离线分析可以很容易地借助于Hive来实现统计分析,但是对于实时的需求Hive就不合适了.实时应用场景可以使用Storm,它是一 ...

  7. 《Lua游戏开发实践指南》读后感

    书籍地址:http://book.douban.com/subject/20392269/ 一句话点评该书:想用Lua作游戏脚本开发的同学值得一读! (一)本书特点 市面专门讲Lua的中文书籍非常少, ...

  8. [转载] Kafka+Storm+HDFS整合实践

    转载自http://www.tuicool.com/articles/NzyqAn 在基于Hadoop平台的很多应用场景中,我们需要对数据进行离线和实时分析,离线分析可以很容易地借助于Hive来实现统 ...

  9. demo2 Kafka+Spark Streaming+Redis实时计算整合实践 foreachRDD输出到redis

    基于Spark通用计算平台,可以很好地扩展各种计算类型的应用,尤其是Spark提供了内建的计算库支持,像Spark Streaming.Spark SQL.MLlib.GraphX,这些内建库都提供了 ...

随机推荐

  1. HBase Shell操作

    Hbase 是一个分布式的.面向列的开源数据库,其实现是建立在google 的bigTable 理论之上,并基于hadoop HDFS文件系统.     Hbase不同于一般的关系型数据库(RDBMS ...

  2. os.path 大全

    os.path.abspath(path) #返回绝对路径 os.path.basename(path) #返回一个路径的最后一个组成部分 os.path.commonprefix(list) #返回 ...

  3. @RequestMapping用法详解

    @RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上.用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径. RequestMapping注解有六个属性,下面我们把 ...

  4. javascript-with()方法

    1)简要说明         with 语句可以方便地用来引用某个特定对象中已有的属性,但是不能用来给对象添加属性.要给对象创建新的属性,必须明确地引用该对象. 2)语法格式  with(object ...

  5. windows Service 创建部署

    Windows Service简介: 一个Windows服务程序是在Windows操作系统下能完成特定功能的可执行的应用程序.Windows服务程序虽然是可执行的,但是它不像一般的可执行文件通过双击就 ...

  6. svn记录删除

    Delete SVN Folders.reg 批量删除文件夹里的SVN 文件 ------------------------------------------------------------- ...

  7. django修改admin密码

    python manage.py shell 然后获取你的用户名,并且重设密码:from django.contrib.auth.models import User user = User.obje ...

  8. Java上面出现这个错误如何解决关于XML的

    Java上面出现这个错误如何解决关于XML的 2015-01-07 14:49 hejiashun11325 | 分类:JAVA相关 | 浏览265次 The type org.xmlpull.v1. ...

  9. idea 显示行号

    File->Settings->Editor->General->Appearence->Show Line Number 选中之后"Apply",然 ...

  10. Hadoop HDFS编程 API入门系列之RPC版本1(八)

    不多说,直接上代码. 代码 package zhouls.bigdata.myWholeHadoop.RPC.rpc1; import java.io.IOException;import java. ...