lua C api
PS:这里是默认我已经学完了lua脚本的基本知识(包括table,元表,函数,基本库, 文件io,库导入等等)
lua是c写的,无论是lua调用c还是c调用lua都非常容易,以下是基于文档做的一些学习笔记,在文档的索引可以快速找到api的名称方便参考,文档地址如下:
官网5.3文档:http://www.lua.org/manual/5.3/#index
中文文档:http://www.runoob.com/manual/lua53doc/contents.html#index
文档包括两部分,一部分是介绍lua的语法,以及一些注(keng)意(die)事项,比如其中说到空字符串和0都被认为是true,恩,要注意(MDZZ),感觉lua的强大之处在于他的table(手动咸鱼),下面开始修仙,下面通过问答的方式给自己学习lua做一些笔记,在此之前,先编译lua,我们到官网可以下载lua的源码
源码下载页面:http://www.lua.org/download.html
5.3源码下载:http://www.lua.org/ftp/lua-5.3.4.tar.gz
lua源码非常小,很容易编译,windows下,装个Mingw即可用make编译,以下是linux的
1.下载解压源码
wget http://www.lua.org/ftp/lua-5.3.4.tar.gz
tar -xzvf lua-5.3.4.tar.gz
~/local/lua/lua-5.3.4$ make
Please do 'make PLATFORM' where PLATFORM is one of these:
aix bsd c89 freebsd generic linux macosx mingw posix solaris
See doc/readme.html for complete instructions.
上面提示指定编译选项,如果是windows下用Mingw的,就make -j12 mingw
, -j12是多线程编译,分12个线程编译,现在是debian,可以选择posix,如果提示缺少一些库,使用apt-get 安装即可
make -j12 posix
编译成功后,在src文件生成一个lua(lua解释器),luac(lua脚本编译器),以及静态库liblua.a,c调用lua的时候,现在是用静态库,现在把以下文件复制到项目目录下
头文件:lua.h lualib.h lauxlib.h luaconf.h
静态库文件:liblua.a
我的项目目录为:~/project/lua
我把头文件放置在~/project/lua/include
静态库文件放在~/project/lua/lib
以下所有的操作都是在项目目录(用$PRO_HOME描述这个目录)
1.c是如何调用lua写的代码的?
1.首先了解,lua在c代码中是如何“工作”的,在c中要调用lua,首先我得创建一个lua状态机(lua_State)
文件$PRO_HOME/main.c
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
int main(void)
{
//创建一个lua状态机
lua_State *L = luaL_newstate();
//销毁这货
lua_close(L);
return 0;
}
然后尝试编译这个源文件
$gcc -I ../include -c main.c
$gcc -lm -o main main.o ../lib/liblua.a
编译成功说明配置没有问题
c调用lua代码,首先要学会怎么把lua代码“翻译”成c的代码,先不着急如何把lua代码加载到lua状态机运行,先了解下它是怎么运行的,根据文档参考,写以下一段代码
-- a是一个全局变量
a="测试"
print(a)
a是一个全局变量,在lua中,无非在维护一个堆栈stack和一个环境表_G(暂时理解)
那么用c怎么写呢?
1.创建一个状态机(lua_State),加载基本库(luaL_openlibs())
2.把字符串“测试”压入堆栈(lua_pushstring)
3.把这个字符串弹出,设置为global,名称为a(lua_setglobal())
4.从全局变量中,把print函数压栈(lua_getglobal())
5.把参数a从全局变量中压栈(lua_getglobal())
6.调用lua_call执行函数
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
int main(void)
{
lua_State *L = luaL_newstate();
//加载基本库(print在其中)
luaL_openlibs(L);
//这里相当于设置全局变量a="测试"
lua_pushstring(L,"测试");
lua_setglobal(L,"a");
//print函数压栈
lua_getglobal(L,"print");
//参数压栈yiban
lua_getglobal(L,"a");
//调用函数,1个参数,0个返回值
lua_call(L,1,0);
lua_close(L);
return 0;
}
小总结:
在lua 的 C api中,
数据类型有:
lua_Integer 整形
lua_Number 默认对应的是C double
lua_Unsigned 无符号整形
字符串、布尔型或者是lua的table之类的,都是不能直接“拿出来”的
如果要操作,设置某某某的变量,可以通过lua_pushxxxx来把对应的数据类型的变量的值压入堆栈,如果,要把变量设置为一个全局变量,就把它设置为global(lua_setglobal()),lua_setglobal会把栈顶的元素弹出,病设置到环境表,详情查看文档,
关于堆栈
lua的堆栈是lua自己实现的一个数据结构,并不是c语言执行时的那个堆栈,这个堆栈,由栈底往栈顶数,元素的序号是1 2 3 4 5...,这是它的绝对索引的方式,还有它支持负索引,也就是参考点是栈顶,而不是栈底,比如-1代表的是栈顶的元素,-2代表栈顶的下一个元素
对于堆栈的操作
有:
1.获取栈顶的索引值,lua_gettop(L)(这个值,可以当做是栈元素个数)
2.把数据压栈(lua_pushxxx,根据压入的数据类型不一样,有对应有一系列的操作函数,例如,lua_pushstring(L,"测试"),把字符串压入,详情看文档lua_pushxxxx),又或者是
3.把堆栈的元素“读取”出来,lua_toxxxx,就可以不弹出堆栈中的数据,把它转换成c的数据类型,例如:lua_tolstring,注意,这个返回值是const类型,也就是说你不能对他进行更改,如果要做其他用途,你需要
把他copy出来
4.更改元素在堆栈中的位置
5.lua有垃圾回收机制GC,一般不用担心内存释放的问题
2.如何在lua状态机中执行一个c写的函数?
刚刚已经学了,如何在c中,“执行”lua,这个print函数他已经实现了,但是我们自己怎么实现一个函数,然后在lua中调用呢?
这个函数可以是
1.lua写的函数
2.c写的可以让lua识别的函数
我们先来搞第二种,程序还是在原来的基础上进行扩充
我们把print换成我们自己实现的一个函数,这个函数在lua里叫print2,它比较简单,只能接受一个参数,不做任何检查,也不做任何的错误的处理
lua注册的函数的类型是:
static int fun(lua_State *L);
返回值是这个函数的返回值个数(因为lua支持多个返回值,执行完毕后,把返回值依次压栈,然后返回返回值个数就行了),调用结束后,堆栈中剩下的是fun的返回值(如果有返回值,并且按照顺序压栈)
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
static int print2(lua_State *L)
{
const char * s = luaL_optlstring (L,1,"啥都没",NULL); //获取函数调用时第1个参数为字符串,如果参数不存在或者是nil就返回“啥都没”,并且把长度放在第四个参数(这里设置为NULL,不获取长度),
printf("这是:%s\n",s);
return 0; //返回值个数为0
}
int main(void)
{
lua_State *L = luaL_newstate();
luaL_openlibs(L); //加载基本库(print在其中)
//这里相当于设置全局变量a=image.png"测试"
lua_pushstring(L,"测试");
lua_setglobal(L,"a");
//注册这个函数到全局表
lua_register(L,"print2",print2);
lua_getglobal(L,"print2"); //把这个函数从全局表中压栈
lua_getglobal(L,"a"); //参数压栈
lua_call(L,1,0); //调用函数,1个参数,0个返回值
lua_close(L); //关闭状态机
return 0;
}