首先,你需要下载Lua。你需要从Lua下载页面去下载源代码。如果你需要编译好了的二进制库,你能在LuaBinaries 中找到你想要的库(lib or dll)。
现在,我们需要安装Lua。在Linux下,你应该先解压文件,然后以root用户在命令行键入"make linux"和"make linux install"。如果你需要帮助,请参考源代码文件夹中的INSTALL文件。现在,我下载了windows平台下的二进制库包并把它们解压到"C:\Program Files\lua5.1"。
在Linux下不需要我们做任何设置,但是在windows平台下我们必须配置Visual C++,以便让编译器和连接器找到Lua文件。
打开Visual C++,选择Tools菜单中的选项菜单。
展开"项目",并选择"VC++ 目录"。
选择"包含文件",添加一个新路径"C:\Program Files\lua5.1\include"。
在选择"库文家",添加路径"C:\Program Files\lua5.1\lib\dll"(这里假设你下载的库为dll,你也可以下载静态链接库)。
确定。
现在你可以开始编译你的第一个Lua应用了。
使用Lua开始你的第一个程序
这个程序简短且直接,下面做一点说明:
lua_open()返回一个指向Lua解释器的一个指针。
luaL_openlibs()用于装载Lua库,它提供了一些简单的函数,如:print。
通过调用luaL_dofile()执行脚本。它的作用是读取并解释脚本。
最后,通过lua_close()函数关闭Lua。
保存文件为luatest.cpp。如果你直接使用C而不是C++,将文件名改为luatest.c,然后将extern "C"删除。
#include <stdio.h>
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}/* Lua解释器指针 */
lua_State* L;int main ( int argc, char *argv[] )
{
/* 初始化Lua */
L = lua_open(); /* 载入Lua基本库 */
luaL_openlibs(L); /* 运行脚本 */
luaL_dofile(L, "test.lua"); /* 清除Lua */
lua_close(L); /* 暂停 */
printf( "Press enter to exit…" );
getchar(); return 0;
}
下面是test.lua的内容。-- simple test
print "Hello, World!"
编译
在Linux下,在命令行键入:
g++ luatest.cpp -llua -ldl -o luatest然后,键入下列命令运行:
./luatest如果没有问题,程序将在终端输出Hello, World!
在Lua中定义函数是相当简单的。Lua函数以关键字"function"开头,后面跟随函数名,然后是参数列表。函数定义以关键字"end"结束。Lua函数能够接受多个参数,而且可以返回多个参数。
下面是一个实现两个数相加并返回结果的Lua函数。我们将它保存为"add.lua"文件。
-- add two numbers
function add ( x, y )
return x + y
end
在Lua入门中,我们已经知道调用luaL_dofile()就是执行脚本。因为在本文中我们只定义了一个函数,故只需简单地调用luaL_dofile()函数就能执行add函数。
我在前面已经说过,Lua函数能够接受多个参数,返回多个结果。这是用栈来实现的。
为了调用一个Lua函数,首先需要将函数压入栈中。再将参数压入。然后,调用lua_call()去调用Lua函数。函数调用完成之后,返回值存在于栈中。所有这些步骤将被展示在luaadd()函数定义中。
- 调用lua_getglobal()将add()函数压入栈中。
- 调用lua_pushnumber()将第一个参数x压入栈中。
- 同样,调用lua_pushnumber()将第二个参数y压入栈中。
- 调用lua_call(),其参数的意思是两个参数,一个返回值。
- 现在,我们可以利用lua_tointeger()获得整型返回值。
- 最后,调用lua_pop()将值从栈中移出。
保存文件为luaadd.cpp。如果你直接使用C而不是C++,将文件名改为luaadd.c,然后将extern "C"删除。
#include <stdio.h>
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
/* 指向Lua解释器的指针 */
lua_State* L;
int luaadd ( int x, int y )
{
int sum;
/* 通过名字得到Lua函数 */
lua_getglobal(L, "add");
/* 第一个参数 */
lua_pushnumber(L, x);
/* 第二个参数 */
lua_pushnumber(L, y);
/* 调用函数,告知有两个参数,一个返回值 */
lua_call(L, 2, 1); /* 调用函数,告知有两个参数,一个返回值 */ lua_pcall(L, 2, 1,0); //调用此函数会自动将参数弹出栈,只保留返回值 最后参数是错误检测;
/* 得到结果 */
sum = (int)lua_tointeger(L, -1);
lua_pop(L, 1); 返回值出栈,恢复栈中的元素
return sum;
}
int main ( int argc, char *argv[] )
{
int sum;
/* 初始化Lua */
L = lua_open();
/* 载入Lua基本库 */
luaL_openlibs(L);
/* 载入脚本 */
luaL_dofile(L, "add.lua");
/* 调用Lua函数 */
sum = luaadd( 10, 15 );
/* 显示结果 */
printf( "The sum is %d\n", sum );
/* 清除Lua */
lua_close(L);
/* 暂停 */
printf( "Press enter to exit…" );
getchar();
return 0;
}
编译
在Linux下,在命令行键入:
g++ luaadd.cpp -llua -ldl -o luaadd
然后,键入下列命令运行:
./luaadd
如果没有问题, 程序将显示结果为: "The sum is 25"。
在Visual C++你将需要进行下列步骤:
- 创建一个新的空Win32控制台应用工程。
- 将"luatest.cpp"加入你的工程。
- 选择项目菜单中的属性菜单。
- 在"连接器"的"输入"栏目的"附加依赖项"中输入"lua5.1.lib"。
- 确定。
此时,按F7构建程序。
如果你采用的是dll库,请确保将其放在应用程序的目录中或者windows系统能够找到它的地方。如果你采用的是静态连接库,则不需要。
全局变量
全局变量在Lua中也很好处理。就像我们看到的,lua_getglobal()将一个Lua全局变量压入栈中。例如,在Lua脚本中包含一个全局变量z,下面代码的功能就是得到它的值:
lua_getglobal(L, "z");
z = (int)lua_tointeger(L, -1);
lua_pop(L, 1);
相应地,lua_setglobal()函数能够设置全局变量地值。下面这段代码演示了如何将Lua全局变量z的值变为10:
lua_pushnumber(L, 10);
lua_setglobal(L, "z");
应该记住:在Lua中,我们没有必要显式定义一个全局变量。如果全局变量不存在,调用lua_setglobal()将为你创建一个。
错误检查
if (!lua_isnumber(L, i)) {
lua_pushstring(L, "Incorrect argument to 'average'");
lua_error(L);
}
添加这样的检查很容易,同时这样也让调试更容易。当处理用两种不同语言编写的程序的时候,这显得相当重要。
// 调用lua的函数,都是通过压栈出栈来完成的
// 为表执行一个t[k]=v的操作,则需要先将k压栈,再将v压栈,再调用操作函数
// 这个操作函数会使用栈上的元素,并“可能”将弹出元素和压入元素
// lua_rawset直接赋值(不触发metamethods方法)。
// lua_rawset/lua_settable使用:
// 它从栈中获取参数。以table在栈中的索引作为参数,
// 并将栈中的key和value出栈。
// lua_pushnumber函数调用之前,
// table是在栈顶位置(索引为-1)。index和value入栈之后,
// table索引变为-3。
lua_pushnumber( state, 1 );
lua_pushnumber( state, 45 );
lua_rawset( state, -3 );
// set the number of elements (index to the last array element)
// lua_pushliteral压入一个字符串,不需要指定长度
// 如果lua_pushlstring,则需要指定长度
lua_pushliteral( state, "n" );
lua_pushnumber( state, 2 );
lua_rawset( state, -3 );
// set the name of the array that the script will access
// Pops a value from the stack and sets it as the new value of global name.
// 从栈顶弹出一个值,并将其设置全局变量"arg"的新值。
lua_setglobal( state, "arg" );
//定义函数(返回table)
int func_return_table(lua_State *L)
{
lua_newtable(L);//创建一个表格,放在栈顶
lua_pushstring(L, "mydata");//压入key
lua_pushnumber(L,66);//压入value
lua_settable(L,-3);//弹出key,value,并设置到table里面去 lua_pushstring(L, "subdata");//压入key
lua_newtable(L);//压入value,也是一个table
lua_pushstring(L, "mydata");//压入subtable的key
lua_pushnumber(L,53);//value
lua_settable(L,-3);//弹出key,value,并设置到subtable lua_settable(L,-3);//这时候父table的位置还是-3,弹出key,value(subtable),并设置到table里去
lua_pushstring(L, "mydata2");//同上
lua_pushnumber(L,77);
lua_settable(L,-3);
return 1;//堆栈里现在就一个table.其他都被弹掉了。
}
如果要返回一个table:
lua_newtable(L); //创建一个表格,放在栈顶
lua_pushstring(L, "mydata"); //压入key
lua_pushnumber(L,66); //压入value
lua_settable(L,-3); //弹出key,value,并设置到table里面去
lua_pushstring(L, "subdata"); //压入key
lua_newtable(L); //压入value,也是一个table
lua_pushstring(L, "mydata"); //压入subtable的key
lua_pushnumber(L,53); //value
lua_settable(L,-3); //弹出key,value,并设置到subtable
lua_settable(L,-3); //这时候父table的位置还是-3,弹出key,value(subtable),并设置到table里去
lua_pushstring(L, "mydata2"); //同上
lua_pushnumber(L,77);
lua_settable(L,-3);
return 1; //堆栈里现在就一个table.其他都被弹掉了。
如果要返回一个数组,用如下代码:(注意那个关于trick的注释,我在等官方的解释。经过验证,这个问题只在windows版本调用dll中方法的时候出现。WinCE正常)
lua_pushstring(L,"arri");
lua_newtable(L);
{
//a trick:otherwise the lua engine will crash. This element is invisible in Lua script
lua_pushnumber(L,-1);
lua_rawseti(L,-2,0);
for(int i = 0; i < arri.size();i++)
{
lua_pushnumber(L,arri);
lua_rawseti(L,-2,i+1);
}
}
lua_settable(L,-3);
这样产生的数组可以在Lua中如下遍历:
for i,v in ipairs(data.arri) do
print(v)
end
或者是
for i=1,table.getn(data.arri) do
print(data.arri)
end
只有数组才能这样,name,value构成的Record不行,table.getn也只对数组有效。
lua中调用C函数
将用C++创建一个函数,告诉Lua解释器它的情况,最后从Lua中调用它并使用其结果。我在后面也将谈一谈Lua程序中的错误检查。
定义函数
第一步是定义函数。所有在Lua中被调用的C/C++函数将使用下面一类指针进行调用:
typedef int (*lua_CFunction) (lua_State *L);换句话说,函数必须要以Lua解释器作为唯一的参数,并且返回一个唯一的整数。由于用一个Lua解释器作为参数,因此函数实际上能够从栈中取得任意数量的参数。在后面我们将看到,返回的整数实际上是被压入栈的值的个数。通过如此容易的封装,就能满足你在Lua中调用C++函数的需求。
下面给出的C++函数average()演示了如何接受多个参数且返回超过一个值。记住,该函数是一个与上面typedef相匹配的函数。
lua_gettop函数返回栈顶的索引值。因为在Lua中栈是从1开始编号的,因此该函数获得的值就是参数的个数。
在for循环中计算所有参数之和。
调用lua_pushnumber()将参数的平均值压栈。
将参数之和压入栈中。
最后,函数返回2,说明有两个返回值在栈中。
现在C++函数已经被定义好了,我们必须将它告诉Lua解释器。这将在main函数中初始化Lua解释器和载入库完成之后完成:
/* 注册函数 */
lua_register(L, "average", average);
保存文件为luaavg.cpp。如果你直接使用C而不是C++,将文件名改为luaavg.c,然后将extern "C"删除。
#include <stdio.h>
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}/* 指向Lua解释器的指针 */
lua_State* L;static int average(lua_State *L)
{
/* 得到参数个数 */
int n = lua_gettop(L);
double sum = 0;
int i; /* 循环求参数之和 */
for (i = 1; i <= n; i++)
{
/* 求和 */
sum += lua_tonumber(L, i);
} /* 压入平均值 */
lua_pushnumber(L, sum / n); /* 压入和 */
lua_pushnumber(L, sum); /* 返回返回值的个数 */
return 2;
}int main ( int argc, char *argv[] )
{
/* 初始化Lua */
L = lua_open(); /* 载入Lua基本库 */
luaL_openlibs(L); /* 注册函数 */
lua_register(L, "average", average); /* 运行脚本 */
luaL_dofile(L, "avg.lua"); /* 清除Lua */
lua_close(L); /* 暂停 */
printf( "Press enter to exit…" );
getchar(); return 0;
}
下面是以5个参数调用average函数并且显示两个返回值的Lua脚本,我们将其保存为avg.lua:-- call a C++ function
avg, sum = average(10, 20, 30, 40, 50)
print("The average is ", avg)
print("The sum is ", sum)
lua是一个跨平台的脚本语言,在linux下也可以使用,但是我目前只使用过windows,这篇文章也是在windows下的编程。
1.lua的简介
百度百科的简介:
Lua 是一个小巧的脚本语言。是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。Lua 有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。
2.下载lua
lua官网的下载页面:http://www.lua.org/download.html
3.lua包含的文件
下载后的lua是一个压缩包,解压后可以获得doc和src文件夹,还有makefile和readme文件。
doc文件夹下主要有lua的api文档
src文件夹包含lua的源码
(lua压缩包并不包含lib文件,lib文件直接通过vs去编译lua源代码就可以生成lib了,方法见第四步)
4.生成lua lib文件。
使用visual studio添加静态库项目(静态库项目的生成方法见此链接)
5.使用lua
在生成lib文件的步骤中的链接能看到如何在项目中使用lib,lua的使用同样需要使用lib,除了添加lib,还需要添加lauxlib.h,lua.h,luaconf.h,lualib.h这几个头文件。
关于lua的使用,我直接用代码说明吧。
以下代码演示了lua的栈操作 ,执行内存脚本,加载脚本中定义的变量,执行脚本中定义的无参函数,执行脚本中定义的有参函数,脚本中调用C++函数等操作。
[cpp] view plaincopy
1. // lua_test.cpp : 定义控制台应用程序的入口点。
2. //
3.
4. #include "stdafx.h"
5.
6. #include <iostream>
7. #include <string>
8.
9. extern "C"
10. {
11. #include "lua.h"
12. #include "lualib.h"
13. #include "lauxlib.h"
14. }
15.
16. using namespace std;
17.
18.
19. int fcAdd(lua_State *lu)
20. {
21. int a = lua_tointeger(lu, 1);
22. int b = lua_tointeger(lu, 2);
23. //结果压栈
24. return 1; //返回1个结果
25. }
26.
27.
28. int _tmain(int argc, _TCHAR* argv[])
29. {
30.
31. int nret = 0;
32.
33. /*创建Lua对象*/
34. lua_State* lu = luaL_newstate();
35. luaL_openlibs(lu);
36.
37.
38. /*栈操作 */
39. //压入一个int类型的数据,数值为1
40. lua_pushinteger(lu, 1);
41. //压入一个int类型的数据,数值为3
42. lua_pushinteger(lu, 3);
43. //获得栈中第一个元素(也就是刚刚放入的第一个元素“1”)
44. int n = lua_tointeger(lu, 1);
45. //获得栈中第二个元素
46. n = lua_tointeger(lu, 2);
47. //获得栈中的元素总数(如果总数为0,表示空。前面放入两个,这里的值为2)
48. int nStack = lua_gettop(lu);
49. //剔除栈中元素(剔除两个,栈空)
50. lua_pop(lu, 2);
51. //获得栈中元素总数
52. nStack = lua_gettop(lu);
53.
54. /*执行内存脚本*/
55.
56. //在lua脚本中,print函数是打印操作,print("Hello world")相当于打印一句Hello world。此时str相当于脚本中的打印操作
57. "print (\"Hello world!\")";
58. //把str加载到内存中,其中最后一个参数"name"是chunk名字,用于debug或者错误信息的标识
59. "line");
60. //调用函数。PS:lua_pcall的参数中n-nargs表示参数的个数,r-nresults表示...,errfunc表示
61. lua_pcall(lu, 0,0,0);
62.
63. /*加载脚本中定义的变量 */
64. "../../luaScripts/test.lua");
65. //压入aa名字(必须先压入需要取值的数据名,然后通过取值函数(例如tointeger) 从栈中取值)
66. "aa");
67. //压入bb名字
68. "bb");
69. //前面已经演示过传入正数索引调用tointeger,现在演示如果传入的参数为负数,则会从后开始取值
70. int bb = lua_tointeger(lu, -1);
71. int aa = lua_tointeger(lu, -2);
72.
73. /*执行脚本中定义的无参函数 */
74. //压入hello函数名字,其实无论函数还是变量,都是通过压入名字然后去调用的。
75. "hello");
76. //参数nargs为0,表示调用的是无参的函数
77. nret = lua_pcall(lu, 0,0,0);
78.
79. /*执行脚本中定义的有参函数 */
80. "fadd");
81. //压入即将作为参数的对象
82. lua_pushnumber(lu, aa);
83. lua_pushnumber(lu, bb);
84. //调用函数并获得返回结果(如果nret为0,表示调用成功,返回值会放入栈,通过取值即可获得。否则失败,失败时lua会产生失败信息放入栈,通过取值就可以获得错误信息)
85. nret = lua_pcall(lu, 2,1,0);
86.
87.
88. "1 元素总数:" << lua_gettop(lu) << endl;
89. if (nret != 0)
90. {
91. //打印错误信息
92. const char *pc = lua_tostring(lu, -1);
93. cout << pc;
94.
95. "2 元素总数:" << lua_gettop(lu) << endl;
96. }
97. else
98. {
99. //打印结果
100. nret = lua_tointeger(lu, -1);
101. "调用脚本函数:" << endl;
102. " + " << bb << " = " << nret << endl;
103.
104. "3 元素总数:" << lua_gettop(lu) << endl;
105. //?
106. lua_pop(lu, 1);
107. "4 元素总数:" << lua_gettop(lu) << endl;
108. }
109.
110. /*脚本中调用C++函数*/
111. //压入c++函数
112. lua_pushcfunction(lu, fcAdd);
113. //取出fcAdd函数使用
114. "fcAdd");
115. //压入脚本函数
116. "fc");
117. //压入参数
118. lua_pushnumber(lu, aa);
119. lua_pushnumber(lu, bb);
120. //调用函数
121. nret = lua_pcall(lu, 2,1,0);
122.
123. if (nret != 0)
124. {
125. const char *pc = lua_tostring(lu, -1);
126. cout << pc;
127. }
128. else
129. {
130. nret = lua_tointeger(lu, -1);
131. "调用C++函数:" << endl;
132. " + " << bb << " = " << nret << endl;
133. lua_pop(lu, 1);
134. }
135.
136. lua_close(lu);
137. "pause");
138. return 0;
139. }
(代码参考如下博客:,我只是做了更详细的说明和使用体验,部分言语可能不当,因为此时我还是个初学者。)
6.lua脚本的创建
lua脚本其实就是个文本文件,之后后缀名改为lua了,例如第五步的test.lua脚本,这里列出test.lua脚本的源码
[cpp] view plaincopy
1. aa=2;
2. bb=3;
3. function hello(a,b)
4. "Hello in script!")
5. end
6. function fadd(a,b)
7. return a+b
8. end
9. function fc(a,b)
10. return fcAdd(a,b)
11. end