Lua解释器源码(二):math库

一、 数学库

Lua的math库很简单,基本由一组标准的数学函数构成,使用起来很简单,如对x取正弦,则用 math.sin(x)

当c语言调用使用math库的lua代码时,要打开math库:

int Luaopen_math(lua_State *L);打开和初始化数学库

二、 源码分析

Lua math库在Lua源码文件lmathlib.c中定义。 lmathlib.c是Lua源码中比较简单的文件,对C的math库进行了简单的包装。 例如下个函数是对C的sin函数进行包装:

static int math_sin (lua_State *L) {
  lua_pushnumber(L, sin(luaL_checknumber(L, 1)));
  return 1;
}
其中LuaL_checknumber(L,1)返回stack1的数字,计算sin后在压回到栈中。

然后,定义一个LuaL_Reg结构的数组mathlib,包含所有上述Lua math库函数。 LuaL_Reg结构定义如下:

// lauxlib.h
typedef struct luaL_Reg {
  const char *name;
  lua_CFunction func;
} luaL_Reg;

// lua.h
typedef int (*lua_CFunction) (lua_State *L);
其中第一个成员是函数名,即在Lua语言中使用的函数名。 第二个成员是lua_CFunction类型,指向了Lua语言中函数绑定的C函数。 在Lua中,只有Lua_CFunction类型的函数才能直接向Lua注册,lua_CFunction实际上是一个函数指针。 故LuaL_Reg数组如下:
static const luaL_Reg mathlib[] = {
  {"abs",   math_abs},
  {"acos",  math_acos},
  {"asin",  math_asin},
  {"atan2", math_atan2},
  {"atan",  math_atan},
  {"ceil",  math_ceil},
  {"cosh",   math_cosh},
  {"cos",   math_cos},
  {"deg",   math_deg},
  {"exp",   math_exp},
  {"floor", math_floor},
  {"fmod",   math_fmod},
  {"frexp", math_frexp},
  {"ldexp", math_ldexp},
  {"log10", math_log10},
  {"log",   math_log},
  {"max",   math_max},
  {"min",   math_min},
  {"modf",   math_modf},
  {"pow",   math_pow},
  {"rad",   math_rad},
  {"random",     math_random},
  {"randomseed", math_randomseed},
  {"sinh",   math_sinh},
  {"sin",   math_sin},
  {"sqrt",  math_sqrt},
  {"tanh",   math_tanh},
  {"tan",   math_tan},
  {NULL, NULL}
};

最后定义luaopen_math函数:

LUALIB_API int luaopen_math (lua_State *L) {
  luaL_register(L, LUA_MATHLIBNAME, mathlib);
  lua_pushnumber(L, PI);
  lua_setfield(L, -2, "pi");
  lua_pushnumber(L, HUGE_VAL);
  lua_setfield(L, -2, "huge");
#if defined(LUA_COMPAT_MOD)
  lua_getfield(L, -1, "fmod");
  lua_setfield(L, -2, "mod");
#endif
  return 1;
}

这个函数首先向vm中注册库的名字LUA_MATHLIBNAME和所有的函数mathlib。 然后把几个定义的常量压入栈中,然后修改t["pi"]和t["huge"]的值。

经过以上这些加载,在lua中使用math.sin(x)时,首先把x入栈,然后在全局表里查找math表,然后查找其子方法sin,接着调用到其绑定的函数math_sin。math_sin从栈中取出x计算sin后再压入栈。这样就完成了一次math.sin的调用。

发表评论