在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
catalog 1. Lua语言简介 2. 使用 Lua 编写可嵌入式脚本 3. VS2010编译Lua 4. 嵌入和扩展: C/C++中执行Lua脚本 5. 将C++函数导出到Lua引擎中: 在Lua脚本中执行C++函数 6. 将C函数导出到Lua引擎中: 在Lua脚本中执行C函数 7. C++ Function Library For Lua 8. Lua、Python嵌入式语言引擎的优缺点对比
1. Lua语言简介 0x1: 运行 Lua是类C的,所以,他是大小写字符敏感的,同时,Lua脚本的语句的分号是可选的(和GO语言类似) 也可以把脚本存成一个文件,用如下命令行来运行 0x2: 语法 1. 注释 -- 两个减号是行注释 --[[ 这是块注释 这是块注释 --]] 2. 变量 Lua的数字只有double型,64bits,可以以如下的方式表示数字 num = 1024 num = 3.0 num = 3.1416 num = 314.16e-2 num = 0.31416E1 num = 0xff num = 0x56 布尔类型只有nil和false是 false,数字0啊,''空字符串('\0')都是true theGlobalVar = 50 local theLocalVar = "local variable" 3. 字符串 字符串你可以用单引号,也可以用双引号,还支持C类型的转义,比如 1. '\a': 响铃 2. '\b': 退格 3. '\f': 表单 4. '\n': 换行 5. '\r': 回车 6. '\t': 横向制表 7. '\v': 纵向制表 8. '\\': 反斜杠 9. '\"': 双引号 10. '\'': 单引号 下面的四种方式定义了完全相同的字符串(其中的两个中括号可以用于定义有换行的字符串) a = 'alo\n123"' a = "alo\n123\"" a = '\97lo\10\04923"' a = [[alo 123"]] C语言中的NULL在Lua中是nil,比如你访问一个没有声明过的变量,就是nil,比如下面的v的值就是nil v = UndefinedVariable 4. 控制语句 值得注意的是,Lua没有++或是+=这样的操作 1. while循环 sum = 0 num = 1 while num <= 100 do sum = sum + num num = num + 1 end print("sum =",sum) 2. if-else分支 if age == 40 and sex == "Male" then print("男人四十一枝花") elseif age > 60 and sex ~= "Female" then print("old man without country!") elseif age < 20 then io.write("too young, too naive!\n") else local age = io.read() print("Your age is "..age) end /* 上面的语句不但展示了if-else语句,也展示了 1) ~="是不等于,而不是!= 2) io库的分别从stdin和stdout读写的read和write函数 3) 字符串的拼接操作符".." */ 3. for循环 从1加到100 sum = 0 for i = 1, 100 do sum = sum + i end 从1到100的奇数和 sum = 0 for i = 1, 100, 2 do sum = sum + i end 从100到1的偶数和 sum = 0 for i = 100, 1, -2 do sum = sum + i end 4. until循环 sum = 2 repeat sum = sum ^ 2 --幂操作 print(sum) until sum >1000 5. 函数 1. 递归 function fib(n) if n < 2 then return 1 end return fib(n - 2) + fib(n - 1) end 2. 闭包 示例一 function newCounter() local i = 0 return function() -- anonymous function i = i + 1 return i end end c1 = newCounter() print(c1()) --> 1 print(c1()) --> 2 示例二 function myPower(x) return function(y) return y^x end end power2 = myPower(2) power3 = myPower(3) print(power2(4)) --4的2次方 print(power3(5)) --5的3次方 3. 函数的返回值 和Go语言一样,可以一条语句上赋多个值,如 name, age, bGay = "haoel", 37, false, "[email protected]" //上面的代码中,因为只有3个变量,所以第四个值被丢弃 函数也可以返回多个值 function getUserInfo(id) print(id) return "haoel", 37, "[email protected]", "http://coolshell.cn" end name, age, email, website, bGay = getUserInfo() //上面的示例中,因为没有传id,所以函数中的id输出为nil,因为没有返回bGay,所以bGay也是nil 6. 局部函数 下面的两个函数是一样的 function foo(x) return x^2 end foo = function(x) return x^2 end 7. Table 所谓Table其实就是一个Key Value的数据结构,它很像Javascript中的Object,或是PHP中的数组,在别的语言里叫Dict或Map haoel = {name="ChenHao", age=37, handsome=True} 下面是table的CRUD操作 haoel.website="http://coolshell.cn/" local age = haoel.age haoel.handsome = false haoel.name=nil 看上去像C/C++中的结构体,但是name,age, handsome, website都是key。我们还可以像下面这样写义Table t = {[20]=100, ['name']="ChenHao", [3.14]="PI"} //我们可以这样访问: t[20],t["name"], t[3.14] 数组的定义更加灵活 arr = {10,20,30,40,50} 其等价于 arr = {[1]=10, [2]=20, [3]=30, [4]=40, [5]=50} 也可以定义成不同的类型的数组,比如 arr = {"string", 100, "haoel", function() print("coolshell.cn") end} //其中的函数可以这样调用: arr[4]() Lua的下标不是从0开始的,是从1开始的 for i=1, #arr do print(arr[i]) end //上面的程序中:#arr的意思就是arr的长度 我们知道,Lua中的变量,如果没有local关键字,全都是全局变量,Lua也是用Table来管理全局变量的,Lua把这些全局变量放在了一个叫"_G"的Table里 _G.globalVar _G["globalVar"] 也可以通过下面的方式来遍历一个Table for k, v in pairs(t) do print(k, v) end 8. MetaTable 和 MetaMethod MetaTable和MetaMethod是Lua中的重要的语法,MetaTable主要是用来做一些类似于C++重载操作符式的功能 fraction_a = {numerator=2, denominator=3} fraction_b = {numerator=4, denominator=7} 想实现分数间的相加:2/3 + 4/7,我们如果要执行: fraction_a + fraction_b,会报错的 fraction_op={} function fraction_op.__add(f1, f2) ret = {} ret.numerator = f1.numerator * f2.denominator + f2.numerator * f1.denominator ret.denominator = f1.denominator * f2.denominator return ret end 为之前定义的两个table设置MetaTable: (其中的setmetatble是库函数) setmetatable(fraction_a, fraction_op)
setmetatable(fraction_b, fraction_op)
接下来就可以正常进行分数运算了 fraction_s = fraction_a + fraction_b //调用的是fraction_op.__add()函数 0x3: 模块 我们可以直接使用require(“model_name”)来载入别的lua文件,文件的后缀是.lua。载入的时候就直接执行那个文件了(和PHP的逻辑是一样的) 1. require函数,载入同样的lua文件时,只有第一次的时候会去执行,后面的相同的都不执行了,相当于PHP中的require_once 2. 如果要让每一次文件都会执行的话,你可以使用dofile("hello")函数,相当于PHP中的require 3. 如果要 载入后不执行,等需要的时候执行时,你可以使用 loadfile()函数,如下所示 /* local hello = loadfile("hello") ... ... ... ... hello() loadfile("hello")后,文件并不执行,我们把文件赋给一个变量hello,当hello()时,才真的执行 */ mymod.lua local HaosModel = {} local function getname() return "Hao Chen" end function HaosModel.Greeting() print("Hello, My name is "..getname()) end return HaosModel file.lua local hao_model = require("mymod") hao_model.Greeting() Relevant Link: http://coolshell.cn/articles/10739.html
2. 使用 Lua 编写可嵌入式脚本 0x1: Lua新特性 与其他脚本语言一样,Lua也有自己的一些特性 1. Lua类型: 在 Lua 中,值可以有类型,但是变量的类型都是动态决定的。nil、布尔型、数字 和 字符串 类型的工作方式与我们期望的一样 1) Nil: 是值为 nil 的一种特殊类型,用来表示没有值 2) 布尔型的值可以是 true 和 false 常量(Nil 也可以表示 false,任何非 nil 的值都表示 true) 3) Lua 中所有的数字都是双精度的 4) 字符串是定长字符数组(因此,要在一个字符串后面附加上字符,必须对其进行拷贝) 5) 表、函数 和线程类型都是引用。每个都可以赋值给一个变量,作为参数传递,或作为返回值从函数中返回。例如,下面是一个存储函数的例子 /* -- example of an anonymous function -- returned as a value -- see http://www.tecgraf.puc-rio.br/~lhf/ftp/doc/hopl.pdf function add(x) return function (y) return (x + y) end end f = add(2) print(type(f), f(10)) function 12 */ 2. Lua线程: 线程是通过调用内嵌函数 coroutine.create(f) 创建的一个协同例程 (co-routine),其中 f 是一个 Lua 函数。线程不会在创建时启动;相反,它是在创建之后使用 coroutine.resume(t) 启动的,其中 t 就是一个线程。每个协同例程都必须使用 coroutine.yield() 偶尔获得其他协同例程的处理器 3. 赋值语句: Lua允许使用多种赋值语句,可以先对表达式进行求值,然后再进行赋值。例如,下面的语句 /* i = 3 a = {1, 3, 5, 7, 9} i, a[i], a[i+1], b = i+1, a[i+1], a[i] print (i, a[3], a[4], b, I) 会生成 4 7 5 nil nil。如果变量列表的个数大于值列表的个数,那么多出的变量都被赋值为 nil;因此,b 就是 nil。如果值的个数多于变量的个数,那么多出的值部分就会简单地丢弃。在 Lua 中,变量名是大小写敏感的,这可以解释为什么 I 的值是 nil */ 4. 块(Chunk): 块可以是任何 Lua 语句序列。块可以保存到文件中,或者保存到 Lua 程序中的字符串中。每个块都是作为一个匿名函数体来执行的。因此,块可以定义局部变量和返回值。 5. 更酷的东西: Lua 具有一个标记-清理垃圾收集器。在 Lua 5.1 中,垃圾收集器是以增量方式工作的。Lua 具有完整的词法闭包。Lua 具有可靠的尾部调用语义 在所有的工程任务中,要在编译性语言和解释性语言之间作出选择,就意味着要在这种环境中对每种语言的优缺点、权重和折中进行评测,并接受所带来的风险 0x2: Lua 提供了高级抽象,却又没失去与硬件的关联 对高性能代码和高级编程的需要进行平衡是 Lua(一种可嵌入式脚本语言)要解决的问题。在需要时我们可以使用编译后的代码来实现底层的功能,然后调用 Lua 脚本来操作复杂的数据。由于 Lua 脚本是与编译代码独立的,因此我们可以单独修改这些脚本。使用 Lua,开发周期就非常类似于 编码 -> 编译 -> 运行 -> 编写脚本 -> 编写脚本 -> 编写脚本 即Lua对编程开发人员来说是脚本语言,同时它对底层解析引擎中会被进行编译、链接实现高性能和硬件绑定 0x3: Lua堆栈 要理解Lua和C++交互,首先要理解Lua堆栈,简单来说,Lua和C/C++语言通信的主要方法是一个无处不在的虚拟栈。栈的特点是先进后出。在Lua中,Lua堆栈就是一个struct,堆栈索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶 Relevant Link: http://www.ibm.com/developerworks/cn/linux/l-lua.html http://www.cocos.com/doc/tutorial/show?id=1474
3. VS2010编译Lua 0x1: 从源代码编译Lua解释器binary 0x2: 从源代码编译Lua静态Lib库: 用于将Lua嵌入到宿主程序中
1. 下载lua源代码: http://www.lua.org/download.html 2. 选择新建 Win32 console project,在wizard界面选择 static Library;不选择Precomplied Header 3. 往工程中添加代码 Add Existing Item,将所有头文件源文件加入project 4. 点击"属性-c/c++-高级-编译为",选择"编译为C++代码(/TP)"(这样才能是CPP调用C文件,才能不会出现链接lib错误) 5. release/debug编译 6. 得到CompileLuaStaticLib.lib 0x3: 将Lua嵌入到宿主程序中 1. 在解决方案中添加一个 Win32 console project,项目名称命名为CompileLuaBinary,后面wizard界面中的选项取消预编译头 2. 添加对头文件的include directory Configuration Properties -> C/C++-> General -> Additional Include Directories 添加: D:\学习资料\Lua\lua-5.3.1\src 3. 源文件加入#pragma comment(lib,"CompileLuaStaticLib.lib")或者 Configuration Properties -> Linker-> Input -> Additional Dependencies 加入: CompileLuaStaticLib.lib 4. Configuration Properties -> Linker-> General -> Additional Libary Include Directories 加入: C:\Users\zhenghan.zh\Documents\Visual Studio 2010\Projects\CompileLuaStaticLib\Release 5. 编写宿主程序代码,对Lua脚本进行进行解释执行 CompileLuaBinary.cpp #pragma comment(lib,"CompileLuaStaticLib.lib") #include "stdafx.h" #include <stdio.h> #include <luaconf.h> #include <lua.h> #include <lualib.h> #include <lauxlib.h> int _tmain(int argc, _TCHAR* argv[]) { lua_State* L = luaL_newstate(); luaL_openlibs(L); luaL_dofile(L, "file.lua"); lua_close(L); return 0; } Relevant Link: http://www.cnblogs.com/dyllove98/p/3162930.html http://www.tuicool.com/articles/Bj2eQ3 http://blog.csdn.net/berdy/article/details/7925040 http://blog.csdn.net/x_iya/article/details/8644180 http://blog.csdn.net/appletreesujie/article/details/12065369
4. 嵌入和扩展: C/C++中执行Lua脚本 Lua除了语法简单并且具有功能强大的表结构(Table)之外,Lua 的强大功能使其可以与宿主语言混合使用。由于 Lua 与宿主语言的关系非常密切,因此 Lua 脚本可以对宿主语言的功能进行扩充。但是这种融合是双赢的:宿主语言同时也可以对 Lua 进行扩充。举例来说,C 函数可以调用 Lua 函数,反之亦然 0x1: 一个简单的 Lua 解释器 #include <stdio.h> #include <lua.h> #include <lauxlib.h> #include <lualib.h> #include <string.h> int main (void) { char buff[256]; int error; lua_State *L = lua_open(); /* opens Lua */ luaL_openlibs(L); luaopen_base(L); /* opens the basic library */ luaopen_table(L); /* opens the table library */ luaopen_io(L); /* opens the I/O library */ luaopen_string(L); /* opens the string lib. */ luaopen_math(L); /* opens the math lib. */ while (fgets(buff, sizeof(buff), stdin) != NULL) { error = luaL_loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0); if (error) { fprintf(stderr, "%s", lua_tostring(L, -1)); lua_pop(L, 1); /* pop error message from the stack */ } } lua_close(L); } //gcc parseLuaInC.c -o parseLuaInC -llua 运行的时候会出现PANIC: unprotected error in call to Lua API (no calling environment),原因是在Lua5.1中不能直接调用luaopen_*函数,解决办法是调用luaL_openlibs() #include <stdio.h> #include <lua.h> #include <lauxlib.h> #include <lualib.h> #include <string.h> int main (void) { char buff[256]; int error; lua_State *L = lua_open(); /* opens Lua */ luaL_openlibs(L); //luaopen_base(L); /* opens the basic library */ //luaopen_table(L); /* opens the table library */ //luaopen_io(L); /* opens the I/O library */ //luaopen_string(L); /* opens the string lib. */ //luaopen_math(L); /* opens the math lib. */ while (fgets(buff, sizeof(buff), stdin) != NULL) { error = luaL_loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0); if (error) { fprintf(stderr, "%s", lua_tostring(L, -1)); lua_pop(L, 1); /* pop error message from the stack */ } } lua_close(L); } //gcc parseLuaInC.c -o parseLuaInC -llua 传输是通过堆栈进行的。从 C 中调用任何 Lua 函数与这段代码类似:使用 lua_getglobal() 来获得函数,将参数压入堆栈,调用 lua_pcall(),然后处理结果。如果 Lua 函数返回 n 个值,那么第一个值的位置在堆栈的 -n 处,最后一个值在堆栈中的位置是 -1。 0x2: 使用 Lua脚本引擎 Lua 脚本引擎本身是由 C 语言写成的,在 C 或 C++ 中使用 Lua 脚本也相当简单 基本的初始化步骤如下 1. 使用 lua_newstate() 创建一个新的 Lua 状态机 2. 若有必要,调用 luaL_openlibs() 函数加载 Lua 的标准库 一旦初始化了 Lua 脚本引擎,你可以通过如下步骤执行一段 Lua 脚本 1. 使用 luaL_loadfile 加载一段 Lua 程序或脚本到 Lua 执行引擎中 2. 调用 lua_pcall 函数执行已加载的脚本 如果想在应用程序中加载Lua脚本并执行其中的函数,你必须执行被加载的Lua程序块(chunk)。刚刚加载的程序块只是编译后存放于Lua的脚本引擎中,并没有被执行。只有在程序块被执行后,Lua中的全局变量和函数才会被创建,在这之前这些任何全局变量和函数对于应用程序来说都不可用。作为Lua引擎的环境由应用程序提供给Lua脚本引擎的任何全局变量和函数也不可用。应用程序必须首先创建变量和函数,并使用函数lua_setglobal()让它们可用 0x3: C/C++解析执行Lua脚本文件 file.lua str = "I am so cool" tbl = {name = "shun", id = 20114442} function add(a,b) return a + b end loadLuafileInC.cpp #include <iostream> #include <string.h> using namespace std; extern "C" { #include "lua.h" #include "lauxlib.h" #include "lualib.h" } int main() { //1.创建Lua状态 lua_State *L = luaL_newstate(); if (L == NULL) { return 0; } //2.加载Lua文件 int bRet = luaL_loadfile(L,"file.lua"); if(bRet) { cout << "load file error" << endl; return 0; } //3.运行Lua文件 bRet = lua_pcall(L,0,0,0); if(bRet) { cout << "pcall error" << endl; return 0; } //4.读取变量 lua_getglobal(L,"str"); string str = lua_tostring(L,-1); cout<<"str = "<<str.c_str()<<endl; //str = I am so cool~ //5.读取table lua_getglobal(L,"tbl"); lua_getfield(L,-1,"name"); str = lua_tostring(L,-1); cout << "tbl:name = " << str.c_str() << endl; //tbl:name = shun //6.读取函数 lua_getglobal(L, "add"); // 获取函数,压入栈中 lua_pushnumber(L, 10); // 压入第一个参数 lua_pushnumber(L, 20); // 压入第二个参数 int iRet= lua_pcall(L, 2, 1, 0);// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。 if (iRet) // 调用出错 { const char *pErrorMsg = lua_tostring(L, -1); cout << pErrorMsg << endl; lua_close(L); return 0; } if (lua_isnumber(L, -1)) //取值输出 { double fValue = lua_tonumber(L, -1); cout << "Result is " << fValue << endl; } //至此,栈中的情况是: //=================== 栈顶 =================== // 索引 类型 值 // 4 int: 30 // 3 string: shun // 2 table: tbl // 1 string: I am so cool~ //=================== 栈底 =================== //7.关闭state lua_close(L); return 0; } //g++ loadLuafileInC.cpp -o loadLuafileInC -llua 需要注意的是:堆栈操作是基于栈顶的,就是说它只会去操作栈顶的,为了更好地C/C++中和Lua交互都是虚拟栈进行的,我们来看几个Lua C API的逻辑流程 1. lua_getglobal(L,"var"): 会执行两步操作 1) 将var放入栈中 2) 由Lua去寻找变量var的值,并将变量var的值返回栈顶(替换var) 2. lua_getfield(L,-1,"name"): 等价于lua_pushstring(L,"name") + lua_gettable(L,-2) 0x4: C/C++中调用Lua函数过程 函数调用流程是 1. 先将函数入栈 2. 参数入栈 3. 然后用lua_pcall调用函数 //此时栈顶为参数,栈底为函数 函数调用结束返回的栈过程大致会是 1. 参数出栈 2. 保存参数 3. 参数出栈 4. 保存参数 .. 5. 函数出栈 6. 函数返回结果入栈 Relevant Link: http://lua-users.org/wiki/CallingLuaFromCpp http://gearx.googlecode.com/svn-history/r8/trunk/vc_lua_error.txt http://< |
请发表评论