Lua程序设计(4th) 第四部分 C语言API

第27章 C语言API总览

    lua.h(前缀 lua_): 声明了 Lua 提供的基础函数,其中包括创建新 Lua 环境的函数、调用 Lua 函数的函数等等。

    lauxlib.h(前缀 luaL_): 辅助( auxiliary library) 使用 lua.h 提供的基础 API 来提供更高层次的抽象,不能访问 Lua 的内部元素,而只能通过lua.h 中声明的官方基础 API 完成所有工作。
#include <stdio.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
int main (void) {
    lua_State *L = luaL_newstate();   //打开 Lua
    luaL_openlibs(L);                 //打开所有的标准库
    char buff[256];
    while (fgets(buff, sizeof(buff), stdin) != NULL) {
        //1. 编译用户输入的每一行内容,并向栈中压入编译得到的函数
        //2. 从栈中弹出编译后得到的函数,并以保护模式运行
        int error = luaL_loadstring(L, buff) ||
                    lua_pcall(L, 0, 0, 0);
        //3. 错误信息处理:获取栈中的错误信息并打印,随后删除栈中的错误信息
        if (error) { 
            fprintf(stderr, "%s
", lua_tostring(L, -1));
            lua_pop(L, 1);
        }
    }
    lua_close(L);
    return 0;
}
//C API压栈函数:
void lua_pushnil      (lua_State *L);
void lua_pushboolean  (lua_State *L, int bool);
void lua_pushnumber   (lua_State *L, lua_Number n);  //默认double
void lua_pushinteger  (lua_State *L, lua_Integer n); //默认long long
void lua_pushlstring  (lua_State *L, const char *s, size_t len);//任意二进制数据,Lua会生成一个内部副本或复用已有字符串
void lua_pushstring   (lua_State *L, const char *s); //''结尾的字符串,Lua会生成一个内部副本或复用已有字符串

// minimum Lua stack available to a C function
// #define LUA_MINSTACK 20
// 检查找中是否有足够的空间
int lua_checkstack(lua_State *L, int sz);
void lual_checkstack(lua_State *L, int sz, const char* msg);
//C API 查询元素
//栈顶  [3]     [-1]
//     [2]     [-2]
//栈底  [1]     [-3]

//将栈顶的值返回
int lua_toboolean (lua_State *L, int idx);
const char *lua_tolstring (lua_State *L, int idx, size_t *len);
int lua_tothread (lua_State *L, int idx);
int lua_tonumber (lua_State *L, int idx);
int lua_tointeger (lua_State *L, int idx);

//检查找中的一个元素是否为特定的类型. 
//函数 lua_isnumber 则是检查该值是否能被转换为此特定类型
int lua_is*(lua_State *L, int idx); //*: nil, number, string, table
lua_pushvalue(L, -4); // push the value of the index to the stack
lua_replace(L, 3);    // pop a value and replace the index's
lua_settop(L, 6);     // set the top index, fill 'nil'
lua_rotate (L, idx, -1);//将idx处元素移动到-1位置
#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1))
lua_insert(L, -2);    // move top element to the bottom
LUA_POP(L, 1); 

第28章 扩展应用

    读取lua配置文件

--color.lua
BLUE = {r=0.1, g=0.1, b=1}
RED = {r=1, g=0.1, b=0}
background = BLUE
background = RED
/* assume that table is on the stack top */
int getfield(lua_State *L, const char *key)
{
    int result;
    lua_pushstring(L, key);
    lua_gettable(L, -2); /* push background[key] and remove key*/

    if (!lua_isnumber(L, -1))
        error(L, "invalid component in background color");
    result = lua_tonumber(L, -1) * MAX_COLOR;
    lua_pop(L, 1); /* remove number */
    return result;
}

void loadTable()
{
    char filename[] = "c_call_lua/color.lua";
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);      // This is necessary for os.genenv
    if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) {
        error(L, "cannot run configuration file: %s", lua_tostring(L, -1));
    }

    lua_getglobal(L, "background");
    if (!lua_istable(L, -1))
        error(L, "`background' is not a valid color table");
    int red = getfield(L, "r");
    int green = getfield(L, "g");
    int blue = getfield(L, "b");
    printf ("r = %d, g = %d, b = %d
", red, green, blue);
    lua_close(L);
}
void error (lua_State *L, const char *fmt, ...) {
    va_list vl;
    va_start(vl, fmt);
    vfprintf(stderr, fmt, vl);
    va_end(vl);
    lua_close(L);
    exit(EXIT_FAILURE);
    printf ("won't print
");
}

void callLuaf(lua_State *L, const char *func, const char *sig, ...) {
    lua_getglobal(L, func);
    //if (lua_getglobal(L, func) != 0)
    //    error(L, "error getting function `%s': %s", func, lua_tostring(L, -1));
    int narg = 0;/* number of arguments */
    va_list vl;
    va_start(vl, sig);
    while (*sig) {
        /* push arguments */
        switch (*sig++) {
        case 'd': {/* double argument */
            double d = va_arg(vl, double);
            lua_pushnumber(L, d);
            break;
        }
        case 'i': {/* int argument */
            int i = va_arg(vl, int);
            lua_pushnumber(L, i);
            break;
        }
        case 's': {/* string argument */
            char* c = va_arg(vl, char*);
            lua_pushstring(L, c);
            break;
        }
        case '>':
            goto endwhile;
        default:
            error(L, "invalid option (%c)", *(sig - 1));
        }
        narg++;
        luaL_checkstack(L, 1, "too many arguments");
    }

endwhile:
    /* do the call */
    int nres = strlen(sig); /* number of results */
    /* 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);
}


int callwk(const char *filename, const char* func) {
    lua_State *L = luaL_newstate();
    luaopen_base(L);
    luaopen_table(L);
    luaopen_package(L);
    luaopen_io(L);
    luaopen_string(L);
    luaopen_math(L);
    luaL_openlibs(L); // open all the libs above
    if (luaL_dofile(L, filename))
        error(L, "luaL_dofile: %s
", lua_tostring(L, -1));
    callLuaf(L, func, "");
    lua_close(L);

    //double x = 3.0;
    //double y = 2.0;
    //double z = 0;
    //callLuaf(L, func, "dd>d", x, y, &z);//call func(L, x, y);
    //printf ("In %s, %s(%f, %f) = %f
", filename, func, x, y, z);
    return 0;
} 

第29章 在Lua中调用C语言

package.cpath = '/home/yoyu/hub/testcode/lua/api/?.so;'    --搜索so模块
local lib = require "libtest"
print("call c lib: lib.lsum(23,17) = "..lib.lsum(23,17))
local files = lib.ldir(".") -- Get file list in current dir 
//libtest.c
int sum(lua_State *L){
    double d1 = luaL_checknumber(L, 1);
    double d2 = luaL_checknumber(L, 2);
    lua_pushnumber(L, d1+d2);
    return 1;
}

int dir (lua_State *L) {
    DIR *dir;
    struct dirent *entry;
    int i;
    const char *path = luaL_checkstring(L, 1); 
    /* open directory */
    dir = opendir(path);
    if (dir == NULL) {
        lua_pushnil(L);
        /* error opening the directory? */
        /* return nil and ... */
        lua_pushstring(L, strerror(errno)); /* error message */
        return 2; /* number of results */
    }   
    /* create result table */
    lua_newtable(L);
    i = 1;
    while ((entry = readdir(dir)) != NULL) {
        lua_pushnumber(L, i++); /* push key */
        lua_pushstring(L, entry->d_name); /* push value */
    //printf("entry->d_name : %s
", entry->d_name);
        lua_settable(L, -3);
    }   
    closedir(dir);
    return 1;
    /* table is already on top */
}

//Name of array mylib can be changed.
static const struct luaL_Reg mylib[] = {
    {"lsum", sum},
    {"ldir", dir},
    {NULL, NULL}
};

//Function name can not be changed.
int luaopen_libtest(lua_State *L){ 
    luaL_newlib(L, mylib);
    return 1;
}

  

#Makefile
CFLAG1=-g -Wall -llua -shared -fPIC           #build lib
CFLAG2=-g -Wall -llua -lm -ldl -ltest -fPIC   #use lib in c

    Lua 语言中的每个协程都有自己的软栈( soft stack ), 对于 C 函数的调用 ,解释器必须使用 C 语言栈。因此, Lua 中的协程不能挂起 C 函数的执行:如果一个 C 函数位于从 resume 到对应yield 的调用路径中,那么 Lua 无法保存 C 函数的状态以便于在下次 resume 时恢复状态。因此,Lua 5.1 不能将其挂起。

在 Lua 5.2 及后续版本中,用延续(continuation) 改善了对这个问题的处理。
--test.lua:pcall 是一个 C 语言函数
co = coroutine.wrap(function() print(pcall(coroutine.yield)) end)
co()
--~/hub/lua/lua-5.1.4/src/lua testlua/test.lua ---> false attempt to yield across metamethod/C-call boundary
--~/hub/lua/lua-5.3.6/src/lua testlua/test.lua ---> OK

 第30章 编写C函数的技巧

    当 C 函数接收到一个 Lua 字符串为参数时,必须遵守两条规则: 在使用字符串期 间不能从栈中将其弹出,而且不应该修改字符串 。当 C 函数需要创建一个返回给 Lua 的字符串时,要求则更高。需要 C 语言代码负责缓冲区的分配/释放、缓冲区溢出。因此,Lua API提供了一些函数来帮助完成这些任务。
//test.lua
local s = "hi,,there"
print("will split:"..s)
local split_result = lib.lsplit(s, "i")
printAll(split_result, "split_result", 2, "hh ")
//libtest.c
int split (lua_State *L) {
    const char *s = luaL_checkstring(L, 1); 
    const char *sep = luaL_checkstring(L, 2); 
    const char *e; 
    int i = 1;
    lua_newtable(L); /* result */
    /* repeat for each separator */
    while ((e = strchr(s, *sep)) != NULL) {
        lua_pushlstring(L, s, e - s); /* push substring */
        lua_rawseti(L, -2, i++);      /* insert to table */
        s = e + 1;                    /* skip separator */
    }   
    /* push last substring */
    lua_pushstring(L, s); 
    lua_rawseti(L, -2, i); 
    return 1; /* return the table */
}

     lua_concat(L, n) 会连接(并弹出)栈最顶端的 n 个值,并将结果压人栈。

    const char *lua_pushfstring (lua_State *L, const char* fmt, ...); 类似于 C 函数sprintf。然而不同的是, lua_pushfstring 会动态地为我们创建缓冲区 ,将结果字符串压入栈中并返回一个指向它的指针。
    当只需连接几个字符串时, lua_concat 和 lua_pushfstring 都很有用。 不过,如果需要连接很多字符串(或字符),那么逐个连接就会非常低效。 此时,我们可以使用由辅助库提供的缓冲机制(buffer facility )。
    如果已知返回结果大小的上限值,使用下面的函数,参考lstrlib.c:str_upper。
LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz);

    如果不知道返回结果大小的上限值,使用下面的函数:

LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B);
LUALIB_API void luaL_addvalue (luaL_Buffer *B);
LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);
LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s);
#define luaL_addchar(B,c) 
  ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), (*(B)->p++ = (char)(c)))
LUALIB_API void luaL_pushresult (luaL_Buffer *B);

    示例:函数 table.concat 一个简化的实现

static int tconcat(lua_State *L)
{
    luaL_Buffer b;
    int i, n;
    luaL_checktype(L, 1, LUA_TTABLE);
    n = luaL_len(L, 1);
    luaL_buffinit(L, &b);
    for (i = 1; i <= n; i++) {
        lua_geti(L, 1, i);      // Get string from table
        luaL_addvalue(&b);      // Put string in buff
    }
    luaL_pushresult(&b);
    char* s = lua_tostring(L, -1);
    printf("in c   : s = %s
", s);
    //lua_remove(L, -1);
    return 1;
}

 注册表是一张只能被 C 代码访问的全局表 。 通常情况下,我们使用注册表来存储多个模块间共享的数据 。

static int set_regref(lua_State *L)
{
    int key1 = luaL_ref(L, LUA_REGISTRYINDEX);
    char data_of_key1[] = "char data of key1";
    lua_pushlightuserdata(L, (void *)&key1);  /* push address */
    lua_pushstring(L,  data_of_key1);         /* push value */
    lua_settable(L, LUA_REGISTRYINDEX);       /* registry[&key1] =  data_of_key1*/
    printf("key1 = %d set registry[&%p] = %s
", key1, &key1, data_of_key1);

    int key2 = luaL_ref(L, LUA_REGISTRYINDEX);
    int number_of_key2 = key2 * 1000;
    lua_pushlightuserdata(L, (void *)&key2);  /* push address */
    lua_pushnumber(L, number_of_key2);        /* push value */
    lua_settable(L, LUA_REGISTRYINDEX);       /* registry[&key2] = number_of_key2 */
    printf("key2 = %d set registry[&%p] = %d
", key2, &key2, number_of_key2);

    lua_rawgeti(L, LUA_REGISTRYINDEX, key1);  /* why needed? */
    lua_rawgeti(L, LUA_REGISTRYINDEX, key2);  /* why needed? */
    lua_pushnumber(L, (long)(&key1));        /* push address */
    lua_pushnumber(L, (long)(&key2));        /* push address */
    printf("key1 = %p
", (void *)lua_tointeger(L, -2));
    printf("key2 = %p
", (void *)lua_tointeger(L, -1));

    return 2; /* return the keys */
}
int get_regref(lua_State *L)
{
    long key1_addr = luaL_checknumber(L, 1);
    long key2_addr = luaL_checknumber(L, 2);
    /* retrieve a string*/
    lua_pushlightuserdata(L, (void*)key1_addr); /* push address. */
    lua_gettable(L, LUA_REGISTRYINDEX);        /* retrieve value */
    const char *key1_data = lua_tostring(L, -1); /* convert to string*/
    printf("get registry[&%p] = %s
", (void*)key1_addr, key1_data);
    /* retrieve a number */
    lua_pushlightuserdata(L, (void*)key2_addr); /* push address */
    lua_gettable(L, LUA_REGISTRYINDEX);       /* retrieve value */
    int key2_data = lua_tonumber(L, -1);        /* convert to number */
    printf("get registry[&%p] = %d
", (void*)key2_addr, key2_data);

    luaL_unref(L, LUA_REGISTRYINDEX, (int)key1_addr);
    luaL_unref(L, LUA_REGISTRYINDEX, (int)key2_addr);
    printf("return
");
    return 0; /* return the table */
}

  

function test_reg()
    local key1, key2 = lib.lset_regref()
    print(string.format("%x", key1))
    print(string.format("%x", key2))
    lib.lget_regref(key1, key2)
end

     上值( upvalue ) 实现了 一种类似于 C 语言静态变量 (只在特定的函数中可见)的机制。C 函数与其上值 的关联称为闭包 ( closure )。

static int counter(lua_State *L)
{
    int val = lua_tointeger(L, lua_upvalueindex(1));
    lua_pushinteger(L, ++val); //新值
    lua_copy(L, -1, lua_upvalueindex(1)); //用新值更新upvalue
    return 1; //返回新值
}

int newCounter(lua_State *L)
{
    lua_pushinteger(L, 1); //压入upvalue,初始值为1
    lua_pushcclosure(L, &counter, 1); //创建闭包
    return 1; //返回闭包
}
function test_counter()
    local c1 = lib.lnewCounter()
    local c2 = lib.lnewCounter()
    print(c1(), c2(), c1(), c2(), c1(), c2())
end

    元组是一种具有匿名字段的常量结构,我们可以用一个数值索引来获取某个特定的字段,或者一次性地获取所

有字段 。 在我们的实现中,将元组表示为函数,元组的值存储在函数 的上值中 。

int t_tupple(lua_State *L)
{
    lua_Integer op = luaL_optinteger(L, 1, 0); //获取1个参数值,默认0
    if (op == 0) { //没有参数
        int i;
         /*将每一个有效的upvalue压栈*/
        for (i = 1; !lua_isnone(L, lua_upvalueindex(i)); ++i) {
            lua_pushvalue(L, lua_upvalueindex(i));
        }
        return i - 1; //值的个数
    } else {
        luaL_argcheck(L, 0 < op && op <= 256, 1, "index out of range");
        if (lua_isnone(L, lua_upvalueindex(op))) {
            return 0; //字段不存在
        }
        lua_pushvalue(L, lua_upvalueindex(op));
        return 1;
    }
}

int t_new(lua_State *L)
{
    int top = lua_gettop(L);
    luaL_argcheck(L, top < 256, top, "too many fields");
    lua_pushcclosure(L, t_tupple, top);
    return 1;
}
function tupple_test()
    x = lib.lnew(10, "hi tupple", {}, 3)
    print(x(1))
    print(x(2))
    print(x())
    print(x(256))
end

 第31章 C语言中的用户自定义类型

    用户数据(userdata),元表,数组访问

#include "libtest.h"

int newarray (lua_State *L) {
    int n = luaL_checkinteger(L, 1);
    size_t nbytes = sizeof(NumArray) + (n - 1)*sizeof(double);
    //把这块分配的内存压入栈,并把地址存入 a。
    NumArray *a = (NumArray *)lua_newuserdata(L, nbytes);
    a->size = n;
    luaL_getmetatable(L, "LuaBook.array");
    lua_setmetatable(L, -2);
    return 1; // new userdatum is already on the stack
}

//判断第一个参数的元表是否为 registry["LuaBook.array"]
#define checkarray(L) (NumArray *)luaL_checkudata(L, 1, "LuaBook.array")

static int getindex (lua_State *L) {
    NumArray *a = checkarray(L);
    int index = luaL_checkinteger(L, 2) - 1;
    luaL_argcheck(L, 0 <= index && index < a->size, 2,"index out of range");
    return index; /* return element address */
}

int getsize (lua_State *L) {
   NumArray *a = checkarray(L);
   lua_pushnumber(L, a->size);
   return 1;
}

static double *getelem (lua_State *L) {
    NumArray *a = checkarray(L);
    int index = getindex(L);
    return &a->values[index];    // return element address.
}

int setarray (lua_State *L) {
    double newvalue = luaL_checknumber(L, 3);
    *getelem(L) = newvalue;
    return 0;
}

int getarray (lua_State *L) {
    double ret = *getelem(L);
    lua_pushnumber(L, ret);
    return 1;
}

int array2string (lua_State *L) {
    NumArray *a = checkarray(L);
    lua_pushfstring(L, "array(%d)", a->size);
    return 1;
}

//Name of array mylib can be changed.
static const struct luaL_Reg thelib[] = {
    {"new", newarray},
    {"set", setarray},
    {"get", getarray},
    {"size", getsize},
    {NULL, NULL}
};

static const struct luaL_Reg arraylib_m[] =
{
    {"__newindex", setarray},
    {"__index", getarray},
    {"__len", getsize},
    {"__tostring", array2string},
    {NULL,NULL}
};

//Function name can not be changed.
int luaopen_libtest(lua_State *L){ 
    luaL_newmetatable(L, "LuaBook.array"); // 创建元表 mt
    luaL_setfuncs(L, arraylib_m, 0);
    luaL_newlib(L, thelib);
    return 1;
}
asize = 1000
a = lib.new(asize)
print(#a)
for i = 1, asize do
    a[i] = 1 / i --a.set(a, i, 1/i) -- or a:set(i, 1/i)
end
print(a[10])

     轻量用户数据( light userdata )代表指针,用其代替 userdata 会使开销少很多,内存必须自己管理,因为它不会被垃圾回收。

 
#include "libtest.h"
static int dir_iter(lua_State *L);

static int dir_gc(lua_State *L)
{
    DIR *dir = *(DIR**)lua_touserdata(L, 1);
    if (dir) {
        closedir(dir);
    }
    return 0;
}

int dirV2 (lua_State *L) {
    const char *path = luaL_checkstring(L, 1);
    DIR **dir = (DIR**)lua_newuserdata(L, sizeof(DIR*));
    *dir = NULL; // 预先初始化
    luaL_getmetatable(L, "LuaBook.dir");
    lua_setmetatable(L, -2);
    *dir = opendir(path);
    //创建并返回迭代函数;该函数唯一的upvalue,即代表目录的用户数据位于栈顶
    lua_pushcclosure(L, dir_iter, 1);
    return 1;
}

static int dir_iter(lua_State *L)
{
    DIR *dir = *(DIR**)lua_touserdata(L, lua_upvalueindex(1));
    struct dirent *entry = readdir(dir);
    if (entry) {
        lua_pushstring(L, entry->d_name);
        return 1;
    } else {
        return 0; //遍历完成
    }
}

//Name of array mylib can be changed.
static const struct luaL_Reg thelib[] = {
    {"ldir", dirV2},//dir},
    {NULL, NULL}
};

//Function name can not be changed.
int luaopen_libtest(lua_State *L){ 
    luaL_newmetatable(L, "LuaBook.dir");
    lua_pushcfunction(L, dir_gc);
    lua_setfield(L, -2, "__gc");
    luaL_newlib(L, thelib);
    return 1;
}
for fname in lib.ldir(".") do
    print(fname)
end

     

原文地址:https://www.cnblogs.com/yyqng/p/14461269.html