zoukankan      html  css  js  c++  java
  • Lua的C API

    Lua可以与C很好地互通。主要是通过栈来通信。

    引入Lua提供的C API

    lauxlib.h   lua.h   lua.hpp   luaconf.h   lualib.h   

    以下实现一个Lua的简单的解释器

    #include <stdio.h>
    #include <string.h>
    extern "C" {
    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
    }
    int main (void)
    {
        char buff[256];
        int error;
        lua_State *L = lua_open();  /* opens Lua */
        if (NULL == L)
        {
            printf("can not open lua_State");
        }
        
        luaopen_base(L);         /* opens the basic library */
        
        luaopen_table(L);        /* opens the table library */
        
        //luaopen_io(L);           /* opens the I/O library */  
        int x = lua_cpcall(L, luaopen_io, NULL);  //luaopen_io在5.1版本被删除了,这里用lua_cpcall
    
        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环境中。

    创建一个state并将标准库载入之后,就可以着手解释用户的输入了。对于用户输入的每一行,C程序首先调用luaL_loadbuffer编译这些Lua代码。如果没有错误,这个调用返回零并把编译之后的chunk压入栈。之后,C程序调用lua_pcall,它将会把chunk从栈中弹出并在保护模式下运行它。和luaL_laodbuffer一样,lua_pcall在没有错误的情况下返回零。在有错误的情况下,这两个函数都将一条错误消息压入栈;我们可以用lua_tostring来得到这条信息、输出它,用lua_pop将它从栈中删除。

    注意luaopen_io函数在5.1的版本中被删除了,有以下三种解决方法

    1. lua_cpcall(L, luaopen_io, NULL);

     2. lua_pushcfunction(L, luaopen_io);
         lua_pushstring(L, LUA_IOLIBNAME);
         lua_pcall(L, 1, 0, 0);      

     3. lua_pushcfunction(L, luaopen_io);
         lua_pushstring(L, LUA_IOLIBNAME);
         lua_call(L, 1, 0);

    lua_call函数没有返回值,如果函数运行出错程序会崩溃,所以应该用前两种情况。

    lua_cpcall只能用来调用lua_CFunction类型的C函数,而lua_pcall和lua_call可以调用Lua函数也可以调用lua_CFunction类型的C函数

    堆栈:

    Lua与C进行通信时采用堆栈作为中转站(由于篇幅问题就不赘述使用堆栈的原因了,http://book.luaer.cn/ 24.2 堆栈一章有详细的解释)

    无论C要调用Lua的函数还是Lua要调用C函数,都是把数据先压入栈,然后再从栈顶取出数据

    正如上面的例子,先由lua_loadBuffer运行一个chunk,并把结果压入栈(被编译了的chunk或一条错误信息), lua_pcall从栈中获取这个chunk并把一些临时的错误信息压入栈

    Lua的栈严格先进后出,在Lua中只能对栈顶进行操作。但在C中,可以对栈中的任何位置操作,甚至可以在栈中任一位置插入和删除元素

    压入元素:

    API有一系列压栈的函数,它将每种可以用C来描述的Lua类型压栈:空值(nil)用lua_pushnil,数值型(double)用lua_pushnumber,布尔型(在C中用整数表示)用lua_pushboolean,任意的字符串(char*类型,允许包含''字符)用lua_pushlstringC语言风格(以''结束)的字符串(const char*)用lua_pushstring

    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);
    int lua_checkstack (lua_State *L, int sz); // 用于扩大栈空间
    int lua_gettop(Lua_State *L) //获取栈顶的序号,同时也相当于返回栈内元素的个数

    Lua从来不保持一个指向外部字符串(或任何其它对象,除了C函数——它总是静态指针)的指针。对于它保持的所有字符串,Lua要么做一份内部的拷贝要么重新利用已经存在的字符串。因此,一旦这些函数返回之后你可以自由的修改或是释放你的缓冲区。

    Lua默认的栈空间为20,因为Lua不会自己去检测栈空间是否足够,所以我们需要确保不会栈溢出(一般情况下只要不是不断向栈中压入数据都不会栈溢出),lua_checkstack可以用于扩大栈空间。

    Lua的栈起始序号是1,所以lua_gettop函数返回栈顶的序号相当于栈内的元素个数。

    查询元素:

    API用索引来访问栈中的元素。在栈中的第一个元素(也就是第一个被压入栈的)有索引1,下一个有索引2,以此类推。我们也可以用栈顶作为参照来存取元素,利用负索引。在这种情况下,-1指出栈顶元素(也就是最后被压入的),-2指出它的前一个元素,以此类推。

    API提供了一套lua_is*函数来检查一个元素是否是一个指定的类型,*可以是任何Lua类型。因此有lua_isnumber,lua_isstring,lua_istable以及类似的函数。所有这些函数都有同样的原型:

    int lua_is... (lua_State *L, int index);

    实际上,这些函数都是调用了lua_type返回的变量类型,并且与宏类型常量比较(LUA_TNILLUA_TBOOLEANLUA_TNUMBERLUA_TSTRINGLUA_TTABLELUA_TFUNCTIONLUA_TUSERDATA以及LUA_TTHREAD)

    为了从栈中获得值,这里有lua_to*函数:

    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函数返回一个指向字符串的内部拷贝的指针。你不能修改它(使你想起那里有一个const)。只要这个指针对应的值还在栈内,Lua会保证这个指针一直有效。当一个C函数返回后,Lua会清理他的栈,所以,有一个原则:永远不要将指向Lua字符串的指针保存到访问他们的外部函数中。

    Lua_string返回的字符串结尾总会有一个字符结束标志0,但是字符串中间也可能包含0,lua_strlen返回字符串的实际长度。

    其他堆栈操作:

    int  lua_gettop (lua_State *L);

    void lua_settop (lua_State *L, int index);

    void lua_pushvalue (lua_State *L, int index);

    void lua_remove (lua_State *L, int index);

    void lua_insert (lua_State *L, int index);

    void lua_replace (lua_State *L, int index);

    settop可以用于设置栈顶,#define lua_pop(L,n)  lua_settop(L, -(n)-1)  这个宏可用于弹出n个元素。

    为了说明这些函数的用法,这里有一个有用的帮助函数,它dump整个堆栈的内容:

    static void stackDump (lua_State *L) {
        int i;
        int top = lua_gettop(L);
        for (i = 1; i <= top; i++) {  /* repeat for each level */
           int t = lua_type(L, i);
           switch (t) {
     
           case LUA_TSTRING:  /* strings */
               printf("`%s'", lua_tostring(L, i));
               break;
     
           case LUA_TBOOLEAN:  /* booleans */
               printf(lua_toboolean(L, i) ? "true" : "false");
               break;
     
           case LUA_TNUMBER:  /* numbers */
               printf("%g", lua_tonumber(L, i));
               break;
       
           default:  /* other values */
               printf("%s", lua_typename(L, t));
               break;
     
           }
           printf("  ");  /* put a separator */
        }
        printf("
    ");     /* end the listing */
    }

    这个函数从栈底到栈顶遍历了整个堆栈,依照每个元素自己的类型打印出其值。它用引号输出字符串;以%g的格式输出数字;对于其它值(table,函数,等等)它仅仅输出它们的类型(lua_typename转换一个类型码到类型名)。

    下面的函数利用stackDump更进一步的说明了API堆栈的操作。

    #include <stdio.h>
    #include <lua.h>
     
    static void stackDump (lua_State *L) {
        ...
    }
     
    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;
    }

     Lua调用C函数

    需要先把C函数注册到Lua中(相当于把C函数的地址通过栈传递到Lua中)。

    Lua调用C函数,会将参数放入栈中,C函数通过栈获取参数并返回结果。同时,C函数将会返回结果的个数。

    Lua调用C函数所用的栈是私有的,每个C函数都会有一个独立的栈

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

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

    假设现有这样一个函数

    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); // push function into stack
    
    lua_setglobal(l, "mysin"); // named as "mysin" , finish reg

    lua_pushcfunction会把l_sin压入栈,lua_setglobal会把l_sin重新编译并赋值给全局变量'mysin' ,  完成注册后即可调用。^o^

    采用前面提到的简单Lua解释器的C实现就可以轻松调用了。

    未完待续

  • 相关阅读:
    PO-审批设置
    DIS-接收方式设置入口
    网约车
    汽车租赁
    共享单车
    共享充电宝
    佛教四大名山|道教四大名山|五岳|名山
    我读过的诗词文章书籍
    我看过的电影
    redis异常解决:jedis.exceptions.JedisDataException: ERR Client sent AUTH, but no password is set
  • 原文地址:https://www.cnblogs.com/elenno/p/Lua_C.html
Copyright © 2011-2022 走看看