Lua通过标准库实现了核心之外的功能,如math库,I/O库等。本文来分析math标准库的实现, 代码位于lmathlib.c
。
math库提供了一组标准的数学函数,如绝对值函数, 三角函数,随机数函数等。调用方式为:
1 | a = math.abs(-1) |
标准库由C语言函数实现,提供给Lua程序使用。Lua程序与C函数通过虚拟栈交互。Lua将参数压入栈中,C函数从栈中获取参数,并将结果压入栈中,C函数返回入栈的结果数量。C函数无需在压入结果前清空栈,Lua会在函数执行完成后从栈中获取结果并自动清空结果下的内容。
Lua调用的C函数的格式是固定的,定义在lua.h中:
1 | typedef int (*lua_CFunction) (lua_State *L); |
下面分析math库具体代码:
标准库加载时,会调用相应的初始化函数,格式为luaopen_xxx
, math库的初始化函数为luaopen_math:
1 | /* |
luaopen_math()首先调用luaL_newlib()创建一个table压入栈中, 然后将mathlib数组中的函数注册到table中。
mathlib定义:
1 | static const luaL_Reg mathlib[] = { |
luaL_Reg的第一个成员表示Lua函数名称,第二个成员表示相应的C函数指针。如:{“abs”, math_abs}
表示Lua中调用”math.abs”时,会执行C函数math_abs()。
此时,创建table后,栈结构为:
接着,luaopen_math()调用lua_pushnumber()将常量PI入栈,PI定义如下:
1 |
此时栈结构为:
然后调用lua_setfield()在table中添加”pi”变量,赋值为常量PI
, 相当于:
1 |
|
此时,栈结构恢复为之前的只有math table的状态。
接下来同样方式注册huge
, mininteger
, maxinteger
三个变量,完成math库的初始化工作。
当相应的Lua函数被调用时,对应的C函数被执行,下面以math_abs()
为例分析相应C函数的实现:
1 |
|
math_abs()函数从栈中获取参数,然后根据参数为浮点数还是整数分别计算绝对值,最终将结果压入栈中,并返回压入栈中结果的个数。
标准库与Lua的C动态扩展代码实现是一致的,只是编译和加载方式不同。标准库与核心编译在同一个库,动态扩展编译成 SO 文件,标准库通过调用luaL_openlibs()进行加载,动态扩展通过”require”语句加载。后续我们再分析luaL_openlibs()和”require”的处理流程。