24.1 第一个示例程序 |
通过一个简单的应用程序让我们开始这个预览:一个独立的Lua解释器的实现。我们写一个简单的解释器,代码如下:
#include <stdio.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
int main (void)
{
char buff[256];
int error;
lua_State *L = lua_open(); /* opens Lua */
luaopen_base(L); /* opens the basic library */
luaopen_table(L); /* opens the table library */
luaopen_io(L); /* opens the I/O library */
luaopen_string(L); /* opens the string lib. */
luaopen_math(L); /* opens the math lib. */
while (fgets(buff, sizeof(buff), stdin) != NULL) {
error = luaL_loadbuffer(L, buff, strlen(buff),
"line") || lua_pcall(L, 0, 0, 0);
if (error) {
fprintf(stderr, "%s", lua_tostring(L, -1));
lua_pop(L, 1);/* pop error message from the stack */
}
}
lua_close(L);
return 0;
}
头文件lua.h定义了Lua提供的基础函数。其中包括创建一个新的Lua环境的函数(如lua_open),调用Lua函数(如lua_pcall)的函数,读取/写入Lua环境的全局变量的函数,注册可以被Lua代码调用的新函数的函数,等等。所有在lua.h中被定义的都有一个lua_前缀。
头文件lauxlib.h定义了辅助库(auxlib)提供的函数。同样,所有在其中定义的函数等都以luaL_打头(例如,luaL_loadbuffer)。辅助库利用lua.h中提供的基础函数提供了更高层次上的抽象;所有Lua标准库都使用了auxlib。基础API致力于economy and orthogonality,相反auxlib致力于实现一般任务的实用性。当然,基于你的程序的需要而创建其它的抽象也是非常容易的。需要铭记在心的是,auxlib没有存取Lua内部的权限。它完成它所有的工作都是通过正式的基本API。
Lua库没有定义任何全局变量。它所有的状态保存在动态结构lua_State中,而且指向这个结构的指针作为所有Lua函数的一个参数。这样的实现方式使得Lua能够重入(reentrant)且为在多线程中的使用作好准备。
函数lua_open创建一个新环境(或state)。lua_open创建一个新的环境时,这个环境并不包括预定义的函数,甚至是print。为了保持Lua的苗条,所有的标准库以单独的包提供,所以如果你不需要就不会强求你使用它们。头文件lualib.h定义了打开这些库的函数。例如,调用luaopen_io,以创建io table并注册I/O函数(io.read,io.write等等)到Lua环境中。
创建一个state并将标准库载入之后,就可以着手解释用户的输入了。对于用户输入的每一行,C程序首先调用luaL_loadbuffer编译这些Lua代码。如果没有错误,这个调用返回零并把编译之后的chunk压入栈。(记住,我们将在下一节中讨论魔法般的栈)之后,C程序调用lua_pcall,它将会把chunk从栈中弹出并在保护模式下运行它。和luaL_laodbuffer一样,lua_pcall在没有错误的情况下返回零。在有错误的情况下,这两个函数都将一条错误消息压入栈;我们可以用lua_tostring来得到这条信息、输出它,用lua_pop将它从栈中删除。
注意,在有错误发生的情况下,这个程序简单的输出错误信息到标准错误流。在C中,实际的错误处理可能是非常复杂的而且如何处理依赖于应用程序本身。Lua核心决不会直接输出任何东西到任务输出流上;它通过返回错误代码和错误信息来发出错误信号。每一个应用程序都可以用最适合它们自己的方式来处理这些错误。为了讨论的简单,现在我们假想一个简单的错误处理方式,就象下面代码一样,它只是输出一条错误信息、关闭Lua state、退出整个应用程序。
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
void error (lua_State *L, const char *fmt, ...) {
va_list argp;
va_start(argp, fmt);
vfprintf(stderr, argp);
va_end(argp);
lua_close(L);
exit(EXIT_FAILURE);
}
稍候我们再详细的讨论关于在应用代码中如何处理错误.因为你可以将Lua和C/C++代码一起编译,lua.h并不包含这些典型的在其他C库中出现的整合代码:
#ifdef __cplusplus
extern "C" {
#endif
...
#ifdef __cplusplus
}
#endif
因此,如果你用C方式来编译它,但用在C++中,那么你需要象下面这样来包含lua.h头文件。
extern "C" {
#include <lua.h>
}
一个常用的技巧是建立一个包含上面代码的lua.hpp头文件,并将这个新的头文件包含进你的C++程序。