zoukankan      html  css  js  c++  java
  • LUA整合进MFC代码

    1. 这几天研究了一下lua,主要关注的是lua和vc之间的整合,把代码都写好放在VC宿主程序里,然后在lua里调用宿主程序的这些代码(或者叫接口、组件,随便你怎么叫),希望能用脚本来控制主程序的行为。这实际上也是一种把业务分离,用脚本控制的架构,可能有些人把这种脚本叫做业务引擎,工作流等。  
    2. 为什么选择lua?  
    3. 因为它是一个能和C/C++结合得很紧的脚本语言,而我们的程序是用VC++ 写的;另外一点是因为它的名气,连WOW都用lua来提供API让玩家修改其游戏行为,那我是找不到什么理由拒绝它了。  
    4. Lua是什么?在哪里获取LUA?  
    5. 详细的不说了,在网上一搜大把,只说一下它的官网吧:www.lua.org,在这里可以查到lua的应用,lua发布的版本,我用的是5.1.4,下载的是源代码的版本。  
    6. LUA和VC MFC的整合?  
    7. 1、 包含LUA:要使用LUA,当然要先把它包含进我们的工程里,可以有lib/dll方式、也可以用静态lib方式,当然也可以把整个lua的代码放进我们的工程,然后编译,因为lua只有几百K,很小。。。包含整个代码的方法我说一下:   
    8. a) 在VC MFC新建一个工程(例如Dialog base工程)   
    9. b) 去到工程里的文件tab页,新建一个文件夹,然后把所有lua里的.c、.h文件包含进来,注意有几个不用包含,lua.c、wmain.c、luac.c,包含进来之后,选中这个文件夹下面的所有.c文件,然后右键选setting,选择Not using precompiler file,  
    10. 在VS2008中,右键.CPP文件,  
    11. --->属性---->CC++----->预编译头----->创建/使用预编译头----->选择 :不使用预编译头  
    12.    
    13. 这步做完就马上编译一下,应该是没问题的了!   
    14. c) 还有动态库和静态lib两种方式把lua包含进工程里的,自己可以尝试一下。   
    15. 2、 在某个地方(我是在MFCLua1Dlg.cpp文件开始处)包含lua的头文件,并声明一个lua_state指针,如下:   
    16. extern "C"  
    17. {  
    18. #include "lua.h"  
    19. #include "lualib.h"  
    20. #include "lauxlib.h"  
    21. }  
    22. lua_State *lua;  
    23.    
    24. 在某个适当的地方(我是在OnInitDialog里)调用下面一段代码,这段代码的作用是打开一些必要的库:  
    25.        lua = lua_open ();    
    26.        if(lua)  
    27.        {  
    28.               luaopen_base (lua);  
    29.               luaopen_table (lua);  
    30.               luaopen_string (lua);  
    31.               luaopen_math (lua);  
    32.               luaopen_debug (lua);  
    33.               //luaopen_io (lua);  
    34.        }  
    35. 用完lua的时候,调用下面一句来关闭lua库:  
    36. lua_close (lua);  
    37.                 
    38. 好了,到现在为止,lua已经完全变成我们程序的一部分了,试着编译一下,看看能不能顺利通过。。。  
    39. LUA和MFC的交互?  
    40. Lua变成我们程序的一部分之后,我们还要使用它,要记住我们的目标是用脚本程序控制我们宿主程序的执行流程,那我们就要完成两步,一是用mfc程序调用lua的函数,二是用lua调用mfc的函数,下面的内容对于初学者可能会开始有点难理解了,请打醒十二分精神,我会尽量简单的说。。。  
    41. 1、 mfc调用lua的函数,这里用到一个StackDump的函数,是关于主程序和lua的交互栈的问题,下面会对交互栈的问题专门说明。  
    42. 首先我们用记事本建立一个test.lua,内容是一个相加函数:  
    43. function add ( x, y )   
    44. return x + y;  
    45. end  
    46. 然后再VC里调用它,如下的一段代码,看这段代码的时候,先把StackDump函数忽略,只需要知道它是一个输出lua和vc交互栈内容的函数,对了,你可以新建一个button的click函数,然后把这段代码放进去:  
    47. StackDump(lua);      
    48. luaL_dofile(lua, "test.lua");     // 解释分析lua文件  
    49. StackDump(lua);  
    50. lua_getglobal(lua, "add");       // 取到一个全局标号add,取的同时会把add函数压栈  
    51. StackDump(lua);      
    52. lua_pushnumber(lua, 1);        // 把第一个参数压入栈里  
    53. StackDump(lua);  
    54. lua_pushnumber(lua, 2);        // 第二个参数压栈  
    55. StackDump(lua);  
    56. //lua_call(lua, 2, 1);   
    57. if(lua_pcall(lua, 2, 1, 0) != 0)        // 执行add函数  
    58. {  
    59.         AfxMessageBox("lua_pcall error!");               
    60. return;  
    61. }  
    62. StackDump(lua);  
    63. int d = (int)lua_tonumber(lua, -1);        // 函数执行完了,执行结果被压栈,所以取得最顶端的一个数就是结果值,-1就是指取栈顶的值  
    64. CString str;  
    65. str.Format("%d", d);  
    66. AfxMessageBox(str);  
    67. StackDump(lua);  
    68. lua_pop(lua, 1);      // 把值从栈里清除,pop(弹出)一个值  
    69. StackDump(lua);  
    70. 好好分析一下这段代码,我们大概知道调用lua函数的一个过程是:dofile--〉函数名压栈--〉参数依序压栈--〉lua_pcall执行(执行结果压栈)--〉取出执行结果(如果有多个,就从栈里取出多个。。。),这样我们就能很轻松的调用到lua里的函数,其实就是要知道栈里发生了什么。。。  
    71. 2、 lua调用MFC函数,比如我们想在lua里调用一个Msg函数,能弹出一个窗口来显示我们想显示的字串,然后返回值是1个"MsgOK!"字串。  
    72. lua文件是这样的,第一句是调用Msg函数,第二句是测试返回的字串是不是"MsgOK!":  
    73. c = Msg ("123");  
    74. Msg(c);  
    75. MFC程序里是这样的:  
    76. 首先写一个将要被导出的函数,很多文章叫它为粘合函数(glue function):  
    77. static int Msg(lua_State* L)  
    78. {  
    79. const char *s1 = luaL_checkstring(L, 1);     // 测试第一个参数是否为字串形式,并取得这个字串  
    80. StackDump(L);  
    81. MessageBox(NULL, s1, "caption", MB_OK);  
    82. lua_pop(lua, 1);      // 清除栈里的这个字串  
    83. StackDump(L);  
    84. lua_pushlstring(L, "MsgOK!", 6);  // 把返回值压进栈里  
    85. // 这个返回是指返回值的个数  
    86. return 1;  
    87. }  
    88.        然后就导出这个函数,如下:  
    89.        lua_pushcfunction(lua, Msg);  
    90.        lua_setglobal(lua, "Msg");  
    91.        接着就执行刚才的lua文件就行了,记得执行之前要先lua_open () 哦:  
    92.        luaL_dofile(lua, "test.lua");  
    93.        运行的结果就是连续跳出两个messagebox,第一个是123,第二个是"MsgOK!",说明我们返回的字串被lua接收到了,lua的第二行我们没有接收它的返回值,则这个返回值会自动被抛弃了。  
    94.        如果需要多返回值,则我们要把下面一句:  
    95. lua_pushlstring(L, "MsgOK!", 6);  // 把返回值压进栈里  
    96. // 这个返回是指返回值的个数  
    97. return 1;  
    98. 改为:  
    99. lua_pushlstring(L, "MsgOK!", 6);  // 把返回值压进栈里  
    100. lua_pushlstring(L, "haha!", 5);      // 把返回值压进栈里  
    101. // 这个返回是指返回值的个数  
    102. return 2;  
    103. 这样我们在lua文件里就可以像下面一样取得两个返回值了:  
    104. c,d = Msg("123");  
    105. 那c和d就分别是"MsgOK!"和"haha!"两个字串了。 这种自动机制用起来还是比较方便的。  
    106. 3、交互栈  
    107. 上面两个调用其实都是对lua栈的实用,那我们就要好好理解一个概念,lua和vc的交互栈(栈是什么?请参考数据结构的书哈。。。)lua和vc就是通过这个栈来实现交互的,这个栈的访问函数有lua_gettop,lua_settop,lua_tostring,lua_toXXX等等的函数,我们要清楚当一个函数调用发生的时候,栈里是发生了什么。上面我用了一个StackDump函数,当我们调用的时候,能很清楚的看到栈里发生了什么。  
    108.        首先我们要知道从栈顶往下数就是-1、-2,从栈底往上数就是1、2。  
    109. 如果使用lua_gettop(L, 1),就是取得栈底第一个元素。lua_gettop(L, -1)就是取得栈顶的第一个元素。lua_pop() (L, 1)就是把栈顶的一个元素弹出来,lua_pop()(L, 2)就是把栈顶的两个元素弹出。  
    110. 好了,写了一通,最后是这个StackDump函数的实现:  
    111. int StackDump(lua_State* L)  
    112. {  
    113.        int nTop = lua_gettop(L); //得到栈的元素个数。栈顶的位置。  
    114.        OutputDebugString("The Length of stack is %d ", nTop); //输出栈顶位置  
    115.        for (int i = 1; i <= nTop; ++i)  
    116.        {  
    117.               int t = lua_type(L, i);  
    118.               OutputDebugString("%s:", lua_typename(L, t)); //这里的typename是把类型的枚举变成字符串,是类型名。不是栈中的位置。  
    119.               switch(t)  
    120.               {  
    121.               case LUA_TNUMBER:  
    122.                      OutputDebugString("%f", lua_tonumber(L, i));  
    123.                      break;  
    124.               case LUA_TSTRING:  
    125.                      OutputDebugString("%s", lua_tostring(L, i));  
    126.                      break;  
    127.               case LUA_TTABLE:  
    128.                      //OutputDebugString("%s ", lua_tostring(L,i));  
    129.                      break;  
    130.               case LUA_TFUNCTION:  
    131.                      //OutputDebugString("%s ", lua_tostring(L,i));  
    132.                      break;  
    133.               case LUA_TNIL:  
    134.                      OutputDebugString("Is NULL");  
    135.                      break;  
    136.               case LUA_TBOOLEAN:  
    137.                      OutputDebugString("%s", lua_toboolean(L, i) ? "true" : "false");  
    138.                      break;  
    139.               default:  
    140.                      break;  
    141.               }  
    142.               OutputDebugString(" ");  
    143.        }  
    144.        return 0;  
    145. }  
    146. 本篇文章主要讲了lua和VC的整合、把LUA源代码和VC工程一起编译,VC调用LUA的代码,LUA调用VC的代码,返回值以及多个返回值、交互栈、输出交互栈里的元素信息等内容,下一篇将会说说如何避免阻塞的脚本,lua和多线程的使用等内容。  
     
     

    http://blog.csdn.net/chinazhd/article/details/7268910

  • 相关阅读:
    Hard Rock
    Codeforces Round #416 (Div. 2) B. Vladik and Complicated Book
    codeforces 793B. Igor and his way to work
    codeforces 1B Spreadsheets
    HDU 1069 Monkey and Banana
    codeforces 2B The least round way
    【机器学习】 通俗说拟合
    python-八皇后问题
    python-核心知识思维导图
    python-@property 属性
  • 原文地址:https://www.cnblogs.com/findumars/p/4531265.html
Copyright © 2011-2022 走看看