首先,你需要下载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添加静态库项目(静态库项目的生成方法见此链接http://blog.csdn.net/xv_ly15/article/details/8548791)
5.使用lua
在生成lib文件的步骤中的链接能看到如何在项目中使用lib,lua的使用同样需要使用lib,除了添加lib,还需要添加lauxlib.h,lua.h,luaconf.h,lualib.h这几个头文件。
关于lua的使用,我直接用代码说明吧。
以下代码演示了lua的栈操作 ,执行内存脚本,加载脚本中定义的变量,执行脚本中定义的无参函数,执行脚本中定义的有参函数,脚本中调用C++函数等操作。
- // lua_test.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include <iostream>
- #include <string>
- extern "C"
- {
- #include "lua.h"
- #include "lualib.h"
- #include "lauxlib.h"
- }
- using namespace std;
- int fcAdd(lua_State *lu)
- {
- int a = lua_tointeger(lu, 1);
- int b = lua_tointeger(lu, 2);
- lua_pushnumber(lu, a+b); //结果压栈
- return 1; //返回1个结果
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- int nret = 0;
- /*创建Lua对象*/
- lua_State* lu = luaL_newstate();
- luaL_openlibs(lu);
- /*栈操作 */
- //压入一个int类型的数据,数值为1
- lua_pushinteger(lu, 1);
- //压入一个int类型的数据,数值为3
- lua_pushinteger(lu, 3);
- //获得栈中第一个元素(也就是刚刚放入的第一个元素“1”)
- int n = lua_tointeger(lu, 1);
- //获得栈中第二个元素
- n = lua_tointeger(lu, 2);
- //获得栈中的元素总数(如果总数为0,表示空。前面放入两个,这里的值为2)
- int nStack = lua_gettop(lu);
- //剔除栈中元素(剔除两个,栈空)
- lua_pop(lu, 2);
- //获得栈中元素总数
- nStack = lua_gettop(lu);
- /*执行内存脚本*/
- //在lua脚本中,print函数是打印操作,print("Hello world")相当于打印一句Hello world。此时str相当于脚本中的打印操作
- string str = "print (\"Hello world!\")";
- //把str加载到内存中,其中最后一个参数"name"是chunk名字,用于debug或者错误信息的标识
- luaL_loadbuffer(lu, str.c_str(), str.length(), "line");
- //调用函数。PS:lua_pcall的参数中n-nargs表示参数的个数,r-nresults表示...,errfunc表示
- lua_pcall(lu, 0,0,0);
- /*加载脚本中定义的变量 */
- nret = luaL_dofile(lu, "../../luaScripts/test.lua");
- //压入aa名字(必须先压入需要取值的数据名,然后通过取值函数(例如tointeger) 从栈中取值)
- lua_getglobal(lu, "aa");
- //压入bb名字
- lua_getglobal(lu, "bb");
- //前面已经演示过传入正数索引调用tointeger,现在演示如果传入的参数为负数,则会从后开始取值
- int bb = lua_tointeger(lu, -1);
- int aa = lua_tointeger(lu, -2);
- /*执行脚本中定义的无参函数 */
- //压入hello函数名字,其实无论函数还是变量,都是通过压入名字然后去调用的。
- lua_getglobal(lu, "hello");
- //参数nargs为0,表示调用的是无参的函数
- nret = lua_pcall(lu, 0,0,0);
- /*执行脚本中定义的有参函数 */
- lua_getglobal(lu, "fadd");
- //压入即将作为参数的对象
- lua_pushnumber(lu, aa);
- lua_pushnumber(lu, bb);
- //调用函数并获得返回结果(如果nret为0,表示调用成功,返回值会放入栈,通过取值即可获得。否则失败,失败时lua会产生失败信息放入栈,通过取值就可以获得错误信息)
- nret = lua_pcall(lu, 2,1,0);
- cout << "1 元素总数:" << lua_gettop(lu) << endl;
- if (nret != 0)
- {
- //打印错误信息
- const char *pc = lua_tostring(lu, -1);
- cout << pc;
- cout << "2 元素总数:" << lua_gettop(lu) << endl;
- }
- else
- {
- //打印结果
- nret = lua_tointeger(lu, -1);
- cout << "调用脚本函数:" << endl;
- cout << aa << " + " << bb << " = " << nret << endl;
- cout << "3 元素总数:" << lua_gettop(lu) << endl;
- //?
- lua_pop(lu, 1);
- cout << "4 元素总数:" << lua_gettop(lu) << endl;
- }
- /*脚本中调用C++函数*/
- //压入c++函数
- lua_pushcfunction(lu, fcAdd);
- //取出fcAdd函数使用
- lua_setglobal(lu, "fcAdd");
- //压入脚本函数
- lua_getglobal(lu, "fc");
- //压入参数
- lua_pushnumber(lu, aa);
- lua_pushnumber(lu, bb);
- //调用函数
- nret = lua_pcall(lu, 2,1,0);
- if (nret != 0)
- {
- const char *pc = lua_tostring(lu, -1);
- cout << pc;
- }
- else
- {
- nret = lua_tointeger(lu, -1);
- cout << "调用C++函数:" << endl;
- cout << aa << " + " << bb << " = " << nret << endl;
- lua_pop(lu, 1);
- }
- lua_close(lu);
- std::system("pause");
- return 0;
- }
(代码参考如下博客:http://blog.csdn.net/shellching/article/details/7202913,我只是做了更详细的说明和使用体验,部分言语可能不当,因为此时我还是个初学者。)
6.lua脚本的创建
lua脚本其实就是个文本文件,之后后缀名改为lua了,例如第五步的test.lua脚本,这里列出test.lua脚本的源码
- aa=2;
- bb=3;
- function hello(a,b)
- print ("Hello in script!")
- end
- function fadd(a,b)
- return a+b
- end
- function fc(a,b)
- return fcAdd(a,b)
- end