Chapter 26: Calling C from Lua
要从lua 调用一个C 函数,必须注册它,就是说以舍适的方式把函数的地址传给它。C 函数从栈上获取lua 传过来的参数,而函数的返回值也会压入栈中。当lua 调用一个C 函数,第一个参数在栈中的索引总是1.
26.1 C Functions
???Any function registered with Lua must have this same prototype, defined as lua_CFunction in lua.h:
typedef int (*lua_CFunction) (lua_State *L); ???
-- version 1
static int l_sin (lua_State *L) {
double d = lua_tonumber(L, 1); /* get argument */
lua_pushnumber(L, sin(d)); /* push result */
return 1; /* number of results */
}
-- version 2
static int l_sin (lua_State *L) {
double d = luaL_checknumber(L, 1);
lua_pushnumber(L, sin(d));
return 1; /* number of results */
}
lua_pushcfunction(L, l_sin);
lua_setglobal(L, "mysin");
luaL_checknumber 检查给定的参数是否是数字,出错抛出message,否则返回数字值。
快速测式一个C 函数的方法是将上面的代码全部加入lua.c 文件
运行mysin('a') ,会得到错误消息:
bad argument #1 to 'mysin' (number expected, got string)
注意luaL_checknumber 是如何自动填充消息的。
26.2 C Modules
Lua 通过注册过程感知道C 函数,之后lua 调用的时侯就直接引用这个函数的地址(我们注册的时侯传给它的)。换句话说,一旦经过注册,Lua 不依赖函数名,包位置,或可见规则。通常,C 模块有一个公有函数用来打开这个库,而其它函数是私有的,以static 关键字声明。
当你使用C 来扩展lua,将你的代码设计成包是一个好主意,既使只有一个想要注册的函数。
扩充lua 库
linit.c
static const luaL_Reg lualibs[] = {
{"", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_DBLIBNAME, luaopen_debug},
{LUA_MATHLIBNAME, luaopen_my}, /* add a item */
{NULL, NULL}
};
------
lualib.h
/* add signature */
#define LUA_MYLIBNAME "my"
LUALIB_API int (luaopen_my) (lua_State *L);
------
lmylib.c
#include <stdlib.h>
#include <math.h>
#define lmylib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#undef PI
#define PI (3.14159265358979323846)
#define RADIANS_PER_DEGREE (PI/180.0)
/* 使用度数制 */
static int my_sin (lua_State *L) {
lua_pushnumber(L, sin(RADIANS_PER_DEGREE * luaL_checknumber(L, 1)));
return 1;
}
static const luaL_Reg mylib[] = {
{"sin", my_sin},
{NULL, NULL}
};
LUALIB_API int luaopen_my (lua_State *L) {
luaL_register(L, LUA_MYLIBNAME, mylib);
return 1;
}
-------
测试
print(my.sin(90))
--> 1