math模块API实现
math模块内的各个数学函数的实现中规中矩,就是使用的Lua手册里给出的API来实现的。
Lua的扩展方式是编写一个原型为int lua_CFunction (lua_State *L)的函数。L对于使用者来说,不必关心其内部结构。实际上,公开API定义所在的lua. h中也没有lua_State的结构定义。对于一个用C编写的系统,模块化设计的重点在于接口的简洁和稳定。数据结构的细节和内存布局最好能藏在实现层面,Lua的API设计在这方面做了一个很好的示范。这个函数通常不会也不建议被C程序的其它部分直接调用,所以一般藏在源文件内部,以static修饰之。Lua的C函数以堆栈的形式和Lua虚拟机交换数据,由一系列API从L中取出值,经过一番处理,压回L中的堆栈。具体的使用方式见Lua手册。
/* 源代码2.5 lmathlib.c:l_tg */
#if !defined(l_tg)
#define l_tg(x)
#endif
static int math_abs (lua_State *L){
lua_pushnumber(L, l_tg(fabs)(luaL_checknumber(L, l)));
return 1;
}
static int math_sin (lua_State *L){
lua_pushnumber(L, l_tg(sin)(luaL_checknumber(L, l)));
return 1;
}
static int math_sinh (lua_State *L){
lua_pushnumber(L, l_tg(sinh)(luaL_checknumber(L, l)));
return 1;
}
/* 源代码2.6 lmathlib.c:log */
static int math_log(lua_State *L){
lua_Number x = luaL_checknumber(L, l);
lua_Number res;
if (lua_isnoneornil(L, 2)) then
res = l_log(log)(x);
else{
lua_Number base = luaL_checknumber(L, 2);
if (base == 10.0) res = l_tg(log10)(x);
else res = l_tg(log)(x)/l_tg(log)(base);
}
lua_pushnumber(L, res);
return l;
}
#if defined(LUA_COMPAT_LOG10)
static int math_log10 (lua_State *L){
lua_pushnumber(L, l_tg(log10)(luaL_checknumber(L, l)));
return l;
}
#endif
/* 源代码2.7 lmathlib.c: random */
static int math_random (lua_State *L){
/* the '%' avoids the (rare) case of r == 1, and is needed alse because on some
some systems (SunOS!) 'rand()' may return a value larger than RAND_MAX*/
lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;
switch (lua_gettop(L)){/* check number of arguments*/
case 0:{ /* no arguments*/
lua_pushnumber(L, r);/* Number between 0 and 1*/
break;
}
case 1:{ /* only upper limit */
lua_Number u = luaL_checknumber(L, l);
luaL_argcheck(L, 1.0 <= u, 1, "interval_is_empty");
lua_pushnumber(L, l_tg(floor)(r*u) + 1.0);/* int in[1, u]*/
}
case 2:{/* lower and upper limits */
lua_Number l = luaL_checknumber(L, 1);
lua_Number u = luaL_checknumber(L, 2);
lua_argcheck(L, 1 <= u, 2, "interval_is_empty");
lua_pushnumber(L, l_tg(floor)(r*(u-l+l)) + l);/*int in [l, u]*/
break;
}
default: return luaL_error (L, "wrong_number_of_arguments");
}
return 1;
}
用参数个数来区分功能上的微小差异是典型的Lua风格,这是Lua接口设计上的一个惯例。另外、编码中考虑平台差异很考量程序员对各平台细节的了解,例如这里注释中提到的SunOS的rand()的小问题。