lua C API
Posted 程序小栈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lua C API相关的知识,希望对你有一定的参考价值。
一、c++调用lua
lua和c/c++的交互主要通过lua虚拟栈进行的,堆栈的特点是LIFO (后进先出)
堆栈的索引可以是正数也可以是负数 -1表示栈顶,1表示栈底
Lua使用虚拟堆栈机制和C程序相互传值,所有的堆栈中的元素都可以看做一个lua值(如nil,number,string等)
当lua调用c函数时,被调用的C函数将得到一个新的堆栈。这个堆栈和之前调用此函数的堆栈无关,也与其它C函数的堆栈无关。
这一新的堆栈用调用C函数要用到的参数初始化,同时,这一堆栈也被用以返回函数调用结果。
为了便于操作,在API的中大量操作都并不依从堆栈只能操作栈顶元素的严格规则。而通过索引引用堆栈的任一元素。一个正整数索引可以看作某一元素在堆栈中的绝对位置(从1开始计数),一个负整数索引可以看作某一元素相对于栈顶的偏移量。
特别地,如果堆栈中有n个元素,那么索引1指向第一个元素(即第一个压入栈的元素)索引n指向最后一个元素;反过来,索引-1指向最后一个元素(即栈顶元素)索引-n指向第一个元素。当一个索引大于1并小于n时我们称其为一个有效索引(即1 <= abs(index) <= top)。
API接口解析
lua_newstate //创建一个新的state
ua_open/lua_close
lua_open 被 lua_newstate 替换,可以使用luaL_newstate从标准库中创建一个标准配置的 state,如:
lua_State *L = luaL_newstate(); 。
void lua_close (lua_State *L);销毁指定的 state 中所有的对象,并释放指定的 state 中使用的所有动态内存。
lua_load/lua_call/lua_pcall/lua_cpcall
这些函数的目的就是让我们能够执行压入栈中的函数,该函数可能是lua中定义的函数,可能是C++重定义的函数,当然我们一般是用来执行lua中执行的函数,C++中定义的基本上可以直接调用的。
int lua_load (lua_State *L,
lua_Reader reader,
void *data,
const char *chunkname);
void lua_call(lua_State *L, int nargs, int nresults);
void lua_pcall(lua_State *L, int nargs, int nresults, int errfunc);
void lua_cpcall(lua_State *L, int nargs, int nresults, int errfunc, void *ud);
L是执行环境,可以理解为当前栈,nargs参数个数,nresults返回值个数。lua_pcall和该函数区别是多一个参数,用于发生错误处理时的代码返回。lua_cpcall则又多一个用于传递用户自定义的数据结构的指针。
lua_call的运行是无保护的,他与lua_pcall相似,但是在错误发生的时候她抛出错误而不是返回错误代码。当你在应用程序中写主流程的代码时,不应该使用 lua_call,因为你应该捕捉任何可能发生的错误。当你写一个函数的代码时,使用lua_call是比较好的想法,如果有错误发生,把错误留给关心她的人去处理。所以,写应用程序主流程代码用lua_pcall,写C Native Function代码时用lua_call。
=========================一条分隔线================================
示例1:
lua代码:
a = f("how", t.x, 14)
C++代码
lua_getglobal(L, "f");//call function
lua_pushstring(L, "how");//第一参数进栈
lua_getglobal(L, "t");//table入栈
lua_getfield(L,-1,"x");//在栈顶获取x的值 并push t.x入栈 第二个参数入栈
lua_remove(L, -2);//把t从栈中删除
lua_pushinteger(L,14);//第三个参数入栈
lua_pcall(L,3,1,0);//调用函数f 传递3个参数 1个返回值
lua_setfield(L,-1,"a");//弹出栈顶元素并返回给全局变量a
lua_getfield 取出制定元素的值并压栈
lua_pcall:执行栈上的函数调用
lua_getglobal/lua_setglobal
lua_getglobal:把全局 name 的值压入栈顶,它被定义为宏(macro)
#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, s)
lua_setglobal:从栈中弹出一个值并赋值给全局 name,它被定义成宏(macro):
#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, s)
lua_gettop/lua_settop/lua_pop
因为索引从1开始计数,lua_gettop的返回值等于这个堆栈的元素个数(当堆栈为空时返回值为0)
lua_settop用于把堆栈的栈顶索引设置为指定的数值,它可以接受所有可接受索引。如果新的栈顶索引比原来的大,则新的位置用nil填充。如果index为0,则将删除堆栈中的所有元素。在lua.h中定义了如下一个宏:
#define lua_pop(L,n) lua_settop(L,-(n)-1)
lua_pop删除堆栈上的n个元素
lua_pushvalue/lua_insert/lua_remove/lua_replace
lua_pushvalue压入一个元素的值拷贝到指定的索引处,相反地,lua_remove删除给定索引的元素,并将之一索引之上的元素来填补空缺。同样地,lua_insert把栈顶的元素移动到制定位置,制定位置之后的元素往后移动。Lua_replace将栈顶元素压入指定位置而不移动任何元素(因此指定位置的元素的值被替换)。这些函数都仅接受有效索引(你不应当使用假索引调用lua_remove或lua_insert,因为它不能解析为一个堆栈位置)。下面是一个例子,栈的初始状态为10 20 30 40 50 (从栈底到栈顶,“”标识为栈顶,有:
lua_pushvalue(L, 3) --> 10 20 30 40 50 30*
lua_pushvalue(L, -1) --> 10 20 30 40 50 30 30*
lua_remove(L, -3) --> 10 20 30 40 30 30*
lua_remove(L, 6) --> 10 20 30 40 30*
lua_insert(L, 1) --> 30 10 20 30 40*
lua_insert(L, -1) --> 30 10 20 30 40* (没影响)
lua_replace(L, 2) --> 30 40 20 30*
lua_settop(L, -3) --> 30 40*
lua_settop(L, 6) --> 30 40 nil nil nil nil*
lua_gettable/lua_settable
void lua_gettable (lua_State *L, int index);
把 t[k] 压入堆栈,t 是给出的有效的索引 index 的值,k 是栈顶的值,这个函数会从堆栈中弹出 key,并将结果值放到它的位置,和在 Lua 一样,函数可能会触发一个元方法 index 事件。
void lua_settable (lua_State *L, int index);
相当于 t[k]=v,t 是给出的有效的索引 index 的值,v 是堆栈顶部的值,k 是堆栈顶部下面的值。这个函数会从堆栈中弹出 key 和 value 的值,和在 Lua 中一样,函数可能会触发元方法 newindex 事件。
lua_concat
void lua_concat (lua_State *L, int n);
用来连接字符串,等价于Lua中的..操作符:自动将数字转换成字符串,如果有必要的时候还会自动调用metamethods。另外,她可以同时连接多个字符串。调用lua_concat(L,n)将连接(同时会出栈)栈顶的n个值,并将最终结果放到栈顶。
lua_type返回堆栈元素的值类型,当使用无效索引时返回LUA_TNONE(如当堆栈为空的时候),lua_type返回的类型代码为如下在lua.h中定义的常量:LUA_TNIL,LUA_TNUMBER,LUA_TBOOLEAN,LUA_TSTRING,LUA_TTABLE,LUA_TFUNCTION,LUA_USERDATA,LUA_TTHEARD,LUA_TLIGHTUSERDATA。下面的函数可以将这些常量转换为字符串:
const char* lua_typename (lua_State* L, int type);
lua_checkstack
当你使用Lua API的时候,你有责任控制堆栈溢出。函数
int lua_checkstack (lua_State *L, ine extra);
将把堆栈的尺寸扩大到可以容纳top+extra个元素;当不能扩大堆栈尺寸到这一尺寸时返回假。这一函数从不减小堆栈的尺寸;当前堆栈的尺寸大于新的尺寸时,它将保留原来的尺寸,并不变化。
lua_is***
int lua_isnumber(lua_State *L, int index);
int lua_isboolean(lua_State *L, int index);
int lua_isfunction(lua_State *L, int index);
int lua_istable(lua_State *L, int index);
int lua_isstring(lua_State *L, int index);
int lua_isnil(lua_State *L, int index);
int lua_iscfunction(lua_State *L, int index);
带lua_is*前辍的函数在当堆栈元素对象与给定的类型兼容时返回1,否则返回0。Lua_isboolean是个例外,它仅在元素类型为布尔型时成功(否则没有意思,因为任何值都可看作布尔型)。当使用无效索引时,它们总返回0。Lua_isnumber接受数字或者全部为数字的字符串;lua_isstring打接受字符串和数值,lua_isfunction接受lua函数和C函数;lua_isuserdata也可接受完全和轻量级两种userdata。如果想区分C函数和lua函数,可以使用lua_iscfunction函数;同样地,想区分完全和轻量级userdata可以使用lua_islightuserdata;区分数字和数字组成的字符串可以使用lua_type。
API函数中还有比较堆栈中的两个值 的大小的函数:
int lua_equal(lua_State *L, int index1, int index2);
int lua_rawequal(lua_State *L, int index1, int index2);
int lua_lessthan(lua_State *L, int index1, int index2);
lua_equal和lua_lessthan与相对应的lua操作符等价。lua_rawequal直接判断两个值的原始值,而非通过调用元方法来比较。以上的函数当索引无效时返回0
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);
lua_Number lua_tonumber(lua_State *L, int index);
void *lua_topointer(lua_State *L, int index);
lua_State *lua_tothread(lua_State *L, int index);
const char *lua_tostring(lua_State *L, int index);
这些函数可通过任意可接受索引调用,如果用无效索引为参数,则和给定值并不匹配类型一样。lua_toboolean转换指定索引lua值为C“布尔型”值(0或1)。当lua值仅为false或nil时返回0(如果你仅想接受一个真正的布尔值,可以先使用lua_isboolean去测试这个值的类型。
lua_tonumber转换指定索引的值为数字(lua_Number默认为double)。这一lua值必须数字或可转换为数字的字符串(参考2.2.1),否则lua_tonumber返回0。
lua_tostring将指定索引的值转换为字符串(const char*)。lua值必须为字符串或数字,否则返回NULL。当值为数字,lua_tostring将会把堆栈的原值转换为字符串(当lua_tostring应用到键值上时会使lua_next出现难以找出原因的错误)。lua_tostring返回一个完全对齐的字符串指针,这一字符串总是’/0’结尾(和C一样),但可能含有其它的0。如果你不知道一个字符串有多少个0,你可以使用lua_strlen取得真实长度。因为lua有垃圾收集机制,因此不保证返回的字符串指针在对应的值从堆栈中删除后仍然有效。如果你以后还要用到当前函数返回的字符串,你应当备份它或者将它放到registry中(参考3.18)。
lua_tofunction将堆栈中的值转换为C函数指针,这个值必须为C函数指针,否则返回NULL。数据类型lua_CFunction将在3.16节讲述。
lua_tothread转换堆栈中的值为lua线程(以lua_State*为表现形式),此值必须是一个线程,否则返回NULL。
lua_topointer转换堆栈中的值为通用C指针(void*)。这个值必须为userdata、表、线程或函数,否则返回NULL。lua保证同一类型的不同对象返回不同指针。没有直接方法将指针转换为原值,这一函数通常用以获取调试信息。
lua_push***
void lua_pushboolean(lua_State *L, int b);
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
void lua_pushcfunction(lua_State *L, lua_CFunction f);
const char *lua_pushfstring (lua_State *L, const char *fmt, ...);
void lua_pushinteger (lua_State *L, lua_Integer n);
void lua_pushliteral
void lua_pushlstring(lua_State *L, const char *s, size_t len);
void lua_pushnil(lua_State *L);
void lua_pushnumber(lua_State *L, lua_Number n);
void lua_pushstring(lua_State *L, const char *s);
const char *lua_pushvfstring (lua_State *L,
const char *fmt,
va_list argp);
这些函数接受一个C值,并将其转换为对应的lua值,然后将其压入堆栈。lua_pushlstring和lua_pushstring对给定的字符串生成一个可以互转的拷贝,这是个例外。lua_pushstring能压C字符串(即以0结尾并且内部没有0),否则建议使用更通用的lua_pushlstring,它能指定长度。
你同样可以压入“格式化”字符串:
const char *lua_pushfstring (lua_State *L, const char *fmt, ...);
const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp);
这两个函数向堆栈压入格式化字符串并返回指向字符串的指针。它们跟sprintf和vsprintf很象但有如下的重要不同:
你不用申请内存去保存格式化结果,这结果是一个lua字符串并且lua自己会小心管理内存(并通过垃圾收集机制释放)。
使用转义字符受限。它们没有标志量、宽度和精确度。转义字符能够是’%%’(插入一个”%”)、’%s’(插入一个以0结尾的字符串)、’%f’(插入一个lua_Number)、’%d’(插入一个int)和’%c’(插入一个用int表示的字符)。
lua_register
void lua_register (lua_State *L, const char *name, lua_CFunction f);
==========================承认吧,我就是一条分割线===================
完整示例:
void load(lua_State *L, const char *fname, int *w, int *h) {
if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0 ,0)) {
printf("Error Msg is %s.\n", lua_tostring(L, -1));
return;
}
lua_getglobal(L, "width"); // #define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
lua_getglobal(L, "height");
if (!lua_isnumber(L, -2)) {
printf("'width' should be a number\n");
return;
}
if (!lua_isnumber(L, -1)) {
printf("'height' should be a number\n", );
return;
}
*w = lua_tointeger(L, -2);
*h = lua_tointeger(L, -1);
}
int main() {
lua_State *L = luaL_newstate();
int w, h;
load(L, "D:/test.lua", &w, &h);
printf("width = %d, height = %d\n", w, h);
lua_close(L);
return 0;
}
============再看,底裤都没有了,一条分割线而已啦!===================
下一节用代码示例实现lua和c++的相互调用 let's go!
以上是关于lua C API的主要内容,如果未能解决你的问题,请参考以下文章