VC和VS调用Lua设置以及Lua C API使用。
通过c++调用lua 脚本,
环境VC++6.0
lua sdk 5.1.4
在调用前先认识几个函数。
1.调用lua_open()将创建一个指向Lua解释器的指针。
2. luaL_openlibs()函数加载Lua库。
3.使用luaL_dofile()加载脚本并运行脚本。
4. lua_close()来关闭Lua指向解释器的指针。
5.调用lua_getglobal()将add()函数压入栈顶,add()为lua函数。
6.第一个参数x,通过调用lua_pushnumber()入栈。
7.再次调用lua_pushnumber()将第二个参数入栈。
8.使用lua_call()调用Lua函数。
9.调用lua_tonumber()从栈顶取得函数的返回值。
10. lua_pop()移除栈顶的值。
vs设置:
一共有三个点要注意:
1.加入LUA的头文件核库文件到VS2005的VC++目录中。增加include目录即可。
2.引入lib
#pragma comment(lib, "lua51.lib")
#pragma comment(lib, "lua5.1.lib")
3.包含头文件,这个要使用这样的形式,不然可能出现
Win32 ------
1>Compiling...
1>test.cpp
1>Linking...
1>test.obj : error LNK2019: unresolved external symbol "void __cdecl lua_close(struct lua_State *)" (?lua_close@@YAXPAUlua_State@@@Z) referenced in function _main
1>test.obj : error LNK2019: unresolved external symbol "int __cdecl lua_pcall(struct lua_State *,int,int,int)" (?lua_pcall@@YAHPAUlua_State@@HHH@Z) referenced in function _main
1>test.obj : error LNK2019: unresolved external symbol "int __cdecl luaL_loadfile(struct lua_State *,char const *)" (?luaL_loadfile@@YAHPAUlua_State@@PBD@Z) referenced in function _main
1>test.obj : error LNK2019: unresolved external symbol "void __cdecl luaL_openlibs(struct lua_State *)" (?luaL_openlibs@@YAXPAUlua_State@@@Z) referenced in function _main
1>test.obj : error LNK2019: unresolved external symbol "struct lua_State * __cdecl luaL_newstate(void)" (?luaL_newstate@@YAPAUlua_State@@XZ) referenced in function _ma
解决方法如下:
extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
最后,应该就可以了。
1.VS环境设置,增加Lua安装目录下的 include、lib路径。
vc6.0设置:
(1.)工程->设置->C/C++->分类里选->预处理器->附加包含路径下粘贴lua路径->F:\lua-5.1.5\src
(2.)把F:\lua-5.1.5\src里的lua51.lib文件复制到工程目录里
把F:\lua-5.1.5\src里的lua.exe文件复制到工程Debug/Release目录里
代码
add.lua
1 function add ( x, y )
2 return x + y
3 end
4
main.c
#include <stdio.h> #include <lua.hpp> #pragma comment(lib,"lua51.lib") lua_State * L; int luaadd ( int x, int y ) { int sum; //函数名 lua_getglobal(L, "add"); //第一个参数压栈 lua_pushnumber(L, x); //第二个参数压栈 lua_pushnumber(L, y); //调用函数 lua_call(L, 2, 1); //得到返回值 sum = (int)lua_tonumber(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"); //调用函数 sum = luaadd( 10, 11); // print the result printf( "The sum is %d\n", sum ); //关闭 释放资源 lua_close(L); return 0; }
结果21.
//连接lua的函数库
#pragma comment(lib,"lua51.lib")
参考:http://hi.baidu.com/135006819/item/42801f1061537e2cf7625c3d
或者包含Lua头文件:
#include <stdio.h> #include <string.h> extern "C" { #include <lua.h> #include <lauxlib.h> #include <lualib.h> }; #pragma comment(lib,"lua51.lib") #pragma comment(lib, "lua5.1.lib") int main(void) { const char* buff = "print(\"hello\")"; int error; lua_State* L = luaL_newstate(); luaL_openlibs(L); error = luaL_loadbuffer(L,buff,strlen(buff),"line") || lua_pcall(L,0,0,0); int s = lua_gettop(L); if (error) { fprintf(stderr,"%s",lua_tostring(L,-1)); lua_pop(L,1); } lua_close(L); return 0; }
所有C ApI函数都要求传入一个lua_State指针。
lua_pcall函数会将程序块从栈中弹出,并在保护模式下运行该程序块。执行成功返回0,否则将错误信息压入栈中。
2). Lua库中没有定义任何全局变量,而是将所有的状态都保存在动态结构lua_State中,后面所有的C API都需要该指针作为第一个参数。
3). luaL_openlibs函数是用于打开Lua中的所有标准库,如io库、string库等。
4). luaL_loadbuffer编译了buff中的Lua代码,如果没有错误,则返回0,同时将编译后的程序块压入虚拟栈中。
5). lua_pcall函数会将程序块从栈中弹出,并在保护模式下运行该程序块。执行成功返回0,否则将错误信息压入栈中。
6). lua_tostring函数中的-1,表示栈顶的索引值,栈底的索引值为1,以此类推。该函数将返回栈顶的错误信息,但是不会将其从栈中弹出。
7). lua_pop是一个宏,用于从虚拟栈中弹出指定数量的元素,这里的1表示仅弹出栈顶的元素。
8). lua_close用于释放状态指针所引用的资源。
如果将Lua作为C代码来编译,并在c++中使用它,那么可以包含lua.hpp来代替lua.h
hua.hpp定义为:
// lua.hpp
// Lua header files for C++
// <<extern "C">> not supplied automatically because Lua also compiles as C++
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
栈
当在Lua和C之间交换数据时我们面临着两个问题:动态与静态类型系统的不匹配和自动与手动内存管理的不一致。
在Lua中,我们写下a[k]=v时,k和v可以有几种不同的类型(由于metatables的存在,a也可能有不同的类型)。如果我们想在C中提供类似的操作,无论怎样,操作表的函数(settable)必定有一个固定的类型。我们将需要几十个不同的函数来完成这一个的操作(三个参数的类型的每一种组合都需要一个函数)。
我们可以在C中声明一些union类型来解决这个问题,我们称之为lua_Value,它能够描述所有类型的Lua值。然后,我们就可以这样声明settable
void lua_settable (lua_Value a, lua_Value k, lua_Value v);
这个解决方案有两个缺点。第一,要将如此复杂的类型映射到其它语言可能很困难;Lua不仅被设计为与C/C++易于交互,Java,Fortran以及类似的语言也一样。第二,Lua负责垃圾回收:如果我们将Lua值保存在C变量中,Lua引擎没有办法了解这种用法;它可能错误地认为某个值为垃圾并收集他。
因此,Lua API没有定义任何类似lua_Value的类型。替代的方案,它用一个抽象的栈在Lua与C之间交换值。栈中的每一条记录都可以保存任何Lua值。无论你何时想要从Lua请求一个值(比如一个全局变量的值),调用Lua,被请求的值将会被压入栈。无论你何时想要传递一个值给Lua,首先将这个值压入栈,然后调用Lua(这个值将被弹出)。我们仍然需要一个不同的函数将每种C类型压入栈和一个不同函数从栈上取值(译注:只是取出不是弹出),但是我们避免了组合式的爆炸(combinatorial explosion)。另外,因为栈是由Lua来管理的,垃圾回收器知道那个值正在被C使用。几乎所有的API函数都用到了栈。正如我们在第一个例子中所看到的,luaL_loadbuffer把它的结果留在了栈上(被编译的chunk或一条错误信息);lua_pcall从栈上获取要被调用的函数并把任何临时的错误信息放在这里。
Lua以一个严格的LIFO规则(后进先出;也就是说,始终存取栈顶)来操作栈。当你调用Lua时,它只会改变栈顶部分。你的C代码却有更多的自由;更明确的来讲,你可以查询栈上的任何元素,甚至是在任何一个位置插入和删除元素。
1). 压入元素:
Lua针对每种C类型,都有一个C API函数与之对应,如:
void lua_pushnil(lua_State* L); --nil值
void lua_pushboolean(lua_State* L, int b); --布尔值
void lua_pushnumber(lua_State* L, lua_Number n); --浮点数
void lua_pushinteger(lua_State* L, lua_Integer n); --整型
void lua_pushlstring(lua_State* L, const char* s, size_t len); --指定长度的内存数据
void lua_pushstring(lua_State* L, const char* s); --以零结尾的字符串,其长度可由strlen得出。
lua中的字符串不是以零结尾的,他们可以包含任意二进制数据,因此,它们必须同时保存一个显式的长度。将字符串压入栈中的基本函数式lua_pushlstring,他要求传入一个显式的长度。对于零结尾的字符串,可以使用函数lua_pushstring.这个函数通过strlen来计算字符串的长度。Lua不会持有执行外部字符串的指针(也不会持有执行任何其他外部对象的职责,但除了C函数,因为C函数总是精态的。)
对于字符串数据,Lua不会持有他们的指针,而是调用在API时生成一个内部副本,因此,即使在这些函数返回后立刻释放或修改这些字符串指针,也不会有任何问题。
在向栈中压入数据时,可以通过调用下面的函数判断是否有足够的栈空间可用,一般而言,Lua会预留20个槽位,对于普通应用来说已经足够了,除非是遇到有很多参数的函数。
int lua_checkstack(lua_State* L, int extra) --期望得到extra数量的空闲槽位,如果不能扩展并获得,返回false。
2). 查询元素:
API使用“索引”来引用栈中的元素,第一个压入栈的为1,第二个为2,依此类推。我们也可以使用负数作为索引值,其中-1表示为栈顶元素,-2为栈顶下面的元素,同样依此类推。
Lua提供了一组特定的函数用于检查返回元素的类型,都有着相同的形式:
int lua_is*(lua_State *L,int index);
如:
int lua_isboolean (lua_State *L, int index);
int lua_iscfunction (lua_State *L, int index);
int lua_isfunction (lua_State *L, int index);
int lua_isnil (lua_State *L, int index);
int lua_islightuserdata (lua_State *L, int index);
int lua_isnumber (lua_State *L, int index);
int lua_isstring (lua_State *L, int index);
int lua_istable (lua_State *L, int index);
int lua_isuserdata (lua_State *L, int index);
以上函数,成功返回1,否则返回0。需要特别指出的是,对于lua_isnumber而言,不会检查值是否为数字类型,而是检查值是否能转换为数字类型。lua_issting也具有同样的行为,因此,对于任意数字,lua_isstring都返回真。
Lua还提供了一个函数lua_type,用于获取元素的类型,函数原型如下:
int lua_type (lua_State *L, int index); 注意返回的int。
该函数的返回值为一组常量值,分别是:LUA_TNIL、LUA_TNUMBER、LUA_TBOOLEAN、LUA_TSTRING、LUA_TTABLE、LUA_TFUNCTION、LUA_TUSERDATA、LUA_TTHREAD和LUA_TLIGHTUSERDATA。这些常量通常用于switch语句中。另外,若要检查一个元素是否是真正的字符串或数字,也可以使用这个函数。
lua_to*函数用于从栈中取一个值。
int lua_toboolean (lua_State *L, int index);
lua_CFunction lua_tocfunction (lua_State *L, int index);
lua_Integer lua_tointeger (lua_State *L, int index);
const char *lua_tolstring (lua_State *L, int index, size_t *len);
lua_Number lua_tonumber (lua_State *L, int index);
const void *lua_topointer (lua_State *L, int index);
const char *lua_tostring (lua_State *L, int index);
void *lua_touserdata (lua_State *L, int index);
--string类型返回字符串长度,table类型返回操作符‘#‘等同的结果,userdata类型返回分配的内存块长度。
size_t lua_objlen (lua_State *L, int index);
对于上述函数,如果调用失败,lua_toboolean、lua_tonumber、lua_tointeger和lua_objlen均返回0,而其他函数则返回NULL。在很多时候0不是一个很有效的用于判断错误的值,但是ANSI C没有提供其他可以表示错误的值。因此对于这些函数,在有些情况下需要先使用lua_is*系列函数判断是否类型正确,而对于剩下的函数,则可以直接通过判断返回值是否为NULL即可。
所有lua_tolstring返回的字符串在其末尾都会有一个额外的零,不过这些字符串的中间也有可能是0.字符串长度
通过第三个参数len返回,这才是真正的字符串长度。进一步说,假设栈顶的值是一个字符串,如下总是为真:
size_t l;
const char*=lua_tolstring(L,-1,&1);//任何lua字符串
assert(s[1]==‘\0‘)
assert(strlen(s)<=l)
如果不需要长度信息,可以将第三个参数设为null来调用lua_tolstring,或者使用宏lua_tostring.
这个宏就是用NULL作为第3个参数来调用lua_tolstring.
为了演示这些函数作用,以下代码实现了一个有用的辅助函数,他会打印整个栈的内容,这个函数会由下而上的遍历栈,并根据每个元素的类型打印其值,字符串放在一对单引号内打印,数字使用格式"%g"来打印,其他值(table,函数等)则只打印类型。其中,lua_typename可将一个类型编码转换为一个类型名。将 lua_type的返回值int类型换成位字符串。
static void stackDump(lua_State* L) { int top = lua_gettop(L); for (int i = 1; i <= top; ++i) { int t = lua_type(L,i); switch(t) { case LUA_TSTRING: printf("‘%s‘",lua_tostring(L,i)); break; case LUA_TBOOLEAN: printf(lua_toboolean(L,i) ? "true" : "false"); break; case LUA_TNUMBER: printf("%g",lua_tonumber(L,i)); break; default: printf("%s",lua_typename(L,t)); break; } printf(""); } printf("\n"); }
其它栈操作函数:
除了上面给出的数据交换函数之外,Lua的C API还提供了一组用于操作虚拟栈的普通函数,如:
int lua_gettop(lua_State* L); --返回栈中元素的个数。
void lua_settop(lua_State* L, int index); --将栈顶设置为指定的索引值。
void lua_pushvalue(lua_State* L, int index); --将指定索引的元素副本压入栈。
void lua_remove(lua_State* L, int index); --删除指定索引上的元素,其上面的元素自动下移。//删除指定索引元素
void lua_insert(lua_State* L, int index); --将栈顶元素插入到该索引值指向的位置。
void lua_replace(lua_State* L, int index); --弹出栈顶元素,并将该值设置到指定索引上。
lua_gettop函数返回栈中元素的个数,也可以说是栈顶元素的索引。lua_settop将栈顶设置为一个指定的位置,即修改栈中元素的数量。如果之前的栈顶比新设置的高,那么高出来的会被丢弃,反之,会压入nil来补足大小。有一个特例,调用lua_settop(L,0)能清空栈,也可以使用负数索引来使用lua_settop,另外,API根据这个函数还提供了一个宏,用于从栈中弹出n个元素:
#define lua_pop(L,n) lua_settop(L,-(n)-1)
以下的操作不会对栈有任何影响:
lua_settop(L,-1)//将栈顶元素设置它的当前值
lua_insert(L,-1) //将栈顶元素移动栈顶
下面的程序验证了上面 的函数:
int main(void) { lua_State* L = luaL_newstate(); lua_pushboolean(L,1); lua_pushnumber(L,10); lua_pushnil(L); lua_pushstring(L,"hello"); stackDump(L); //true 10 nil ‘hello‘ lua_pushvalue(L,-4); stackDump(L); //true 10 nil ‘hello‘ true lua_replace(L,3); stackDump(L); //true 10 true ‘hello‘ lua_settop(L,6); stackDump(L); //true 10 true ‘hello‘ nil nil lua_remove(L,-3); stackDump(L); //true 10 true nil nil lua_settop(L,-5); stackDump(L); //true lua_close(L); return 0; }
C API的错误处理
不象C++或者JAVA一样,C语言没有提供一种异常处理机制。为了改善这个难处,Lua利用C的setjmp技巧构造了一个类似异常处理的机制。(如果你用C++来编译Lua,那么修改代码以使用真正的异常并不困难。)
Lua中的所有结构都是动态的:它们按需增长,最终当可能时又会缩减。意味着内存分配失败的可能性在Lua中是普遍的。几乎任意操作都会面对这种意外。Lua的API中用异常发出这些错误而不是为每步操作产生错误码。这意味着所有的API函数可能抛出一个错误(也就是调用longjmp)来代替返回。
当我们写一个库代码时(也就是被Lua调用的C函数)长跳转(long jump)的用处几乎和一个真正的异常处理一样的方便,因为Lua抓取了任务偶然的错误。当我们写应用程序代码时(也就是调用Lua的C代码),无论如何,我们必须提供一种方法来抓取这些错误。
应用程序中的错误处理
典型的情况是应用的代码运行在非保护模式下。由于应用的代码不是被Lua调用的,Lua根据上下文情况来捕捉错误的发生(也就是说,Lua不能调用setjmp)。在这些情况下,当Lua遇到像 "not enough memory" 的错误,他不知道如何处理。他只能调用一个panic函数退出应用。(你可以使用lua_atpanic函数设置你自己的panic函数)
不是所有的API函数都会抛出异常,lua_open、lua_close、lua_pcall和lua_load都是安全的,另外,大多数其他函数只能在内存分配失败的情况下抛出异常:比如,luaL_loadfile如果没有足够内存来拷贝指定的文件将会失败。有些程序当碰到内存不足时,他们可能需要忽略异常不做任何处理。对这些程序而言,如果Lua导致内存不足,panic是没有问题的。
如果你不想你的应用退出,即使在内存分配失败的情况下,你必须在保护模式下运行你的代码。大部分或者所有你的Lua代码通过调用lua_pcall来运行,所以,它运行在保护模式下。即使在内存分配失败的情况下,lua_pcall也返回一个错误代码,使得lua解释器处于和谐的(consistent)状态。如果你也想保护所有你的与Lua交互的C代码,你可以使用lua_cpcall。(请看参考手册,有对这个函数更深的描述,在Lua的发布版的lua.c文件中有它应用的例子)
类库中的错误处理
Lua是安全的语言,也就是说,不管你些什么样的代码,也不管代码如何错误,你都可以根据Lua本身知道程序的行为。另外,错误也会根据Lua被发现和解释。你可以与C比较一下,C语言中很多错误的程序的行为只能依据硬件或者由程序计数器给出的错误出现的位置被解释。
不论什么时候你向Lua中添加一个新的C函数,你都可能打破原来的安全性。比如,一个类似poke的函数,在任意的内存地址存放任意的字节,可能使得内存瘫痪。你必须想法设法保证你的插件(add-ons)对于Lua来讲是安全的,并且提高比较好的错误处理。
正如我们前面所讨论的,每一个C程序都有他自己的错勿处理方式,当你打算为Lua写一个库函数的时候,这里有一些标准的处理错误的方法可以参考。不论什么时候,C函数发现错误只要简单的调用lua_error(或者luaL_error,后者更好,因为她调用了前者并格式化了错误信息)。Lua_error函数会清理所有在Lua中需要被清理的,然后和错误信息一起回到最初的执行lua_pcall的地方。
转自:
programming in lua