當(dāng)在Lua和C之間交換數(shù)據(jù)時(shí)主要的問題是自動(dòng)回收與手動(dòng)回收內(nèi)存管理的不一致筐喳。因此彭雾,Lua 用一個(gè)抽象的棧在Lua與C之間交換值疚顷。
本文目錄:
1壮池、c與lua的取值傳值
(1)取值(到c)
(2)傳值(到lua)
2硅瞧、堆棧操作api
(1) 壓入元素
(2) 查詢?cè)兀?) 其他堆棧操作3份乒、堆棧操作實(shí)例
(1)一些堆棧操作和打印堆棧上所有變量的例子
(2)提供一個(gè)打印一張lua表的c的api函數(shù)4、項(xiàng)目例子
本文內(nèi)容:
1腕唧、c與lua的取值傳值
(1)取值
棧中的每一條記錄都可以保存任何Lua值或辖。無論你何時(shí)想要從Lua請(qǐng)求一個(gè)值(比如一個(gè)全局變量的值),調(diào)用Lua枣接,被請(qǐng)求的值將會(huì)被壓入棧颂暇。
Lua以一個(gè)嚴(yán)格的LIFO規(guī)則(后進(jìn)先出;也就是說但惶,始終存取棧頂)來操作棧耳鸯。當(dāng)你調(diào)用Lua時(shí),它只會(huì)改變棧頂部分膀曾。你的C代碼卻有更多的自由县爬;更明確的來講,你可以查詢棧上的任何元素添谊,甚至是在任何一個(gè)位置插入和刪除元素财喳。
(2)傳值
無論你何時(shí)想要傳遞一個(gè)值給Lua,首先將這個(gè)值壓入棧,然后調(diào)用Lua(這個(gè)值將被彈出)耳高。我們?nèi)匀恍枰粋€(gè)不同的函數(shù)將每種C類型壓入棧和一個(gè)不同函數(shù)從棧上取值(只是取出不是彈出)扎瓶。
因?yàn)闂J怯蒐ua來管理的,垃圾回收器知道那個(gè)值正在被C使用泌枪。幾乎所有的API函數(shù)都用到了棧栗弟。例如,luaL_loadbuffer把它的結(jié)果留在了棧上(被編譯的chunk或一條錯(cuò)誤信息);lua_pcall從棧上獲取要被調(diào)用的函數(shù)并把任何臨時(shí)的錯(cuò)誤信息放在這里工闺。
2乍赫、堆棧操作api
(1) 壓入元素API有一系列壓棧的函數(shù),它將每種可以用C來描述的Lua類型壓棧:空值(nil)用lua_pushnil陆蟆,數(shù)值型(double)用lua_pushnumber雷厂,布爾型(在C中用整數(shù)表示)用lua_pushboolean,任意的字符串(char*
類型叠殷,允許包含”字符)用lua_pushlstring改鲫,C語言風(fēng)格(以”結(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ù),稍后會(huì)討論到它們林束。Lua中的字符串不是以零為結(jié)束符的像棘;它們依賴于一個(gè)明確的長(zhǎng)度,因此可以包含任意的二進(jìn)制數(shù)據(jù)壶冒。將字符串壓入串的正式函數(shù)是lua_pushlstring缕题,它要求一個(gè)明確的長(zhǎng)度作為參數(shù)。對(duì)于以零結(jié)束的字符串胖腾,你可以用lua_pushstring(它用strlen來計(jì)算字符串長(zhǎng)度)烟零。Lua從來不保持一個(gè)指向外部字符串(或任何其它對(duì)象,除了C函數(shù)——它總是靜態(tài)指針)的指針咸作。對(duì)于它保持的所有字符串锨阿,Lua要么做一份內(nèi)部的拷貝要么重新利用已經(jīng)存在的字符串。因此记罚,一旦這些函數(shù)返回之后你可以自由的修改或是釋放你的緩沖區(qū)墅诡。無論你何時(shí)壓入一個(gè)元素到棧上,你有責(zé)任確保在棧上有空間來做這件事情桐智。記住末早,你現(xiàn)在是C程序員;Lua不會(huì)寵著你酵使。當(dāng)Lua在起始以及在Lua調(diào)用C的時(shí)候荐吉,棧上至少有20個(gè)空閑的記錄(lua.h中的LUA_MINSTACK宏定義了這個(gè)常量)焙糟。對(duì)于多數(shù)普通的用法棧是足夠的穿撮,所以通常我們不必去考慮它痪欲。無論如何,有些任務(wù)或許需要更多的椧堤撸空間(如礁扮,調(diào)用一個(gè)不定參數(shù)數(shù)目的函數(shù))知举。在這種情況下太伊,或許你需要調(diào)用下面這個(gè)函數(shù):int lua_checkstack (lua_State *
L, int sz);它檢測(cè)棧上是否有足夠你需要的空間(稍后會(huì)有關(guān)于它更多的信息)僚焦。
(2) 查詢?cè)谹PI用索引來訪問棧中的元素芳悲。在棧中的第一個(gè)元素(也就是第一個(gè)被壓入棧的)有索引1,下一個(gè)有索引2,以此類推踢故。我們也可以用棧頂作為參照來存取元素惹苗,利用負(fù)索引。在這種情況下桩蓉,-1指出棧頂元素(也就是最后被壓入的)院究,-2指出它的前一個(gè)元素,以此類推伙窃。例如样漆,調(diào)用lua_tostring(L, -1)以字符串的形式返回棧頂?shù)闹怠N覀兿旅鎸⒖吹剑谀承﹫?chǎng)合使用正索引訪問棧比較方便呻右,另外一些情況下鞋喇,使用負(fù)索引訪問棧更方便。API提供了一套lua_is*
函數(shù)來檢查一個(gè)元素是否是一個(gè)指定的類型落塑,*
可以是任何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ù)不檢查這個(gè)值是否是指定的類型,而是看它是否能被轉(zhuǎn)換成指定的那種類型错蝴。例如顷锰,任何數(shù)字類型都滿足lua_isstring。還有一個(gè)lua_type函數(shù)肛宋,它返回棧中元素的類型束世。(lua_is*
中的有些函數(shù)實(shí)際上是用了這個(gè)函數(shù)定義的宏)在lua.h頭文件中,每種類型都被定義為一個(gè)常量:LUA_TNIL沉帮、LUA_TBOOLEAN贫堰、LUA_TNUMBER、LUA_TSTRING喇勋、LUA_TTABLE偎行、LUA_TFUNCTION贰拿、LUA_TUSERDATA以及LUA_TTHREAD。這個(gè)函數(shù)主要被用在與一個(gè)switch語句聯(lián)合使用汗盘。當(dāng)我們需要真正的檢查字符串和數(shù)字類型時(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沒有提供有效的可以用來判斷錯(cuò)誤發(fā)生數(shù)字值歌懒,所以返回的0是沒有什么用處的溯壶。對(duì)于其他函數(shù)而言且改,我們一般不需要使用對(duì)應(yīng)的lua_is*
函數(shù):我們只需要調(diào)用lua_is*
,測(cè)試返回結(jié)果是否為NULL即可又跛。Lua_tostring函數(shù)返回一個(gè)指向字符串的內(nèi)部拷貝的指針效扫。你不能修改它(使你想起那里有一個(gè)const)。只要這個(gè)指針對(duì)應(yīng)的值還在棧內(nèi)浩习,Lua會(huì)保證這個(gè)指針一直有效济丘。當(dāng)一個(gè)C函數(shù)返回后洽蛀,Lua會(huì)清理他的棧疟赊,所以近哟,有一個(gè)原則:永遠(yuǎn)不要將指向Lua字符串的指針保存到訪問他們的外部函數(shù)中(就算Lua_to*
和lua_is*
沒有彈棧操作,也不該使用c指針指向lua字符串的地址)疯淫。
Lua_string返回的字符串結(jié)尾總會(huì)有一個(gè)字符結(jié)束標(biāo)志0戳玫,但是字符串中間也可能包含0,lua_strlen返回字符串的實(shí)際長(zhǎng)度币绩。特殊情況下府阀,假定棧頂?shù)闹凳且粋€(gè)字符串肌似,下面的斷言(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ù)來完成通常的堆棧維護(hù)工作:(3-1)int lua_gettop (lua_State *
L);
函數(shù)lua_gettop返回堆棧中的元素個(gè)數(shù)力细,它也是棧頂元素的索引固额。注意一個(gè)負(fù)數(shù)索引-x對(duì)應(yīng)于正數(shù)索引gettop-x+1。(3-2)void lua_settop (lua_State *
L, int index);
lua_settop設(shè)置棧頂(也就是堆棧中的元素個(gè)數(shù))為一個(gè)指定的值逝慧。如果開始的棧頂高于新的棧頂啄糙,頂部的值被丟棄隧饼。否則,為了得到指定的大小這個(gè)函數(shù)壓入相應(yīng)個(gè)數(shù)的空值(nil)到棧上燕雁。特別的,lua_settop(L,0)清空堆棧僧免。你也可以用負(fù)數(shù)索引作為調(diào)用lua_settop的參數(shù)懂衩;那將會(huì)設(shè)置棧頂?shù)街付ǖ乃饕@眠@種技巧谒所,API提供了下面這個(gè)宏沛申,它從堆棧中彈出n個(gè)元素:#define lua_pop(L,n) lua_settop(L, -(n)-1)(3-3)void lua_pushvalue (lua_State *
L, int index);
函數(shù)lua_pushvalue壓入堆棧上指定索引的一個(gè)摶貝到棧頂姐军;(3-4)void lua_remove (lua_State *
L, int index);
lua_remove移除指定索引位置的元素奕锌,并將其上面所有的元素下移來填補(bǔ)這個(gè)位置的空白;(3-5)void lua_insert (lua_State *
L, int index);
lua_insert移動(dòng)棧頂元素到指定索引的位置饼丘,并將這個(gè)索引位置上面的元素全部上移至棧頂被移動(dòng)留下的空隔辽话;(3-6)void lua_replace (lua_State *
L, int index);最后,lua_replace從棧頂彈出元素值并將其設(shè)置到指定索引位置典徘,沒有任何移動(dòng)操作益咬。
(3-7)注意到下面的操作對(duì)堆棧沒有任何影響:lua_settop(L, -1);lua_insert(L, -1);
3幽告、堆棧操作實(shí)例(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 依次壓入幾個(gè)變量 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 用棧頂更換第三個(gè)
printf("3:\n");
lua_replace(L, 3);
stackDump(L);//true 10 true hello
//4 設(shè)置棧變量為6個(gè)
printf("4:\n");
lua_settop(L, 6);
stackDump(L);//true 10 true hello nil nil
//5 移除從棧頂往下數(shù)的第三個(gè)
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)提供一個(gè)打印一張表的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媒殉、項(xiàng)目例子
這里是將一個(gè)c++的配置壓入到lua一張叫QuestData(任務(wù)表)的全局的表里。這個(gè)例子可以看出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表的下標(biāo)的表
lua_pushvalue(L,-1);//拷貝表引用到棧頂
lua_rawseti(L, -3, quest->nQid);//設(shè)置questId作為QuestData表的下標(biāo)的表
//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標(biāo)
}
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