Lua_C_API

Posted 程序员不是码农

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lua_C_API相关的知识,希望对你有一定的参考价值。

《Programming in Lua的第4部分,也是最后一部分,主要介绍C语言中如何调用Lua函数,以及Lua中如何调用C库中函数。


1
An Overview of the C API(C API概述)


Iterators and Closures(迭代器和闭包)

Lua是一门嵌入式的和可扩展的语言,C和Lua之间有两种交互方式,第一种在C中调用Lua的库函数,第二种Lua中调用C的库函数。应用代码(Application Code)和库代码(Lib Code)同Lua交流使用相同的API,就是所谓的C_API,C_API是一组能和Lua进行交互的函数、常量和类型。Lua独立的解析器(lua.c)为应用代码的实现,标准库(lmathlih.c等)提供了库代码的实现,可以学习借鉴。



A First Example(第一个实例)

实现一个基本的独立的Lua解释器,如下:

lua.h:申明Lua提供的基本函数,比如创建一个新的Lua环境,调用Lua函数,读写环境中的全局变量,注册被Lua调用的新函数等等。所有申明在lua.h中的函数,都有一个前缀lua_,比如lua_pcall


laxulib.h:申明了由auxiliary library提供的函数,其中所有函数都有前缀luaL_,比如luaL_loadstring。其不能访问Lua内部的代码,而是基于lua.h库封装出来的更高级的库。


Lua库没有定义c全局变量,其所有的状态都保存在动态结构lua_State中,通过luaL_newstate可以创建一个新的Lua状态。lualib.h中定义了打开标准库的函数luaL_openlibs



The Stack(虚拟栈)

Lua和C之间通过虚拟栈进行沟通,几乎所有的API调用都会操作栈上的值,Lua和C所有的数据交换都在虚拟栈上进行,也可使用栈来保存一些中间结果。虚拟栈遵循后入先出形式。

对于元素入栈,每一种Lua类型都有一个入栈API,如下:

Lua_C_API


虚拟栈默认有20个位置(lua.h中LUA_MINSTACK定义),对于大多数情况是够用的,对于一些特殊情况,需要使用lua_checkstack来检查是否有足够的空间,如果空间不够,会增加栈的空间,如下

Lua_C_API


第一个入栈元素索引值为1,第二个为2... 也可以用-1来索引栈顶的元素,-2来索引倒数第二个元素。比如使用lua_tostring(L, -1)以字符串形式返回栈顶元素。API提供了一组函数还检查栈中的元素类型lua_is*其中*代表lua中的类型,比如lua_isnil, lua_isnumber等。所有函数原型为:

Lua_C_API

实际上,lua_is*不仅仅检查栈中元素是否为该类型,可以转换为对应类型的也可。


从栈中获取元素,可以使用lua_to*这组函数,如下:

Lua_C_API


通过实现一个将栈上所有的内容dump打印出来的例子来说明这些接口的使用:

Lua_C_API


其它一些栈的操作如下:

Lua_C_API


还是用一个例子来说明栈操作相关接口的使用,脑海中运行一遍加深理解:

Lua_C_API



Error handling(异常处理)

一些操作可以抛出错误:访问全局变量触发__index元方法抛出错误,分配内存操作最终触发垃圾回收,并通过finalizers抛出错误,总而言之,大量的Lua API都能抛出错误。因为C语言没有提供异常处理,面对这种困难,Lua是使用C中的setjmp来实现异常处理机制的。

在应用代码(c代码)中通过lua_pcall调用C函数的方式来实现安全调用和进行异常处理,如下:

Lua_C_API

为Lua写的库函数通过pcall来处理异常。



Memory Allocation(内存分配器)

Lua提供了默认的内存分配机制malloc-realloc-free,基于C标准库,如下:

Lua_C_API


我们也可以自己定制内存分配机制,lua_newstate提供了相应的参数

Lua_C_API


也可通过lua_getallocflua_setallocf来获取和设置内存分配器

Lua_C_API


Exercises

Lua_C_API



2
Extending Your Application(扩展应用程序)



The Basics(基础)

使用Lua配置文件来定义窗口大小,并解析获得相关参数,如下:

Lua_C_API

对于table的操作可以使用lua_settablelua_gettable。还有对应的更便捷的接口lua_setfieldlua_getfield


Calling Lua Functions(C中调用Lua函数)

Lua的一个优点是其配置文件可以定义函数并被应用程序调用。如下在C中调用Lua函数

Lua_C_API


lua_pcall

原型:int lua_pcall(lua_State *L, int nargs, int nresults, int msgh)

功能:在保护模式下调用一个函数

如果没有抛错的话,lua_pcall同lua_call,如果有错误发生,lua_pcall会捕获错误,并将错误信息压入栈,并返回一个错误代码。lua_pcall会将函数和参数从栈上pop掉。


A Generic Call Function(通用函数调用)

可使用stdarg标准库来封装对lua函数的调用,如下:

call_va(L, "f", "dd>d", x, y, &z)


其中dd>d表示2个double类型参数,返回1个double类型的结果。d代表double,i代表integer,s调用string。>用来分隔参数和结果,如果不需要返回结果则这个>可有可无。



一个通用的函数调用如下:

Lua_C_API




Exercise


Lua_C_API


3
Calling C from Lua(Lua中调用C函数)




C Functions(C函数)

任何注册给Lua的函数都有相同的原型

Lua_C_API

以下为sin函数实现:

Lua_C_API

在Lua脚本中调用这个函数前,需要进行注册:

Lua_C_API


以下为一个更为复杂例子,编写一个函数返回给定目录下所有的内容

Lua_C_API


C Modules(C模块)

通常将所有的C函数放在一个数组中

Lua_C_API

接着编译上述库文件,生产对应的so文件,然后将其放入对应的C路径下,就可以在lua脚本中使用了

Lua_C_API


Exercise


Lua_C_API


4
Techniques for Writing C Functions(写C函数的技巧)



Array Manipulation

我们可以使用lua_settablelua_gettable来操作table,此外API还提供了lua_getilua_seti来访问和更新table,

Lua_C_API

以下为一个具体的例子,用C实现一个Map函数,将数组中每一个元素作为参数传入函数f,并将执行结果替换为数组中原来的元素。

Lua_C_API



String Manipulation

以下为分割字符串例子


以上主要介绍了常用C API,虚拟栈的使用,异常处理方式,如何定制自己的内存分配策略,如何扩展C程序,如何在C中调用Lua,以及如何编写给Lua调用的C模块等。整本书的四个部分分享到此,选择性的跳过了一些部分。


参考:

http://www.lua.org/pil/

http://www.lua.org/manual/5.3/manual.html#lua_call

END



以上是关于Lua_C_API的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段6——CSS选择器

VSCode自定义代码片段——声明函数