前言
经常使用Python语言进行开发,充分感受到动态语言的优势,列表和字典等高级数据结构在这些动态语言里非常方便使用,但是在C语言里就被束缚住了,并且C的库也比较少,很多功能都需要另外找别人写的库,或者自己造轮子。
LUA
LUA作为一种嵌入式语言,很多优秀的软件都有集成它,比如nginx和redis和游戏引擎。LUA虽然是解释型语言,但它比其他解释型语言都快。
LUA语言一般用于业务控制,适用于经常变化的用户逻辑控制,核心功能仍然使用C编写。达到快速开发和解耦的目的。
lua 源文件
一个简单的lua源文件,包含2个函数
--
-- Created by IntelliJ IDEA.
-- User: yumm
-- Date: 2018/4/12
-- Time: 11:50
-- To change this template use File | Settings | File Templates.
--
function add (x, y)
return x + y
end
function str_split(lines, c)
return lines..c.."可以的"
end
函数封装
参照LUA 5.3 版本的手册,封装了一个调用LUA的函数。
void call_va(lua_State *L, const char *func, const char *sig, ...) {
va_list vl;
int narg, nres; /* number of arguments and results */
va_start(vl, sig);
lua_getglobal(L, func); /* push function */
for (narg = 0; *sig; narg++) { /* repeat for each argument */
/* check stack space */
luaL_checkstack(L, 1, "too many arguments");
switch (*sig++) {
case 'd': /* double argument */
lua_pushnumber(L, va_arg(vl, double));
break;
case 'i': /* int argument */
lua_pushinteger(L, va_arg(vl, int));
break;
case 's': /* string argument */
lua_pushstring(L, va_arg(vl, char *));
break;
case '>': /* end of arguments */
goto endargs; /* break the loop */
default:
error(L, "invalid option (%c)\n", *(sig - 1));
}
}
endargs:
nres = strlen(sig); /* number of expected results */
if (lua_pcall(L, narg, nres, 0) != 0) /* do the call */
error(L, "error calling '%s': %s\n", func,
lua_tostring(L, -1));
nres = -nres; /* stack index of first result */
while (*sig) { /* repeat for each result */
switch (*sig++) {
case 'd': { /* double result */
int isnum;
double n = lua_tonumberx(L, nres, &isnum);
if (!isnum)
error(L, "wrong result type\n");
*va_arg(vl, double *) = n;
break;
}
case 'i': { /* int result */
int isnum;
int n = lua_tointegerx(L, nres, &isnum);
if (!isnum)
error(L, "wrong result type\n");
*va_arg(vl, int *) = n;
break;
}
case 's': { /* string result */
const char *s = lua_tostring(L, nres);
if (s == NULL)
error(L, "wrong result type\n");
*va_arg(vl, const char **) = s;
break;
}
default:
error(L, "invalid option (%c)\n", *(sig - 1));
}
nres++; }
va_end(vl);
}
调用方式为
call_va(L, "add", "ii>i", x, y, &z);
- L为lua对象
- add为lua 函数
ii>i
为 参数格式,'>'隔开入参和出参,这个格式表示函数add有2个入参,都为整数。返回1个值,类型为整数。- x, y, &z 为入参和出参,出参必须传址。
main函数
5.3 版本通过luaL_newstate()函数获取一个lua对象
int main(void) {
lua_State *L = luaL_newstate();
luaL_openlibs(L); /* opens the standard libraries */
int x = 3, y = 4, z;
char *s1 = "this is test line", *s = " 不一定 and next line", *new_str;
if (luaL_dofile(L, "../lua_fun.lua"))
error(L, "cannot run config. file: %s\n", lua_tostring(L, -1));
call_va(L, "add", "ii>i", x, y, &z);
lua_pop(L, -1);
printf("z is %d\n", z);
call_va(L, "str_split", "ss>s", s1, s, &new_str);
lua_pop(L, -1);
printf("new str is %s\n", new_str);
stackDump(L);
lua_close(L);
return 0;
}
C和LUA通讯机制
C和LUA通过一个虚拟栈进行数据交换。
- 当调用lua函数时,先获取lua文件中的函数(获取的结果会报错在栈中),然后依次压人参数。通过函数lua_pcall使lua发送函数调用。
- lua调用函数时,会将函数名和参数从栈中取出。运行函数,并将结果再压入栈中。如果调用出错,则会压入一条错误信息到栈中。
- lua_pcall返回后,需要通过函数lua_to从栈中获取返回值。lua_to函数不会将值从栈中弹出,需要通过函数lua_pop手动弹出返回值。
- 虚拟栈默认保存20个对象。
其余代码
头文件和辅助函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
void error(lua_State *L, const char *fmt, ...) {
va_list argp;
va_start(argp, fmt);
vfprintf(stderr, fmt, argp);
va_end(argp);
lua_close(L);
exit(EXIT_FAILURE);
}
static void stackDump(lua_State *L) {
int i;
int top = lua_gettop(L); /* depth of the stack */
if (top == 0) {
printf("<stack is emtpy>\n");
return;
}
for (i = 1; i <= top; i++) { /* repeat for each level */
int t = lua_type(L, i);
switch (t) {
case LUA_TSTRING: { /* strings */
printf("'%s'", lua_tostring(L, i));
break;
}
case LUA_TBOOLEAN: { /* Booleans */
printf(lua_toboolean(L, i) ? "true" : "false");
break; }
case LUA_TNUMBER: { /* numbers */
printf("%g", lua_tonumber(L, i));
break;
}
default: { /* other values */
printf("%s", lua_typename(L, t));
break; }
}
printf("\n"); /* put a separator */
}
}