programing in 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_open 创建一个新环境(或 state)。lua_open 创建一个新的环境时,这个环
境并不包括预定义的函数,甚至是 print。为了保持 Lua 的苗条,所有的标准库以单独的
包?供,所以如果你不需要就不会强求你使用它们。头文件 lualib.h 定义了打开这些库
的函数。例如,调用 luaopen_io,以创建 io table 并注册 I/O 函数(io.read,io.write 等等)
到 Lua 环境中。

堆栈

C 和 lua之间的数据交互,是通过堆栈来实现的。

设计原因:解决C与lua通信问题

  • 动态与静态类型系统的不匹配
  • 自动与手动内存管理的不一致

相关操作API

压栈的函数
void lua_pushnil (lua_State *L);
void lua_pushboolean (lua_State *L, int bool);
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s,
size_t length);
void lua_pushstring (lua_State *L, const char *s);

查询函数
API 用索引来访问栈中的元素。bottom=1,top = n ; bottom = -n,top = -1

int lua_is... (lua_State *L, int index);  --是否能被转换成指定的那种类型

从栈取值
int lua_toboolean (lua_State *L, int index);
double lua_tonumber (lua_State *L, int index);
const char * lua_tostring (lua_State *L, int index);
size_t lua_strlen (lua_State *L, int index);

Lua_tostring 函数返回一个指向字符串的内部拷贝的指针。
只要这个指针对应的值还在栈内,Lua 会保证这个指针一直有效。


其 他 堆 栈 操作
int lua_gettop (lua_State *L);  return #stack
void lua_settop (lua_State *L, int index);  将堆栈中某个索引设置为栈顶,“小清大补”
void lua_pushvalue (lua_State *L, int index); 将堆栈中某个索引的拷贝设置为栈顶
void lua_remove (lua_State *L, int index);  remove index
void lua_insert (lua_State *L, int index);  移动栈顶到制定位置
void lua_replace (lua_State *L, int index); 用栈顶替换某个index

stack操作示例

int main (void) {
    lua_State *L = lua_open();
    lua_pushboolean(L, 1); lua_pushnumber(L, 10);
    lua_pushnil(L); lua_pushstring(L, "hello");
    stackDump(L);
        /* true 10 nil `hello' */
    lua_pushvalue(L, -4); stackDump(L);
        /* true 10 nil `hello' true */
    lua_replace(L, 3); stackDump(L);
        /* true 10 true `hello' */
    lua_settop(L, 6); stackDump(L);
        /* true 10 true `hello' nil nil */
    lua_remove(L, -3); stackDump(L);
        /* true 10 true nil nil */
    lua_settop(L, -5); stackDump(L);
        /* true */
    lua_close(L);
    return 0;
}
tips
  • 当你调用 Lua 时,它只会改变栈顶部分;C则更灵活。
  • 无论你何时压入一个元素到栈上,你有责任确保在栈上有空间来做这件事情。LUA_MINSTACK=20
  • 当一个 C 函数返回后,Lua 会清理他的栈

在C中调用lua代码

加载lua文件

lua代码文件
    XX.lua = {
        width = 111,
        height = 222,
    }
C代码:
    -- 引入lua库
    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
    void load (char *filename, int *width, int *height) {
        -- 创建lua解析环境
        lua_State *L = lua_open();
        luaopen_base(L);
        luaopen_io(L);
        luaopen_string(L);
        luaopen_math(L);

        -- 加载lua代码
        if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
            error(L, "cannot run configuration file: %s",lua_tostring(L, -1));

        -- 获取lua的执行结果

        -- lua代码声明了两个全局变量,通过这个操作把它们压到堆栈里;
        lua_getglobal(L, "width");
        lua_getglobal(L, "height");

        -- 校验width
        if (!lua_isnumber(L, -2))
            error(L, "`width' should be a number\n");
        -- 校验height
        if (!lua_isnumber(L, -1))
            error(L, "`height' should be a number\n");
        -- 赋值
        *width = (int)lua_tonumber(L, -2);
        *height = (int)lua_tonumber(L, -1);
        lua_close(L);
}

小结

之前看堆栈的时候,总是不知道怎么去理解好;感觉应该从通信开始讲,再讲原理会好一些。

调用lua函数

流程:首先,将被调用的函数入栈;第二,依次将
所有参数入栈;第三,使用 lua_pcall 调用函数;最后,从栈中获取函数执行返回的结果。

lua:
    function f (x, y)
        return (x^2 * math.sin(y))/(1 - x)
    end
c:
加载lua文件的代码参考上文
/* call a function `f' defined in Lua */
double f (double x, double y) {
    double z;
    /* push functions and arguments */
    lua_getglobal(L, "f"); /* function to be called压栈 */
    lua_pushnumber(L, x); /* push 1st argument */
    lua_pushnumber(L, y); /* push 2nd argument */
    /* do the call (2 arguments, 1 result) */
    if (lua_pcall(L, 2, 1, 0) != 0)
        error(L, "error running function `f': %s",lua_tostring(L, -1));
    /* retrieve result */
    if (!lua_isnumber(L, -1))
        error(L, "function `f' must return a number");
    z = lua_tonumber(L, -1);
    lua_pop(L, 1); /* pop returned value */
    return z;
}


高级例子:
用函数名、参数说明作为参数来自动完成调用函数时的全部工作
#include <stdarg.h>
void call_va (const char *func, const char *sig, ...) {
    va_list vl;
    int narg, nres; /* number of arguments and results */
    va_start(vl, sig);
    lua_getglobal(L, func); /* get function */
    /* push arguments */
    narg = 0;
    while (*sig) { /* push arguments */
        switch (*sig++) {
            case 'd': /* double argument */
                lua_pushnumber(L, va_arg(vl, double));
            break;
                case 'i': /* int argument */
            lua_pushnumber(L, va_arg(vl, int));
                break;
            case 's': /* string argument */
                lua_pushstring(L, va_arg(vl, char *));
                break;
            case '>':
                goto endwhile;
            default:
                error(L, "invalid option (%c)", *(sig - 1));
        }
        narg++;
        luaL_checkstack(L, 1, "too many arguments");
    } 
    endwhile:
    /* do the call */
    nres = strlen(sig); /* number of expected results */
    if (lua_pcall(L, narg, nres, 0) != 0) /* do the call */
        error(L, "error running function `%s': %s",
    func, lua_tostring(L, -1));
    /* retrieve results */
    nres = -nres; /* stack index of first result */
    while (*sig) { /* get results */
        switch (*sig++) {
            case 'd': /* double result */
                if (!lua_isnumber(L, nres))
                error(L, "wrong result type");
                *va_arg(vl, double *) = lua_tonumber(L, nres);
                break;
            case 'i': /* int result */
                if (!lua_isnumber(L, nres))
                error(L, "wrong result type");
                *va_arg(vl, int *) = (int)lua_tonumber(L, nres);
                break;
            case 's': /* string result */
                if (!lua_isstring(L, nres))
                error(L, "wrong result type");
                *va_arg(vl, const char **) = lua_tostring(L, nres);
                break;
            default:
             error(L, "invalid option (%c)", *(sig - 1));
        }
        nres++;
    }
    va_end(vl);
}

lua调用C函数

从 Lua调用 C 函数我们必须注册函数,也就是说,我们必须把 C 函数的地址以一个适当的方式
传递给 Lua 解释器。

调用过程也是通过堆栈交互,用来交互的栈不是全局变量,每一个函数都有他自己的私有栈。

C 函数从栈中获取lua传过来的参数,调用结束后将返回结果放到栈中。为了区分返回结果和栈中的其他的值,每个 C 函数还会返回结果的个数。

任何在 Lua 中注册的函数必须有同样的原型,这个原型声明定义就是 lua.h 中的
lua_CFunction:

typedef int (*lua_CFunction) (lua_State *L);

/*
这个类型定义了指向某个函数的指针,对应上文提到的:
必须把 C 函数的地址以一个适当的方式传递给 Lua 解释器。
*/

如何注册函数

最底层方法
static int l_sin (lua_State *L) {
    double d = lua_tonumber(L, 1); /* get argument */
    lua_pushnumber(L, sin(d)); /* push result */
    return 1; /* number of results */
}
lua_pushcfunction(l, l_sin);
lua_setglobal(l, "mysin");

第一行将类型为 function 的值入栈,第二行将 function 赋值给全局变量 mysin。