當在Lua和C之間交換數(shù)據(jù)時主要的問題是自動回收與手動回收內(nèi)存管理的不一致。因此,Lua 用一個抽象的棧在Lua與C之間交換值。
本文目錄:
1、c與lua的取值傳值
(1)取值(到c)
(2)傳值(到lua)
2、堆棧操作api
(1) 壓入元素
(2) 查詢元素(3) 其他堆棧操作3、堆棧操作實例
(1)一些堆棧操作和打印堆棧上所有變量的例子
(2)提供一個打印一張lua表的c的api函數(shù)4、項目例子
本文內(nèi)容:
1、c與lua的取值傳值
(1)取值
棧中的每一條記錄都可以保存任何Lua值。無論你何時想要從Lua請求一個值(比如一個全局變量的值),調(diào)用Lua,被請求的值將會被壓入棧。
Lua以一個嚴格的LIFO規(guī)則(后進先出;也就是說,始終存取棧頂)來操作棧。當你調(diào)用Lua時,它只會改變棧頂部分。你的C代碼卻有更多的自由;更明確的來講,你可以查詢棧上的任何元素,甚至是在任何一個位置插入和刪除元素。
(2)傳值
無論你何時想要傳遞一個值給Lua,首先將這個值壓入棧,然后調(diào)用Lua(這個值將被彈出)。我們?nèi)匀恍枰粋€不同的函數(shù)將每種C類型壓入棧和一個不同函數(shù)從棧上取值(只是取出不是彈出)。
因為棧是由Lua來管理的,垃圾回收器知道那個值正在被C使用。幾乎所有的API函數(shù)都用到了棧。例如,luaL_loadbuffer把它的結(jié)果留在了棧上(被編譯的chunk或一條錯誤信息);lua_pcall從棧上獲取要被調(diào)用的函數(shù)并把任何臨時的錯誤信息放在這里。
2、堆棧操作api
(1) 壓入元素API有一系列壓棧的函數(shù),它將每種可以用C來描述的Lua類型壓棧:空值(nil)用lua_pushnil,數(shù)值型(double)用lua_pushnumber,布爾型(在C中用整數(shù)表示)用lua_pushboolean,任意的字符串(char*類型,允許包含”字符)用lua_pushlstring,C語言風格(以”結(jié)束)的字符串(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);同樣也有將C函數(shù)和userdata值壓入棧的函數(shù),稍后會討論到它們。Lua中的字符串不是以零為結(jié)束符的;它們依賴于一個明確的長度,因此可以包含任意的二進制數(shù)據(jù)。將字符串壓入串的正式函數(shù)是lua_pushlstring,它要求一個明確的長度作為參數(shù)。對于以零結(jié)束的字符串,你可以用lua_pushstring(它用strlen來計算字符串長度)。Lua從來不保持一個指向外部字符串(或任何其它對象,除了C函數(shù)——它總是靜態(tài)指針)的指針。對于它保持的所有字符串,Lua要么做一份內(nèi)部的拷貝要么重新利用已經(jīng)存在的字符串。因此,一旦這些函數(shù)返回之后你可以自由的修改或是釋放你的緩沖區(qū)。無論你何時壓入一個元素到棧上,你有責任確保在棧上有空間來做這件事情。記住,你現(xiàn)在是C程序員;Lua不會寵著你。當Lua在起始以及在Lua調(diào)用C的時候,棧上至少有20個空閑的記錄(lua.h中的LUA_MINSTACK宏定義了這個常量)。對于多數(shù)普通的用法棧是足夠的,所以通常我們不必去考慮它。無論如何,有些任務(wù)或許需要更多的??臻g(如,調(diào)用一個不定參數(shù)數(shù)目的函數(shù))。在這種情況下,或許你需要調(diào)用下面這個函數(shù):int lua_checkstack (lua_State *L, int sz);它檢測棧上是否有足夠你需要的空間(稍后會有關(guān)于它更多的信息)。
(2) 查詢元素API用索引來訪問棧中的元素。在棧中的第一個元素(也就是第一個被壓入棧的)有索引1,下一個有索引2,以此類推。我們也可以用棧頂作為參照來存取元素,利用負索引。在這種情況下,-1指出棧頂元素(也就是最后被壓入的),-2指出它的前一個元素,以此類推。例如,調(diào)用lua_tostring(L, -1)以字符串的形式返回棧頂?shù)闹?。我們下面將看到,在某些場合使用正索引訪問棧比較方便,另外一些情況下,使用負索引訪問棧更方便。API提供了一套lua_is*函數(shù)來檢查一個元素是否是一個指定的類型,*可以是任何Lua類型。因此有l(wèi)ua_isnumber,lua_isstring,lua_istable以及類似的函數(shù)。所有這些函數(shù)都有同樣的原型:int lua_is… (lua_State *L, int index);lua_isnumber和lua_isstring函數(shù)不檢查這個值是否是指定的類型,而是看它是否能被轉(zhuǎn)換成指定的那種類型。例如,任何數(shù)字類型都滿足lua_isstring。還有一個lua_type函數(shù),它返回棧中元素的類型。(lua_is*中的有些函數(shù)實際上是用了這個函數(shù)定義的宏)在lua.h頭文件中,每種類型都被定義為一個常量:LUA_TNIL、LUA_TBOOLEAN、LUA_TNUMBER、LUA_TSTRING、LUA_TTABLE、LUA_TFUNCTION、LUA_TUSERDATA以及LUA_TTHREAD。這個函數(shù)主要被用在與一個switch語句聯(lián)合使用。當我們需要真正的檢查字符串和數(shù)字類型時它也是有用的為了從棧中獲得值,這里有l(wèi)ua_to*函數(shù):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);即使給定的元素的類型不正確,調(diào)用上面這些函數(shù)也沒有什么問題。在這種情況下,lua_toboolean、lua_tonumber和lua_strlen返回0,其他函數(shù)返回NULL。由于ANSI C沒有提供有效的可以用來判斷錯誤發(fā)生數(shù)字值,所以返回的0是沒有什么用處的。對于其他函數(shù)而言,我們一般不需要使用對應(yīng)的lua_is*函數(shù):我們只需要調(diào)用lua_is*,測試返回結(jié)果是否為NULL即可。Lua_tostring函數(shù)返回一個指向字符串的內(nèi)部拷貝的指針。你不能修改它(使你想起那里有一個const)。只要這個指針對應(yīng)的值還在棧內(nèi),Lua會保證這個指針一直有效。當一個C函數(shù)返回后,Lua會清理他的棧,所以,有一個原則:永遠不要將指向Lua字符串的指針保存到訪問他們的外部函數(shù)中(就算Lua_to* 和lua_is*沒有彈棧操作,也不該使用c指針指向lua字符串的地址)。
Lua_string返回的字符串結(jié)尾總會有一個字符結(jié)束標志0,但是字符串中間也可能包含0,lua_strlen返回字符串的實際長度。特殊情況下,假定棧頂?shù)闹凳且粋€字符串,下面的斷言(assert)總是有效的:const char *s = lua_tostring(L, -1);size_t l = lua_strlen(L, -1);assert(s[l] == ”);assert(strlen(s) <= l);
(3) 其他堆棧操作除開上面所提及的C與堆棧交換值的函數(shù)外,API也提供了下列函數(shù)來完成通常的堆棧維護工作:(3-1)int lua_gettop (lua_State *L);
函數(shù)lua_gettop返回堆棧中的元素個數(shù),它也是棧頂元素的索引。注意一個負數(shù)索引-x對應(yīng)于正數(shù)索引gettop-x+1。(3-2)void lua_settop (lua_State *L, int index);
lua_settop設(shè)置棧頂(也就是堆棧中的元素個數(shù))為一個指定的值。如果開始的棧頂高于新的棧頂,頂部的值被丟棄。否則,為了得到指定的大小這個函數(shù)壓入相應(yīng)個數(shù)的空值(nil)到棧上。特別的,lua_settop(L,0)清空堆棧。你也可以用負數(shù)索引作為調(diào)用lua_settop的參數(shù);那將會設(shè)置棧頂?shù)街付ǖ乃饕?。利用這種技巧,API提供了下面這個宏,它從堆棧中彈出n個元素:#define lua_pop(L,n) lua_settop(L, -(n)-1)(3-3)void lua_pushvalue (lua_State *L, int index);
函數(shù)lua_pushvalue壓入堆棧上指定索引的一個摶貝到棧頂;(3-4)void lua_remove (lua_State *L, int index);
lua_remove移除指定索引位置的元素,并將其上面所有的元素下移來填補這個位置的空白;(3-5)void lua_insert (lua_State *L, int index);
lua_insert移動棧頂元素到指定索引的位置,并將這個索引位置上面的元素全部上移至棧頂被移動留下的空隔;(3-6)void lua_replace (lua_State *L, int index);最后,lua_replace從棧頂彈出元素值并將其設(shè)置到指定索引位置,沒有任何移動操作。
(3-7)注意到下面的操作對堆棧沒有任何影響:lua_settop(L, -1);lua_insert(L, -1);
3、堆棧操作實例(1)一些堆棧操作和打印堆棧上所有變量的例子
//打印棧上的所有的變量
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();
//1 依次壓入幾個變量 true,10,nil,"hello"
printf("1:\n");
lua_pushboolean(L, 1);
lua_pushnumber(L, 10);
lua_pushnil(L);
lua_pushstring(L,"hello");
stackDump(L);//true 10 nil hello
//2
printf("2:\n");
lua_pushvalue(L, -4);
stackDump(L);//true 10 nil hello true
//3 用棧頂更換第三個
printf("3:\n");
lua_replace(L, 3);
stackDump(L);//true 10 true hello
//4 設(shè)置棧變量為6個
printf("4:\n");
lua_settop(L, 6);
stackDump(L);//true 10 true hello nil nil
//5 移除從棧頂往下數(shù)的第三個
printf("5:\n");
lua_remove(L, -3);
stackDump(L);//true 10 true nil nil
//6 設(shè)置-5位置的變量作為棧頂變量
printf("6:\n");
lua_settop(L, -5);
stackDump(L);//true
//7
lua_close(L);
return 0;
}
輸出結(jié)果(輸出是從棧底到棧頂):
1:
true 10 nil hello
2:
true 10 nil hello true
3:
true 10 true hello
4:
true 10 true hello nil nil
5:
true 10 true nil nil
6:
true
(2)提供一個打印一張表的c的api函數(shù)
void tableDump (lua_State *L,int index)
{
if (!lua_istable(L,index))
{
return;
}
int tLen = lua_objlen(L,index);
for(int i = 0;i < tLen;i++)
{
lua_rawgeti(L,index,i+1);
int t = lua_type(L, -1);
switch (t) {
case LUA_TSTRING:
printf("t[%d]=%s ",i+1, lua_tostring(L,-1));
break;
case LUA_TBOOLEAN:
printf("t[%d]=%s ",i+1,lua_toboolean(L, -1)? "true":"false");
break;
case LUA_TNUMBER:
printf("t[%d]=%g ",i+1, lua_tonumber(L, -1));
break;
default:
printf("t[%d]=%s ",i+1, lua_typename(L, t));
break;
}
lua_pop(L,1);
}
printf("\n");
}
4、項目例子
這里是將一個c++的配置壓入到lua一張叫QuestData(任務(wù)表)的全局的表里。這個例子可以看出c構(gòu)建lua表數(shù)據(jù)的基本應(yīng)用
void QuestAccessor::makeScriptQuestData()
{
QuestConfig** ppQuestList = m_Quest.own_ptr();
INT_PTR nCount = m_Quest.length();
lua_State* L = g_Script->getLuaState();
lua_newtable(L);//QuestData表
for (INT_PTR i = 1; i < nCount; ++i)
{
pushToQuestData(L, ppQuestList[i]);
}
lua_setglobal(L, "QuestData");//設(shè)置該表作為全局的表,名為QuestData
}
/*
QuestData[questId] = {
name = "任務(wù)名",
acceptTalk = { "111", "222", "333" },
acceptReply = {"11", "22", "33"},
completeTalk = { "aaa", "bbb", "ccc" },
completeReply = {"aa", "bb", "cc"},
}
*/
void QuestAccessor::pushToQuestData(lua_State* L, QuestConfig *quest)//最外面建了一張表quests
{
lua_newtable(L);//新建questId作為QuestData表的下標的表
lua_pushvalue(L,-1);//拷貝表引用到棧頂
lua_rawseti(L, -3, quest->nQid);//設(shè)置questId作為QuestData表的下標的表
//name = "quest-name"
lua_pushstring(L, quest->sName);
lua_setfield(L, -2, "name");//questId 表的名字
const QuestAccessor::TalkStruct* pTalkStruct = &(m_questTalkList.get(quest->nQid));
if (NULL != pTalkStruct)
{
if (pTalkStruct->accept.nCount > 0)
{
lua_newtable(L);//acceptTalk 表
lua_pushvalue(L,-1);
lua_setfield(L, -3, "acceptTalk");
for (int i = 0; i < pTalkStruct->accept.nCount; ++i)
{
lua_pushstring(L, pTalkStruct->accept.talkList[i].c_str());
lua_rawseti(L, -2, i + 1);
}
lua_pop(L, 1);//彈出acceptTalk表
lua_newtable(L);//新建acceptReply表
lua_pushvalue(L, -1);
lua_setfield(L, -3, "acceptReply");//設(shè)置acceptReply表到questId表
for (int i = 0; i < pTalkStruct->accept.nCount; ++i)
{
lua_pushstring(L, pTalkStruct->accept.replyList[i].c_str());
lua_rawseti(L, -2, i+1);
}
lua_pop(L, 1);//彈出acceptReply標
}
if (pTalkStruct->complete.nCount > 0)
{
lua_newtable(L);//新建completeTalk表
lua_pushvalue(L, -1);
lua_setfield(L, -3, "completeTalk");
for (int i = 0; i < pTalkStruct->complete.nCount; ++i)
{
lua_pushstring(L, pTalkStruct->complete.talkList[i].c_str());
lua_rawseti(L, -2, i + 1);
}
lua_pop(L, 1);
lua_newtable(L);//新建completeReply表
lua_pushvalue(L, -1);
lua_setfield(L, -3, "completeReply");
for (int i = 0; i < pTalkStruct->complete.nCount; ++i)
{
lua_pushstring(L, pTalkStruct->complete.replyList[i].c_str());
lua_rawseti(L, -2, i+1);
}
lua_pop(L, 1);
}
}
lua_pop(L, 1);//彈出questId表
}
轉(zhuǎn)自:http://blog.csdn.net/chenjiayi_yun/article/details/23772627