Lua和C語言的交互——C API

  Lua可做爲擴展性語言(Lua能夠做爲程序庫用來擴展應用的功能),同時也是個可擴展的語言(Lua程序中能夠註冊由其餘語言實現的函數)。函數

  C和Lua交互的部分稱爲C API。C API是一個C代碼與Lua進行交互的函數集。他由如下部分組成:讀寫Lua全局變量的函數、調用Lua函數的函數、運行Lua代碼片段的函數、註冊C函數而後能夠在Lua中被調用的函數,等等。lua

  API中有些函數爲了方便以宏的方式實現。spa

  當在Lua和C之間交換數據時咱們面臨着兩個問題:動態與靜態類型系統的不匹配和自動與手動內存管理的不一致。解決辦法是在C和Lua之間通訊關鍵在於一個虛擬的棧。幾乎全部的API調用都是對棧上的值進行操做,全部C與Lua之間的數據交換也都經過這個棧來完成。由於棧是由Lua來管理的,垃圾回收器知道那個值正在被C使用。指針

  Lua以一個嚴格的LIFO規則(後進先出;也就是說,始終存取棧頂)來操做棧:
  (1)當你調用Lua時,它只會改變棧頂部分。
  (2)你的C代碼卻有更多的自由;更明確的來說,你能夠查詢棧上的任何元素,甚至是在任何一個位置插入和刪除元素。code

  在調用C API時有幾個重要的頭文件:blog

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

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("\n");
}

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\n");
    
    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);
}

int main(void)
{
    int width, height;
    load("c:\\luatest\\cfg.lua", &width, &height);
    printf("width = %d height = %d \n", width, height);
    return 0;
}
相關文章
相關標籤/搜索