设为首页 加入收藏

TOP

C语言开发lua模块入门 --- 虚拟栈和基本代码结构
2017-08-09 10:22:40 】 浏览:14
Tags:语言 开发 lua 模块 入门 --- 虚拟 基本 代码 结构

lua是一门小巧高效的脚本语言,核心代码不到500kb,由于要保持小巧,所以lua的核心库功能不可能很复杂,只实现一些基本功能,甚至没有操作目录的API,但由于lua良好的设计,并且是用标准C开发,所以很容易用C语言来扩展lua的功能。

用C编写lua模块需要遵循一定的规则,由于C和lua中的数据结构是完全不一样的,并且lua函数可以返回多个结果,所以要有一种在lua脚本和C函数之间交换数据的机制,这个机制就是虚拟栈,这个栈可以储存任何类型的数据,供lua调用的C函数从这个栈中获取调用参数,然后将结果再压入栈中返回到lua,这个虚拟栈用索引来操作,栈底(第一个入栈)的元素索引为1, 第二个入栈的索引为2,依次类推...,这个虚拟栈也支持逆向索引,即栈顶(最后一个入栈)的元素索引为-1,倒数第二个入栈的为-2,依次类推,这两种索引方式依据实际情况都很常用。

lua的C API提供了很多的从栈中读取数据以及向栈中添加数据的基本方法,下面列出一些常用方法:

 

void lua_pushboolean (lua_State *L, int b); //向栈中压入一个bool值
void lua_pushinteger (lua_State *L, lua_Integer n); //压入一个整数
void lua_pushnil (lua_State *L);  //压入一个nil
void lua_pushnumber (lua_State *L, lua_Number n); //压入一个双精度数
void lua_pushstring (lua_State *L, const char *s);  //压入一个字符串

int lua_toboolean (lua_State *L, int index);  //从栈中读取一个bool值,index为读取的数据在栈中的索引
lua_Integer lua_tointeger (lua_State *L, int index);  //读取一个整数
lua_Number lua_tonumber (lua_State *L, int index);  //读取一个浮点数
const char *lua_tostring (lua_State *L, int index); //读取一个字符串

以上方法第一个参数都是 lua_State *L, 我们可以认为该参数代表了虚拟栈, int index是需要读取的元素在栈中的索引。

 

 

由于为lua编写的C函数从虚拟栈中读取参数,然后再把返回结果添加到栈中,所以为lua编写的C函数都只需要一个参数,就是这个虚拟栈 lua_State *L, 下面是C函数的函数原型:

 

int funcname(lua_State *L);

 

int类型的返回值表示该函数返回值的个数,下面是一个求两个数和的C函数示例:

 

int sum(lua_State *L)
{
	int a = lua_tointeger(L, 1); //第一个加数,函数的第一个参数总是索引1
	int b = lua_tointeger(L, 2);  //第二个加数
	lua_pushinteger(L, a + b);  //压入结果
	return 1;					//返回1表示该方法只有一个返回值
}

函数写好了,那lua怎么才能调用该函数呢,第一种方法是使用lua_pushcfunction方法在lua的 源码中直接注册这个函数,然后重新编译lua就可以使用这个方法了,第二种方法把这个函数编译进一个lua C module,然后放入lua加载C模块的路径中,代码中require这个模块即可。由于第一种方法需要修改源码,并且每次修改C函数都需要重新编译lua,显然很麻烦,所以这里主要介绍第二种方法。

 

编写C模块有固定的代码结构:

 

//加载lua头文件
#include 
  
   

//编写C函数 static使外部无法直接访问这个函数
static int sum(lua_State *L)
{
	int a = lua_tointeger(L, 1); //第一个加数,函数的第一个参数总是索引1
	int b = lua_tointeger(L, 2);  //第二个加数
	lua_pushinteger(L, a + b);  //压入结果
	return 1;					//返回1表示该方法只有一个返回值
}

//声明一个luaL_Reg结构体数组
static const struct luaL_Reg funcs[] = {
	{"sum", sum},  //该结构体第一个成员是一个字符串,表示C模块中该函数的名称,第二个成员是指向该函数的指针
	{NULL, NULL}	// 该数组最后一个元素始终是 {NULL, NULL}
};

//最后注册这个数组,并给这个C模块定义一个名字,注意,这个函数名不是固定的,如果你的模块名叫xxx,那么这个函数名就应该是 luaopen_xxx
int luaopen_clib(lua_State *L)
{
	luaL_register(L, "clib", funcs);	//第二个参数要和你的模块名一致
	return 1;
}
  

把这个C文件编译成动态链接库,动态链接库的文件名命名规则为 xxx.so, 其中xxx就是你在C文件中定义的模块名

 

 

gcc -fPIC -c clib.c -I/usr/include     # -I指定lua头文件的位置
gcc -shared clib.o /usr/lib64/liblua-5.1.so -o clib.so  # /usr/lib64/liblua-5.1.so 为你的lua库路径,根据你的实际路径修改

把编译好的clib.so放到你的lua C模块路径下,那怎么知道自己lua加载C模块的路径呢,在lua脚本中打印一下 package.cpath 就可以获取到这个路径

 

 

[root@localhost lua-c-module]# lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> print(package.cpath)
./?.so;/usr/lib64/lua/5.1/?.so;/usr/lib64/lua/5.1/loadall.so
> 
可以看到 我这里的cpath路径有当前目录 ./ 和 /usr/lib64/lua/5.1/ , 所以 我把 clib.so 移动到/usr/lib64/lua/5.1/ 目录下

编写lua测试脚本

 

local clib = require "clib"

local s = clib.sum(3, 99)

print(s)

执行结果:

 

 

[root@localhost lua]# lua test.lua 
102

这样,一个简单的clib模块就开发好了,是不是也很简单呢,想要更深入的了解lua C模块的编写技术,欢迎关注后续文章:)。
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C语言头文件封装 下一篇C库头文件(C99)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

最新文章

热门文章

C 语言

C++基础

windows编程基础

linux编程基础

C/C++面试题目