Luaconf.h是配置的总集, 定义了平台相关的设置,是所有文件都包含的,即Rootly
Included。
0. 前言
开始关注Lua也是06年六月的事情,《程序员》的2006年第六期中,我独独看中了Lua,而不是当时我已经
比较熟悉的Python和Ruby,即使它们我都关注了好几年,但是都没有Lua给我的震撼大。于是那个夏天,
稍微地尝试读了Lua的代码。
开学后,我突然觉得自己有点受唆使,轻信了动态的福音,对Lisp系以及脚本系关注太多,有点不务正业
,于是回到了C,并且在年底投入了C++。C++的模板实在是让我爱不释手,于是寒假的时候写了一个内存
分配器,用了很多模板技巧,于是渐渐地对C++也熟悉了。
到了下学期,也就是现在,经历了一个小项目之后,在放弃C++/CLI的福音转向C#后,在.NET的信仰下,
在对DLR的期待中,决心写一个Lua的DLR实现,于是我回来了。
1. 基础设施代码解读
这部分包括基本数据结构,内存管理等部分,是重头戏。
1.1. 基本数据结构
正如Brooks所说“给我看你的流程图而藏起你的表,我将仍然莫名其妙。如果给我看你的表,那么我将不
再要你的流程图,因为它们太明显了。”不理解Lua最基本的数据结构,想要理解Lua是完全不可能的。
我们从包含的Root项,即Luaconf.h开始,它给出了最基本的信息。它是基于编译器和平台,对Lua进行最
基本的设置,是程序的元数据区。
第一段,平台选择:
#if defined(__STRICT_ANSI__)
#define LUA_ANSI
#endif
#if !defined(LUA_ANSI) &&
defined(_WIN32)
#define LUA_WIN
#endif
#if defined(LUA_USE_LINUX)
#define LUA_USE_POSIX
#define LUA_USE_DLOPEN #define LUA_USE_READLINE #endif
#if defined(LUA_USE_MACOSX)
#define LUA_USE_POSIX
#define LUA_DL_DYLD #endif
显然对于LUA_WIN来说,w32平台的确是用Unicode的,也就是非LUA_ANSI的。当定义了LUA_USE_LINUX和
LUA_USE_MACOSX后,需要POSIX标准以及定义相应的打开动态链接库的方法。这里可以看出Linux和BSD系
在打开动态库时的差别。
这里是POSIX的相关定义:
#if defined(LUA_USE_POSIX)
#define LUA_USE_MKSTEMP
#define LUA_USE_ISATTY
#define LUA_USE_POPEN
#define LUA_USE_ULONGJMP
#endif
这个是环境变量相关的:
#define
LUA_PATH "LUA_PATH"
#define
LUA_CPATH "LUA_CPATH"
#define LUA_INIT "LUA_INIT"
具体路径自己定。
于是这一段也很好理解了:
#if defined(_WIN32)
#define LUA_LDIR "!\\lua\\"
#define LUA_CDIR "!\\"
#define LUA_PATH_DEFAULT \
".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua"
#define LUA_CPATH_DEFAULT \
".\\?.dll;" LUA_CDIR"?.dll;"
LUA_CDIR"loadall.dll"
#else
#define LUA_ROOT "/usr/local/"
#define LUA_LDIR LUA_ROOT "share/lua/5.1/"
#define LUA_CDIR LUA_ROOT "lib/lua/5.1/"
#define LUA_PATH_DEFAULT \
"./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua"
#define LUA_CPATH_DEFAULT \
"./?.so;" LUA_CDIR"?.so;"
LUA_CDIR"loadall.so"
#endif
好了,接下来是Lua中特别指明的平台相关部分,库的加载:
#if defined(LUA_BUILD_AS_DLL)
#if defined(LUA_CORE) || defined(LUA_LIB)
#define LUA_API __declspec(dllexport)
#else
#define LUA_API __declspec(dllimport)
#endif
#else
#define LUA_API extern
#endif
#define LUALIB_API LUA_API
这里的LUA_BUILD_AS_DLL就是在编译的时候提交给编译器的一个选项。下面是另外一些自定义关键字。
#if defined(luaall_c)
#define LUAI_FUNC static
#define LUAI_DATA
#elif defined(__GNUC__) &&
((__GNUC__*100 + __GNUC_MINOR__) >= 302)
&& \
defined(__ELF__)
#define
LUAI_FUNC __attribute__((visibility("hidden")))
extern
#define LUAI_DATA LUAI_FUNC
#else
#define LUAI_FUNC extern
#define LUAI_DATA extern
#endif
显然,一般的编译器将会标记为static,而当编译器版本为GCC 3.2+或者是ELF时,就可以使用
__attribute__((visibility("hidden"))) extern了。
#define LUA_QL(x) "'" x "'"
#define LUA_QS LUA_QL("%s")
要理解这段代码其实也不难,随便找个程序,把这两个宏代入,比如,printf(LUA_QS, "hello world")
中,你可以得到'hello world'的输出。
#define LUA_IDSIZE 60
一下是Lua作为一个独立程序的设置:
#if defined(lua_c) || defined(luaall_c)
#if defined(LUA_USE_ISATTY)
#include
#define lua_stdin_is_tty() isatty(0)
#elif defined(LUA_WIN)
#include
#include
#define
lua_stdin_is_tty() _isatty(_fileno(stdin))
#else
#define
lua_stdin_is_tty() 1 #endif
这里有几种选择,如果定义了LUA_USE_ISATTY,那么则使用unistd.h(POSIX标准)并将tty定义为
isatty(0),否则,检查是否为w32环境,则定义_isatty(_fileno(stdin)),再不然,用传统的Unix的习
惯,stdin的文件号(即w32中的文件句柄)为1。
提示符部分:
#define
LUA_PROMPT ">
"
#define
LUA_PROMPT2 ">>
"
程序名称:
#define LUA_PROGNAME "lua"
#define LUA_MAXINPUT 512
#if defined(LUA_USE_READLINE)
#include
#include
#include
#define lua_readline(L,b,p) ((void)L,
((b)=readline(p)) != NULL)
#define lua_saveline(L,idx) \
if (lua_strlen(L,idx) >
0) \
add_history(lua_tostring(L,
idx)); #define lua_freeline(L,b) ((void)L, free(b))
#else
#define lua_readline(L,b,p) \
((void)L, fputs(p, stdout),
fflush(stdout), \
fgets(b, LUA_MAXINPUT, stdin) !=
NULL) #define lua_saveline(L,idx) { (void)L; (void)idx;
}
#define lua_freeline(L,b) { (void)L; (void)b;
}
#endif
#endif
但是不知道为什么,它要求了Lua_State的L,但是直接将之Discard了。
再往下就是GC相关Configurations了。
#define LUAI_GCPAUSE 200
#define LUAI_GCMUL 200
#undef LUA_COMPAT_GETN
#undef LUA_COMPAT_LOADLIB
#define LUA_COMPAT_VARARG
#define LUA_COMPAT_MOD
#define LUA_COMPAT_LSTR 1
#define LUA_COMPAT_GFIND
#define LUA_COMPAT_OPENLIB
#if defined(LUA_USE_APICHECK)
#include
#define luai_apicheck(L,o) { (void)L; assert(o);
}
#else
#define luai_apicheck(L,o) { (void)L; }
#endif
如果定义了这个宏,则用标准库的assert.h的assert来判断o,但是不知为什么还是抛弃了L。
#if INT_MAX-20 < 32760
#define LUAI_BITSINT 16
#elif INT_MAX > 2147483640L
#define LUAI_BITSINT 32
#else
#error "you must define LUA_BITSINT with number of bits in an
integer"
#endif
#if LUAI_BITSINT >= 32
#define LUAI_UINT32 unsigned int
#define LUAI_INT32 int
#define LUAI_MAXINT32 INT_MAX
#define LUAI_UMEM size_t
#define LUAI_MEM ptrdiff_t
#else
#define LUAI_UINT32 unsigned long
#define LUAI_INT32 long
#define LUAI_MAXINT32 LONG_MAX
#define LUAI_UMEM unsigned long
#define LUAI_MEM long
#endif
#define LUAI_MAXCALLS 20000
#define LUAI_MAXCSTACK 2048
#define LUAI_MAXCCALLS 200
#define LUAI_MAXVARS 200
#define LUAI_MAXUPVALUES 60
#define
LUAL_BUFFERSIZE BUFSIZ
#define LUA_NUMBER_DOUBLE
#define LUA_NUMBER double
#define LUAI_UACNUMBER double
#define
LUA_NUMBER_SCAN "%lf"
#define
LUA_NUMBER_FMT "%.14g"
#define lua_number2str(s,n) sprintf((s),
LUA_NUMBER_FMT, (n))
#define LUAI_MAXNUMBER2STR 32
#define lua_str2number(s,p) strtod((s), (p))
#if defined(LUA_CORE)
#include
#define luai_numadd(a,b) ((a)+(b))
#define luai_numsub(a,b) ((a)-(b))
#define luai_nummul(a,b) ((a)*(b))
#define luai_numdiv(a,b) ((a)/(b))
#define luai_nummod(a,b) ((a) -
floor((a)/(b))*(b))
#define luai_numpow(a,b) (pow(a,b))
#define
luai_numunm(a) (-(a))
#define
luai_numeq(a,b) ((a)==(b))
#define
luai_numlt(a,b) ((a)
#define
luai_numle(a,b) ((a)<=(b))
#define luai_numisnan(a) (!luai_numeq((a),
(a)))
#endif
#if defined(LUA_NUMBER_DOUBLE) &&
!defined(LUA_ANSI) &&
!defined(__SSE2__) && \
(defined(__i386) || defined (_M_IX86) || defined(__i386__))
#if defined(_MSC_VER)
#define lua_number2int(i,d) __asm fld d __asm fistp i
#define
lua_number2integer(i,n) lua_number2int(i,
n)
#else
union luai_Cast { double l_d; long l_l; };
#define lua_number2int(i,d) \
{ volatile union luai_Cast u; u.l_d = (d) +
6755399441055744.0; (i) = u.l_l; }
#define
lua_number2integer(i,n) lua_number2int(i,
n)
#endif
#else
#define lua_number2int(i,d) ((i)=(int)(d))
#define
lua_number2integer(i,d) ((i)=(lua_Integer)(d))
#endif
#define LUAI_USER_ALIGNMENT_T union { double u;
void *s; long l; }
#if defined(__cplusplus)
#define LUAI_THROW(L,c) throw(c)
#define LUAI_TRY(L,c,a) try { a } catch(...)
\
{ if ((c)->status == 0)
(c)->status = -1; }
#define luai_jmpbuf int
#elif defined(LUA_USE_ULONGJMP)
#define
LUAI_THROW(L,c) _longjmp((c)->b,
1)
#define LUAI_TRY(L,c,a) if
(_setjmp((c)->b) == 0) { a }
#define luai_jmpbuf jmp_buf
#else
#define
LUAI_THROW(L,c) longjmp((c)->b,
1)
#define LUAI_TRY(L,c,a) if
(setjmp((c)->b) == 0) { a }
#define luai_jmpbuf jmp_buf
#endif
这部分是源代码级的异常处理。Unix下用_longjmp/_setjmp效率更加高。
#define LUA_MAXCAPTURES 32
#if defined(loslib_c) || defined(luaall_c)
#if defined(LUA_USE_MKSTEMP)
#include
#define LUA_TMPNAMBUFSIZE 32
#define lua_tmpnam(b,e) { \
strcpy(b, "/tmp/lua_XXXXXX"); \
e = mkstemp(b); \
if (e != -1) close(e); \
e = (e == -1); }
#else
#define LUA_TMPNAMBUFSIZE L_tmpnam
#define lua_tmpnam(b,e) { e =
(tmpnam(b) == NULL); }
#endif
#endif
#if defined(LUA_USE_POPEN)
#define lua_popen(L,c,m) ((void)L,
popen(c,m))
#define lua_pclose(L,file) ((void)L, (pclose(file)
!= -1))
#elif defined(LUA_WIN)
#define lua_popen(L,c,m) ((void)L,
_popen(c,m))
#define lua_pclose(L,file) ((void)L,
(_pclose(file) != -1))
#else
#define lua_popen(L,c,m) ((void)((void)c,
m), \
luaL_error(L, LUA_QL("popen") "
not supported"), (FILE*)0)
#define
lua_pclose(L,file) ((void)((void)L,
file), 0)
#endif
#if defined(LUA_USE_DLOPEN)
#define LUA_DL_DLOPEN
#endif
#if defined(LUA_WIN)
#define LUA_DL_DLL
#endif
#define LUAI_EXTRASPACE 0
#define
luai_userstateopen(L) ((void)L)
#define
luai_userstateclose(L) ((void)L)
#define luai_userstatethread(L,L1) ((void)L)
#define
luai_userstatefree(L) ((void)L)
#define luai_userstateresume(L,n) ((void)L)
#define luai_userstateyield(L,n) ((void)L)
#if defined(LUA_USELONGLONG)
#define
LUA_INTFRMLEN "ll"
#define LUA_INTFRM_T long
long
#else
#define
LUA_INTFRMLEN "l"
#define LUA_INTFRM_T long
#endif
综上所述,这个文件定义的最重要的几个方面:动态库操作(重中之重,根据Programming in Lua的说法
,这方面是Lua中唯一依赖于平台的做法),异常及错误处理,打印数据格式,数字运算,C Stack,GC,
对齐等方面。