使用Lua作為配置文件真的是酷死,比什么XML,INI爽多了。用戶可以有更多的控制,解析也更輕松,又安全。缺點我是沒有發現啦,發現的朋友告知一聲哦!
C獲取Lua中的一般全局變量(boolean, number, string)
下面以字符串為例。
Lua 文件 config.lua
app_name = "Test"
app_author = "Gotaly"
解析配置文件的C文件 config.c (這里不作出錯處理,聚焦於Lua的C API和其邏輯的運用)
#include <stdio.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
int main(int argc,char *argv[])
{
lua_State *lua = luaL_newstate();
/**
* luaL_openlibs(lua);
*/
luaL_loadfile(lua,"config.lua");
lua_pcall(lua,0,0,0);
lua_getglobal(lua,"app_name");
lua_getglobal(lua,"app_author");
printf("application name is %s,author is %s \n",lua_tostring(lua,-2),lua_tostring(lua,-1));
return 0;
}
這個例子和《programming in lua》中的很類似(其實同樣功能的代碼都類似,呵呵),這里解釋下各 API的作用。
1) lua_State *lua = luaL_newstate();
這個在你的C程序中創建一個Lua環境,之后就可以通過該句柄和Lua進行通信了。
2) luaL_openlibs(lua);
打開Lua中要使用的標准庫,具體到我們這個例子可以不使用。
3) luaL_loadfile(lua,"config.lua");lua_pcall(lua,0,0,0);
加載Lua程序並執行,這里要做出錯處理。例子中為了突出邏輯,沒有做相應處理。同樣除了loadfile,還有loadbuffer,loadstring等加載Lua程序的接口,可以自行查閱Manual
4)重點來了lua_getglobal(lua,"app_name");
這里會從句柄lua表示的環境中獲取全局變量"app_name",這里說是全局變量,但是它只是相對於該句柄所表示的餓環境中的全局變量。 調用了該接口后,相應的變量的值就被壓入到Lua和C交流的棧空間了。后面將其出棧就可以在C程序中得到該值。
5)lua_tostring(lua,-2)
這里就根據之前入棧的順序,將句柄lua和C交互中的棧空間里相應的值出棧。lua_toxxxx是一個系列函數,具體可以參考Lua的Manual,這里主要將其內容轉換成字符串。
C獲取Lua中的Table
講完了獲取一般的變量,這里切入正題,獲取Table內容。
Lua 文件 config.lua
app_content = {
name = "app_Test",
author = "Gota"
}
解析配置文件的C文件 config.c (
#include <stdio.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
int main(int argc,char *argv[])
{
lua_State *lua = luaL_newstate();
/**
* luaL_openlibs(lua);
*/
luaL_loadfile(lua,"config.lua");
lua_pcall(lua,0,0,0);
lua_getglobal(lua,"app_content");
lua_getfield(lua,-1,"name");
lua_getfield(lua,-2,"author");
printf("application name is %s,author is %s \n",lua_tostring(lua,-2),lua_tostring(lua,-1));
return 0;
}
這里對上面的代碼進行說明。主要就是多了一個API的使用
6)lua_getfield(lua,-1,"name"); 該API主要是用來處理Table。其第一個參數是交互的句柄,第二個參數是Table在交互的棧的位置,第三個參數是前面Table中的鍵。 該函數的結果是將該Table中對於鍵的值取出來,並壓入到交互棧中。這樣就使得原來位於(-1)位置的Table就下壓了一個位置到了 (-2)。
再說明下上述程序的整個執行過程,首先弓getglobal得到一個全局變量app_content,然后將他壓入到交互棧中,位於(-1)位置。 然后調用lua_getfield(lua,-1,"name");,將(-1)位置的變量作Table解析,並取出其中的“name”鍵的值壓入交互棧,位於(-1)位置, 原來的Table被下壓一層至(-2)。然后調用lua_getfield(lua,-2,"author");對(-2)位置的Table進行取值,取出其鍵為“author”的 值並壓入到交互棧位置(-1),這樣原來的“name”就被壓入到(-2)位置。最后用tostring,將棧中的數據取出來。
這里我省掉了2個API。一個是lua_isxxx系列,這個系列中的lua_istable( lua_State *lua,int index),可以檢查index位置的變量是否 為Table。上面我們主要就是對table作測試就沒有進行驗證了。
第二個是lua_pop(lua_State *lua,int num),從交互句柄的交互棧中彈出幾個值,這個函數在下面的例子中將會用到。
C獲取Lua中的嵌套Table
Lua 文件 config.lua
app_content = {
name = "app_Test",
author = "Gota"
}
app_config.content = app_content
解析配置文件的C文件 config.c
#include <stdio.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
int main(int argc,char *argv[])
{
lua_State *lua = luaL_newstate();
/**
* luaL_openlibs(lua);
*/
luaL_loadfile(lua,"config.lua");
lua_pcall(lua,0,0,0);
lua_getglobal(lua,"app_content");
lua_pushnil(lua);
while(lua_next(lua,-2)){
printf("get key %s\n is table %d\n",lua_tostring(lua,-2),lua_istable(lua,-1));
lua_getfield(lua,-1,"name");
printf("application name is %s \n",lua_tostring(lua,-2));
lua_pop(lua,1);
}
return 0;
}
對於嵌套解析Table,大家看以看過來,作者對其機制解釋的特別清楚。這里我只說下我 看了文章后的學到的內容。
7)lua_pushnil(lua_State *lua);
看過《programming in lua》都知道lua_pushxxx系列函數,可以在C中將一些值壓入交互棧。pushnil就是向棧中壓入Lua的nil,nil在Lua中是一個類型值,在C中,大家可以 將其視為NULL,並且像lua_tostring這樣的從棧中未取到值,也就是Lua中的nil,得到的結果就是NULL。
8) lua_next(lua_State *lua,int index)
lua_next(lua_State *lua,int index)函數是這個例子的主角,他可以根據指定交互棧中index處的Table,進行遍歷,每次取(-1)位置的一個key作為前輩,即將要取得一對元素的上一對元素的key,然后返回Table的該 對元素,將其鍵先壓入棧,再將該鍵對應的值壓入棧,結果就是(-2)位置放的是鍵,(-1)位置放的是值。Table自然被壓入到其后,本例中的(-3)位置。如果key為nil,則默認為首對數據, 會隨機的壓入一對值。當所有值都被遍歷一遍后,next返回0。
9)lua_pop(lua_State *lua,int num)
該函數如上面所述:從交互句柄的交互棧中彈出num個值。這里不得不說下Lua作配置文件的另一個好處-Lua自己處理堆棧,使得配置文件程序更安全。所有壓入棧中的內容,只要 調用該函數,Lua就是自己對其內存進行處理,無需程序員得干預,當然,這樣也說明了,不可以帶走棧中的內存,也就是不可以將棧中彈出來的內存如字符串內存用作他用,否則可能 在pop后,該內存將失效。
這里將整個程序邏輯說一遍
首先lua_getglobal(lua,"app_content");將配置文件中app_content變量壓入交互棧(-1)位置,然后lua_pushnil(lua);將nil壓入交互棧(-1)位置以供后續的lua_next作首個元素使用。 app_content被壓入下一層(-2),接着進while循環,用lua_next進行迭代,初次遇到nil,會隨機的取得一對鍵值,然后將鍵和值壓入交互棧,得到的結果就是:(-2)位置為key,(-1) 位置為value.Table自然被壓入(-3)位置。在處理完value(如printf)后,用pop將value彈出,留下key用作下一對值的前輩,這時,(-1)位置為當前key,(-2)位置為Table,再次進 while循環的next函數,去處理下一對鍵值。最后當所有鍵值對都處理完全后,next返回0,退出while循環。
總結
變量Table的一個總的結構就是
lua_getglobal(L, table_name);
lua_pushnil(L);
while (lua_next(L, -2)) {
/* 處理相應數據。此時棧上 -1 處為 value, -2 處為 key */
lua_pop(L, 1);
}
當然,這里的索引是根據需要進行變更的。