• Lua和C语言的交互——C API


      Lua可作为扩展性语言(Lua可以作为程序库用来扩展应用的功能),同时也是个可扩展的语言(Lua程序中可以注册由其他语言实现的函数)。

      C和Lua交互的部分称为C API。C API是一个C代码与Lua进行交互的函数集。他由以下部分组成:读写Lua全局变量的函数、调用Lua函数的函数、运行Lua代码片断的函数、注册C函数然后可以在Lua中被调用的函数,等等。

      API中有些函数为了方便以宏的方式实现。

      当在Lua和C之间交换数据时我们面临着两个问题:动态与静态类型系统的不匹配和自动与手动内存管理的不一致。解决办法是在C和Lua之间通信关键在于一个虚拟的栈。几乎所有的API调用都是对栈上的值进行操作,所有C与Lua之间的数据交换也都通过这个栈来完成。因为栈是由Lua来管理的,垃圾回收器知道那个值正在被C使用。

      Lua以一个严格的LIFO规则(后进先出;也就是说,始终存取栈顶)来操作栈:
      (1)当你调用Lua时,它只会改变栈顶部分。
      (2)你的C代码却有更多的自由;更明确的来讲,你可以查询栈上的任何元素,甚至是在任何一个位置插入和删除元素。

      在调用C API时有几个重要的头文件:

      (1)lua.h:Lua基础函数库,lua_前缀
      (2)lauxlib.h:辅助库,luaL_前缀,利用lua.h实现的更高层的抽象
      (3)lualib.h:为了保持Lua的苗条,所有的标准库以单独的包提供,所以如果你不需要就不会强求你使用它们。头文件lualib.h定义了打开这些库的函数。例如,调用luaopen_io,以创建io table并注册I/O函数(io.read,io.write等等)到Lua环境中。

      API用索引来访问栈中的元素。栈底为1,依次往上增加,也可用负数索引,-1表示栈顶元素。下面列出一些常用的C API函数:

    lua_push*:压入栈元素
      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);

    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);

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

      size_t lua_strlen (lua_State *L, int index):返回字符串的实际长度。

      int lua_checkstack(lua_State *L, int sz):检查栈空间。默认有20个空闲的记录,lua.h中的LUA_MINSTACK宏定义了这个常量。

      int lua_is... (lua_State *L, int index):检查一个元素能否被转换成指定的类型。

      int lua_type (lua_State *L, int idx):返回栈中元素的类型;

      const char* lua_typename(lua_State *L, int tp):返回type对应的名字字符串,第二个参数为lua_type返回的类型

      void luaL_checktype (lua_State *L, int arg, int t):返回参数arg是否是类型t,第三个参数为lua_type的取值。

      在lua.h头文件中,每种类型都被定义为一个常量:LUA_TNIL、LUA_TBOOLEAN、LUA_TNUMBER、LUA_TSTRING、LUA_TTABLE、LUA_TFUNCTION、LUA_TUSERDATA以及LUA_TTHREAD。

      int lua_gettop (lua_State *L):返回栈中元素个数,它也是栈顶元素的索引。

      void lua_settop (lua_State *L, int index):设置栈顶元素的索引,相当于设置栈的大小。如果开始的栈顶高于新的栈顶,顶部的值被丢弃。否则,为了得到指定的大小这个函数压入相应个数的空值(nil)到栈上。lua_settop(L,0):清空堆栈。

      #define lua_pop(L,n) lua_settop(L, -(n)-1):宏定义,弹出n个元素。

      void lua_pushvalue (lua_State *L, int index):压入堆栈上指定索引的一个抟贝到栈顶,等于拷贝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):从栈顶弹出元素值并将其设置到指定索引位置,没有任何移动操作。

      下面来看一段Lua和C++简单交互的代码:

    #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 */
        luaL_openlibs(L);
    
        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;
    }

      上述代码在VS2010中正常运行,Lua使用5.1的版本,其中有几点需要注意的:

      (1)lua5.0之前初始化库的用法如下:

      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. */

      但5.0之后只需要一句话即可:

    luaL_openlibs(L);

      (2)需要添加依赖库:lua5.1.lib lua51.lib。

      下面这个例子可以有助于加深对C和Lua之间堆栈的理解:

    // 从栈底到栈顶依次遍历整个堆栈
    static void stackDump(lua_State* L)
    {
        int i;
        int top = lua_gettop(L);
        for(i = 1; i <= top; ++i)
        {
            int t = lua_type(L, i);
            switch(t)
            {
            case LUA_TSTRING:
                printf("'%s'", lua_tostring(L, i));
                break;
            case LUA_TBOOLEAN:
                printf(lua_toboolean(L, i) ? "true": "false");
                break;
            case LUA_TNUMBER:
                printf("'%g'", lua_tonumber(L, i));
                break;
            default:
                printf("'%s'", lua_typename(L, t));
                break;
            }
            printf("    ");
        }
        printf("
    ");
    }
    
    int main(void)
    {
        lua_State* L = lua_open();
        luaL_openlibs(L);
    
        lua_pushboolean(L, 1);
        lua_pushnumber(L, 10);
        lua_pushnil(L);
        lua_pushstring(L, "hello");
        stackDump(L);
    
        lua_pushvalue(L, -4);
        stackDump(L);
    
        lua_replace(L, 3);
        stackDump(L);
    
        lua_settop(L, 6);
        stackDump(L);
    
        lua_remove(L, -3);
        stackDump(L);
    
        lua_settop(L, -5);
        stackDump(L);
    
        return 0;
    }

      注意lua_replace首先会弹出栈顶元素,并且需要注意的是lua_replace(L, -1);语句会导致站顶元素弹出,其他元素不变。

       作为配置语言是LUA的一个重要应用,下面看一个简单的这方面的例子。有一个含有简单字段记录的lua文件如下:

    width = 200
    height = 300

      对该lua文件的解析大妈如下:

    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    extern "C" 
    {  
        #include <lua.h>  
        #include <lauxlib.h>  
        #include <lualib.h>  
    } 
    
    void error(lua_State* L, const char* fmt, ...)
    {
        va_list argp;
        va_start(argp, fmt);
        vfprintf(stderr, fmt, argp);
        va_end(argp);
        lua_close(L);
        exit(EXIT_FAILURE);
    }
    
    void load(char* filename, int* width, int* height)
    {
        lua_State* L = lua_open();
        luaL_openlibs(L);
    
        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, "width");
        lua_getglobal(L, "height");
    
        if(!lua_isnumber(L, -2))
            error(L, "'width' should be a number
    ");
        
        if(!lua_isnumber(L, -1))
            error(L, "'height' should be a number
    ");
    
        *width = (int)lua_tonumber(L, -2);
        *height = (int)lua_tonumber(L, -1);
    
        lua_close(L);
    }
    
    int main(void)
    {
        int width, height;
        load("c:\luatest\cfg.lua", &width, &height);
        printf("width = %d height = %d 
    ", width, height);
        return 0;
    }
  • 相关阅读:
    OnboardSDK分析
    Ubuntu14.04安装pycharm用于Python开发环境部署,并且支持pycharm使用中文输入
    TCP/IP四层模型和OSI七层模型的概念
    二分查找
    Combination Sum
    全面解析回溯法:算法框架与问题求解
    Search Insert Position
    过滤器
    Java中Web页面信息获取
    jQuery和Ajax联动
  • 原文地址:https://www.cnblogs.com/sifenkesi/p/3871992.html
Copyright © 2020-2023  润新知