Lua C API

C API

  • 云风Blog:Lua C API 的正确用法
  • C读取和调用Lua文件的库:lua.h, lauxlib.h, lualib.h
  • 包括:读写Lua全局变量的函数、调用Lua函数的函数、运行Lua代码片段的函数、注册C函数然后可以在Lua中被调用的函数
  • C和Lua之间的数据交换,通过对栈上的值进行操作。栈的使用解决:Lua会自动进行垃圾回收,而C要求显示的分配内存单元;Lua中的动态类型和C的静态类型。
  • 压入元素
void lua_pushnil(lua_State *L);                                        //插入空值
void lua_pushboolean(lua_State *L, int bool);                        //插入布尔值
void lua_pushnumber(lua_State *L, double n);                        //插入double
void lua_pushlstring(lua_State *L, const char* s, size_t length);    //插入任意字符串
void lua_pushstring(lua_State *L, const char* s);                    //插入带'\0'的字符串
  • 查询元素
    1表示第一个被压栈的元素;-1表示栈顶元素;当一个C函数返回后,Lua会清理他的栈,所以,有一个原则:永远不要将指向Lua字符串的指针保存到访问他们的外部函数中。
int lua_is*(lua_State *L, int index);        //检查一个元素是否指定类型(number,string,boolean,table)

int lua_toboolean(lua_State *L, int index);
double lua_tonumber(lua_State *L, int index);
const char* lua_tostring(lua_State *L, int index);
size_t lua_strlen(lua_State *L, int index);
  • 其他堆栈操作
int lua_gettop(lua_State *L);                    //返回堆栈中的元素个数
void lua_settop(lua_State *L, int index);        //设置栈顶为一个指定的值,多余值被抛弃,否则压入nil值
void lua_pop(lua_State *L, int n);                //lua_settop(L, -n - 1),从堆栈中弹出n个元素
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的C代码)的错误处理:当Lua遇到类似内存分配失败的情况,大部分会抛出异常,调用panic函数退出应用;其余情况会忽略异常,在保护模式下运行代码(Lua通过调用lua_pcall来运行)。
    类库(被Lua调用的C函数)中的错误处理:C函数发现错误只要调用luaL_error函数,清理所有在Lua中需要被清理的,然后和错误信息一起回到最初执行lua_pcall的地方。
  • 表操作
void lua_gettable(lua_State *L, int idx);                    //以栈顶元素为key值,获取指定索引的表的值到栈顶
void lua_getfield(lua_State *L, int idx, const char *k);    //获取指定索引的表对应key的值到栈顶
void lua_getglobal(lua_State *L, const char *name);            //等于lua_getfield(L, LUA_GLOBALSINDEX, (name))。获取全局表的变量到栈顶
void lua_settable(lua_State *L, int idx);                    //以栈顶元素为value,栈顶下一元素为key,设置指定索引的表的值
void lua_setfield(lua_State *L, int idx, const char *k);    //弹出栈顶元素,并设置为指定索引的表对应key的值
void lua_setglobal(lua_State *L, const char *name);            //等于lua_setfield(L, LUA_GLOBALSINDEX, (name))。设置全局变量的值
void lua_rawgeti(lua_State *L, int idx, int n);                //获得idx索引的表以n为key的值
void lua_rawget(lua_State *L, int idx);                        //获得idx索引的表以栈顶为key的值
void lua_rawseti(lua_State *L, int idx, int n);                //设置idx索引的表以n为key的值
void lua_rawset(lua_State *L, int idx);                        //设置idx索引的表以栈顶下一个元素为key的值
  • 调用函数
    使用API调用函数的方法是很简单的:首先,将被调用的函数入栈;第二,依次将所有参数入栈;第三,使用lua_pcall调用函数;最后,从栈中获取函数执行返回的结果。
    在将结果入栈之前,lua_pcall会将栈内的函数和参数移除。如果lua_pcall运行时出现错误,lua_pcall会返回一个非0的结果,并将错误信息入栈(依然会先移除栈内函数和参数)
    错误处理函数需要在被调用函数和参数之前入栈,参数errfunc为错误函数在栈中的索引。一般错误返回LUA_ERRRUN。内存分配错误返回LUA_ERRMEM;在错误处理函数中出错返回LUA_ERRERR,这两种情况并不会调用错误处理函数。
int  lua_pcall(lua_State *L, int nargs, int nresults, int errfunc);        //调用栈顶函数,指定参数格式nargs,返回结果个数,nresults,和错误函数
  • 调用C函数
    Lua调用C函数,使用栈进行交互,用来交互的栈不是全局变量,每一个函数都有他自己的私有栈,第一个参数总是在这个私有栈的index = 1的位置。
    一个Lua库实际上是一个定义了一系列Lua函数的chunk,并将这些函数保存在适当的地方,通常作为table 的域来保存。LuaC库就是这样实现的。
    在宿主程序中调用C函数
//注册被Lua调用的C函数的格式,以a+b为例
static int func(lua_State *L)
{
    //从栈中获取函数参数并检查合法性,1表示Lua调用时的第一个参数,以此类推
    double arg1 = luaL_checknumber(L, 1);
    double arg2 = luaL_checknumber(L, 2);
    
    //将函数结果入栈,如果有多个返回值,可以多次入栈
    lua_pushnumber(L, arg1 + arg2);
    
    //返回值表示该函数的返回值的数量
    return 1;
}

int main()
{
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);
    
    //注册被Lua调用的C函数,参数"add"表示Lua调用时使用的全局函数名,func为被调用的C函数
    lua_register(L, "add", func);
    luaL_dostring("print(add(1, 2))");
    
    lua_close(L);
    return 0;
}

C函数库

//注册被Lua调用的C函数的格式,以a+b为例,函数必须以C的形式被导出
extern "C" int func(lua_State *L)
{
    //从栈中获取函数参数并检查合法性,1表示Lua调用时的第一个参数,以此类推
    double arg1 = luaL_checknumber(L, 1);
    double arg2 = luaL_checknumber(L, 2);
    
    //将函数结果入栈,如果有多个返回值,可以多次入栈
    lua_pushnumber(L, arg1 + arg2);
    
    //返回值表示该函数的返回值的数量
    return 1;
}

static luaL_Reg mylibs[] = 
{
    {"add", func},
    {NULLL, NULL},
};

//注册mylibs库,Lua中使用该库的用法:testlibs.add(1, 2)
extern "C" __declspec(dllexport)
int luaopen_testlibs(lua_State* L) 
{
    luaL_newlib(L, mylibs);
    return 1;
}
  • 字符串处理
    C函数接受一个来自lua的字符串作为参数时,有两个规则必须遵守:当字符串正在被访问的时候不要将其出栈;永远不要修改字符串。
    C函数需要创建一个字符串返回给lua的时候,C代码负责缓冲区的分配和释放,负责处理缓冲溢出等情况。
void lua_concat(lua_State *L, int n);        //连接栈顶开始的n个字符串,弹出这n个字符串并压栈结果
const char *lua_pushfstring(lua_State *L, const char *fmt, ...);    //根据格式串fmt的要求创建一个新的字符串。
  • 注册表
    使用LUA_REGISTRYINDEX索引来保存注册表中的Lua值。任何C库都可以在这张表里保存数据, 为了防止冲突,可以使用保护库名前缀的名字作为key值。注册表中的整数key用于应用机制luaL_ref
  • 闭包
    一个C函数和它的upvalues的组合称为闭包。upvalues为函数能访问的外部局部变量。
static int counter(lua_State *L)
{
    double val = lua_tonumber(L, lua_upvalveindex(1));
    lua_pushnumber(L, ++val);
    lua_pushvalue(L, -1);
    lua_replace(L, lua_upvalueindex(1));
    return 1;
}

int newCounter(lua_State *L)
{
    lua_pushnumber(L, 0);
    //第二个参数为基本函数,第三个参数是upvalues的个数
    lua_pushcclosure(L, &counter, 1);
    return 1;
}
  • Userdata
void *lua_newuserdata(lua_State *L, size_t size);        //按照指定size的大小分配一段内存放入栈内,并返回这个地址
  • Metatable
int luaL_newmetatable(lua_State *L, const char* tname);             //栈顶创建metatable,建立表和registry中类型名的双向关系(分别以tname为表的key和以表为tname的key)
void luaL_getmetatable(lua_State *L, const char* tname);             //获取registry中tname对应的表
void* luaL_checkudata(lua_State *L, int index, const char* tname);    //检查指定索引的元素是否为带有给定tname的metatable的useratum,如果是,返回useratum的地址,否则返回NULL
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容

  • 第一篇 语言 第0章 序言 Lua仅让你用少量的代码解决关键问题。 Lua所提供的机制是C不擅长的:高级语言,动态...
    testfor阅读 2,638评论 1 7
  • Nginx API for Lua Introduction ngx.arg ngx.var.VARIABLE C...
    吃瓜的东阅读 5,680评论 0 5
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,204评论 0 4
  • 原文地址:C语言函数调用栈(一)C语言函数调用栈(二) 0 引言 程序的执行过程可看作连续的函数调用。当一个函数执...
    小猪啊呜阅读 4,581评论 1 19
  • 鐵皮火車停在一個小站 有一陣一動不動。 門怦然關上,鋪路石踩在脚下, 有人道着永别。 一隻手套墜下,日影轉暗。 門...
    东丰林波阅读 239评论 0 0