c++ string remove_Lua和C的互交互

6be93099627f1c190fb20f5209e05ab4.png

Lua在游戏中是非常常见的嵌入式语言,常用来做ui界面,ai逻辑脚本,手机热更等.最近一个项目中用到了lua,需要自己写一些lua的库.这块其实并不是很难,这里简单总结下.

建议对照lua的文档阅读 https://www.runoob.com/manual/lua53doc/contents.html, 里面有每个函数具体功能的解释.

以及lua的源码,lua.c就是C中调用lua的一个解释器实现,lmathlib.clstrlib.c就是lua的模块实现.

最后再推荐一本书,讲的非常详细:Programming in Lua, Fourth Edition.

从C中启动Lua

启动一个Lua State并处理输入:

#include 

lua.h是Lua的主要头文件,包含了所有Lua提供的基础函数,里面的函数都是lua_开头.

luaxlib.h是Lua的辅助库头文件,里面的函数都是通过lua.h中提供的函数来实现的,是为了方便编程实现的一个辅助库,里面的函数都以luaL_开头.

luaL_newstate新建一个Lua State并返回一个指针,几乎所有的Lua交互函数都需要传入一个lua_State的指针作为第一个参数.

新建出来的Lua State是没有Lua的math,io等这些库甚至print函数的,luaL_openLibs是加载Lua自带的一些库.


栈的操作

Lua和C交互的最大特色就是使用一个虚拟的栈来实现,虚拟的栈上可以是任意类型的值,包括nil,number,string,userdata等.

Lua内部所有的值类型都是用TValue这种结构来实现的,TValue包括Value_tt,_tt标识Value的类型,Value是一个Union,可以表示不同类型的值,userdatestring*gc表示,lightuserdata*p表示.

/*

默认情况下栈满足LIFO.大部分函数需要通过索引index指定一个栈上元素的位置作为参数,索引的位置可以是正数或者负数,正数表示从栈底计数,负数表示从栈顶计数.比如一个有4个元素的栈的索引:

8df023ae2d95c1cce5c0ee38baf71246.png

Push 元素:

void 

检查栈上空余格子数:

int  

检查栈中元素类型:

int 

获取栈上的元素:

lua_Number 

Dump栈:

static 

操作栈元素:

int  

一个示例:

int 

全局变量:

int 

表:

int 

从C中调用Lua函数:

假设我们有一个全局的Lua函数:

function 

我们在C中加载了这个Lua函数,现在来调用它:

double 

int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);

调用lua_pcall前,需要将要调用的函数,参数依次入栈. nargs表示参数的个数, nresults表示期望返回值的个数, Lua会自动裁剪或者用nil补齐返回值.lua_pcall将函数和所有参数出栈,再将所有返回值依次入栈.

如果调用过程中出现错误,lua_pcall会返回一个错误码, 并尝试调用一个错误处理函数, msgh可以指定一个错误处理函数的位置,为0时表示使用默认的错误处理函数.错误处理函数会将一个错误消息入栈.

不管是调用函数还是进行其他的操作,都有义务去保持栈和操作前一样.


从Lua中调用C函数

从Lua中调用C函数时,C函数必须满足下列的格式:

typedef 

值得注意的是,调用函数时的栈是一个私有栈,栈上的参数总是从索引1开始的.执行完函数后,需要将所有参数出栈,再将所有返回值入栈,并返回返回值的个数.

一个简单的例子:

static 

现在把这个函数注册成为全局函数:

lua_puscfunction

这样我们就可以直接在Lua代码中使用这个函数了.

当然大部分情况下,我们还是用模块的方式来提供一组函数,我们在Lua中可以加载这个模块.

要加载模块,就要先在C中写好注册模块的代码:

static 

把模块编译成一个dll或者so文件,文件的名字应该和模块名字一致. 比如在windows来用mingw来编译一个dll,假设我们的文件名字叫mylib.c.

gcc -shared -I $LUAPATH/src -o mylib.dll mylib.c $LUAPATH/src/lua53.dll

在lua中就可以直接用require这个库(函数名称必须是 luaopen_mylib):

local 

如果你不想在Lua中require这个模块,可以把luaopen_mylib放到源码linit.c下的luaL_openlibs的标准库列表里面.

LuaL_Buffer:

LuaL_Buffer是一个用来辅助操作字符串的字符串缓存,有两种使用方式.

一种使用方式是预分配大小:

const 

来看先Lua在lstrlib.c中实现的string.upper:

static 

另外一种方式是变长使用luaL_Buffer:

void 

来看一个简单版本的table.concat实现:

static 

注册表:

注册表是一个只能在C代码中访问的一个没有元表的表,用于在模块间共享数据.

注册表通过伪索引 LUA_REGISTRYINDEX来访问,大部分的Lua API的索引值都支持用伪索引来调用,除了像lua_insert,lua_remove这种直接操作栈的API.

获取注册表上的一个值:

lua_getfield

因为注册表是所有模块共用的,选用string作为key值时,要注意防止冲突.

无论在何种情况下,都不可以直接用一个自定义的数值作为注册表的key值,Lua提供了引用系统来提供保证唯一的key:

int 

上面的函数会将栈顶元素出栈,并得到一个不冲突的整型key值,存储到注册表中.得到的整型key值叫做引用.

lua_rawgeti

引用系统会对nil值特殊处理,如果我们尝试对nil值获取引用,luaL_ref不会创建新的引用,而是返回固定的引用LUA_REFNIL.同样,用LUA_REFNIL作key值取表中的元素也会返回nil.

上值:

注册表提供了全局变量的访问,上值实现了相当于C中的static变量.当我们在Lua中创建一个新的C函数,可以给它关联任意数量的上值.当我们调用函数时,可以使用伪索引来访问这些上值.

这些带有上值的C函数叫做闭包,C闭包和Lua闭包很相似.通常,我们可以使用相同的函数代码,用不同的上值来生成不同的闭包.

来看一个计数器的示例:

c1 

C代码:

static 

lua_pushcclosure创建一个新的闭包,第二个参数是它的基础函数,第三个参数是上值的个数.

lua_upvalueindex是一个宏,返回一个上值的伪索引.

闭包最多可以拥有255个上值.如果尝试取一个不存在的上值,会返回LUA_TNONE.

有的时候,我们需要再一个模块之间的所有函数中共享变量.除了使用注册表,还可以使用上值.

不像Lua中的闭包,C闭包无法共享上值.但是我们可以把上值设置为一个公共的table,用来共享数据.

luaL_newlib是一个这样的宏定义:

#define luaL_newlib(L, lib) (LuaL_newlibtable(L, lib), LuaL_setfuncs(l, lib, 0))

luaL_setfunc的最后一个参数可以指定栈上的上值个数,为所有函数添加上值.

创建一个共享table的模块:

luaL_newlibtable

自定义类型

再C中自定义类型,通过userdata来实现,userdata分为full userdata和light userdata两种.

Userdata:

void

lua_newuserdata初始化分配的内存会在对象GC时自动释放掉.

每个userdata都可以像table那样设置元表,所以在C中写自定义类型和在Lua中没有太大区别.

一个实现布尔数组的示例:

#include

在Lua中试验一下:

local 

Light Userdata:

上面普通的userdata叫full userdata,另外一种userdata就是light userdata.

一个light userdata就是一个C指针 void*值.light userdata就是一个值,而不是一个对象,lightuserda不会有GC,表现行为和number类型是类似的.

Light userdata不能给每个值指定一个元表.但是和Lua中的值类型一样,可以共享一个全局的元表,默认情况下light userdata没有元表.

void 

Light userdata的两个值相等,当且仅当它们指向的地址相同.因此light userdata常用来做表的key值,比如注册表的key.

Light userdata另外的用处就是用来映射到一个C对象,实现代理的功能.


--END--

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值