小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

再說(shuō)C模塊的編寫(1) | 果凍想

 quasiceo 2016-07-15

再說(shuō)C模塊的編寫(1)

2014-08-26 分類:Lua 閱讀(958) 評(píng)論(0)

前言

在《Lua“控制”C》中對(duì)Lua調(diào)用C函數(shù)做了初步的學(xué)習(xí),而這篇才是重中之重,這篇文章會(huì)重點(diǎn)的總結(jié)C模塊編寫過(guò)程中遇到的一些問(wèn)題,比如數(shù)組操作、字符串操作和C函數(shù)的狀態(tài)保存等問(wèn)題。現(xiàn)在就開始吧。

數(shù)組操作

在Lua中應(yīng)該不能叫數(shù)組,而是一種table的東西;而在C語(yǔ)言中,沒(méi)有table這種東西,只有數(shù)組。Lua中的table可以使關(guān)聯(lián)的,也就是key=>value鍵值對(duì),而C中,數(shù)組不是關(guān)聯(lián)的,下標(biāo)是從0開始的。當(dāng)然了,Lua中的數(shù)組表示,只是table的一個(gè)子集,就是因?yàn)檫@種關(guān)系,就有了C數(shù)組和Lua table的交互關(guān)系了。

比如lua_settable和lua_gettable這種操作table的API(其實(shí)之前我一直用的都是lua_setfield和lua_getfield),也可以操作數(shù)組。然而,API為數(shù)組操作提供了專門的函數(shù),出于以下兩個(gè)原因:

  1. 性能;我們一般使用C語(yǔ)言來(lái)擴(kuò)展Lua,都是用來(lái)做一些Lua難以做到,而C卻非常容易做到的事情,比如一些追求效率的算法;如果提高了訪問(wèn)數(shù)組的效率,那就能提高整個(gè)算法的性能了;
  2. 便利;整數(shù)key是非常常用的,所以提供專門的API也會(huì)非常便利的。

API為數(shù)組操作提供了兩個(gè)函數(shù):

void lua_rawgeti(lua_State *L, int index, int key);
void lua_rawseti(lua_State *L, int index, int key);

lua_rawgeti和lua_rawseti的參數(shù)中涉及到兩個(gè)索引,index表示table在棧中的位置,key表示元素在table中的元素。這兩個(gè)函數(shù)都是原始操作,比涉及元表的table訪問(wèn)更快。通常,作為數(shù)組使用的table很少會(huì)用到元表。

下面就來(lái)一個(gè)實(shí)例,看看如何使用上面的兩個(gè)API函數(shù),不知道你會(huì)不會(huì)PHP,在PHP中,有一個(gè)array_walk函數(shù),這個(gè)函數(shù)允許用戶定義一個(gè)函數(shù),然后對(duì)數(shù)組中的每個(gè)函數(shù)都應(yīng)用這個(gè)函數(shù)。我現(xiàn)在就來(lái)實(shí)現(xiàn)這個(gè)功能。把重點(diǎn)代碼貼上來(lái):

static int array_walk(lua_State *L)
{
	// 和寫別的函數(shù)一行,先檢查參數(shù)的合法性
	// 第一個(gè)參數(shù)必須是一個(gè)table
	luaL_checktype(L, 1, LUA_TTABLE);

	// 第二個(gè)參數(shù)必須是一個(gè)用戶定義的函數(shù)
	luaL_checktype(L, 2, LUA_TFUNCTION);

	// 獲取table的大小
	int iLen = lua_objlen(L, 1);

	for (int i = 1; i <= iLen; ++i)
	{
		// 將用戶定義的函數(shù)壓入棧
		lua_pushvalue(L, 2);

		// 將參數(shù)table的所以i對(duì)應(yīng)的值壓入棧
		lua_rawgeti(L, 1, i);

		// 調(diào)用用戶定義的函數(shù)
		lua_call(L, 1, 1);
		lua_rawseti(L, 1, i);
	}
	// 沒(méi)有返回值壓入棧中
	return 0;
}

代碼比較簡(jiǎn)單,不多說(shuō),哪里不懂的地方,可以留言。對(duì)于代碼中出現(xiàn)的luaL_checktype和lua_call函數(shù),這里說(shuō)一下。luaL_checktype用來(lái)檢查給定的參數(shù)符合特定的類型,從而防止由于參數(shù)類型錯(cuò)誤而引起的后續(xù)錯(cuò)誤;如果參數(shù)不正確,這個(gè)函數(shù)就會(huì)引發(fā)一個(gè)錯(cuò)誤。

lua_call運(yùn)行在無(wú)保護(hù)的模式下,這個(gè)是它和lua_pcall最大的區(qū)別,所以它在發(fā)生錯(cuò)誤時(shí),會(huì)傳播錯(cuò)誤,而不是簡(jiǎn)單的返回一個(gè)錯(cuò)誤代碼。在我們的實(shí)際編程開發(fā)中,在一個(gè)應(yīng)用程序中編寫主函數(shù)時(shí),不應(yīng)該使用lua_call,因?yàn)檫@樣需要捕獲所有的錯(cuò)誤;而編寫C函數(shù)時(shí),通常可以用lua_call,當(dāng)錯(cuò)誤發(fā)生時(shí),就應(yīng)該讓錯(cuò)誤顯示出來(lái)。

上面只是貼出了關(guān)鍵代碼,可以點(diǎn)擊這里下載完整工程。

字符串操作

實(shí)際開發(fā)中,我們都是在和各種字符串打交道,現(xiàn)在我們就來(lái)完成這個(gè)功能,Lua傳進(jìn)一個(gè)字符串到C模塊中,C模塊進(jìn)行字符串處理。

當(dāng)一個(gè)C函數(shù)從Lua接收到一個(gè)字符串參數(shù)時(shí),必須遵守兩條規(guī)則:

  1. 不要在訪問(wèn)字符串時(shí),從棧中彈出它;
  2. 不要修改字符串。

當(dāng)一個(gè)C函數(shù)需要?jiǎng)?chuàng)建一個(gè)字符串返回給Lua時(shí),C代碼還必須處理字符串緩沖的分配和釋放等問(wèn)題。Lua API也提供了一些函數(shù)來(lái)幫助完成這些任務(wù)。

標(biāo)準(zhǔn)API為兩種常用的字符串操作提供了支持:提取子串和字符串連接。lua_pushlstring支持提取子串,它接受一個(gè)額外的字符串長(zhǎng)度參數(shù),這就好比我們?cè)趬喝霔r(shí),對(duì)字符串進(jìn)行了一個(gè)截取操作。下面我先來(lái)完成一個(gè)簡(jiǎn)單的功能,根據(jù)指定的切割符號(hào)來(lái)切割字符串,將子串保存在一個(gè)table中,然后向Lua返回這個(gè)table。來(lái)吧!??!

static int split(lua_State *L)
{
	// 傳進(jìn)來(lái)兩個(gè)參數(shù),先檢查參數(shù)的合法性
	const char *pSrc = luaL_checkstring(L, 1);
	const char *pSep = luaL_checkstring(L, 2);
	lua_newtable(L);
	int index = 1;
	char *pLocation = NULL;
	while ((pLocation = strchr(pSrc, *pSep)) != NULL)
	{
		// 壓入字符串
		lua_pushlstring(L, pSrc, pLocation - pSrc);

		// 設(shè)置結(jié)果表
		lua_rawseti(L, -2, index++);

		// 跳過(guò)分隔符
		pSrc = pLocation + 1;
	}

	// 把最后一部分壓入table中
	// eg.abc,def,cg
	// 現(xiàn)在把cg放到結(jié)果表中
	lua_pushstring(L, pSrc);
	lua_rawseti(L, -2, index);
	return 1;
}

把重點(diǎn)代碼貼上來(lái)了。無(wú)需多解釋,慢慢看,能看懂的。Lua測(cè)試代碼如下:

require "split"

local str = "abc,de,fg"
local strsep = ","
local tbRet = MySplit.split(str, strsep)
for _, v in pairs(tbRet) do
    print(v)
end

單擊這里下載完整項(xiàng)目代碼。

為了連接字符串,Lua API提供了一個(gè)叫l(wèi)ua_concat的函數(shù)。它類似于Lua中的“..”操作符。不過(guò),它可以同時(shí)連接多個(gè)字符串,調(diào)用lua_concat(L, n)連接(并彈出)棧頂?shù)膎個(gè)值,然后壓入結(jié)果。此外,這個(gè)函數(shù)會(huì)將數(shù)字轉(zhuǎn)換為字符串,并在需要的時(shí)候調(diào)用元方法(__tostring)。還有另外一個(gè)有用的函數(shù)是lua_pushfstring,這個(gè)函數(shù)和C中的sprintf有點(diǎn)類似,它們都會(huì)根據(jù)一個(gè)格式字符串和一些額外的參數(shù)來(lái)創(chuàng)建一個(gè)新字符串;但是與sprintf不同的是,無(wú)需提供這個(gè)新字符串的緩沖。Lua會(huì)動(dòng)態(tài)的創(chuàng)建一個(gè)足夠大的緩沖區(qū)來(lái)存放字符串,確保不會(huì)有緩沖溢出的問(wèn)題。這個(gè)函數(shù)會(huì)將結(jié)果字符串壓入棧中,并返回一個(gè)指向它的指針,當(dāng)前這個(gè)函數(shù)接受的指示符只有以下幾種:

  1. %%,表示字符%;
  2. %s,表示字符串;
  3. %d,表示整數(shù);
  4. %f,表示Lua中的數(shù)字, 即雙精度浮點(diǎn)數(shù);
  5. %c,接受一個(gè)整數(shù),并將它格式化為一個(gè)字符,和string.char功能類似。

除了上述列出的指示符以外,它不接受任何其它選項(xiàng)。

如果只是連接一些字符串的話,這樣簡(jiǎn)單的工作,lua_concat和lua_pushfstring就能夠很簡(jiǎn)單的完成;但是,如果要連接很多字符串的話,為了提高效率,我們可以使用輔助庫(kù),也就是lauxlib.h中定義的API函數(shù)來(lái)完成這項(xiàng)工作。輔助庫(kù)提供了什么呢?它提供了一種緩沖機(jī)制,包含了兩個(gè)層面的緩沖:

  1. 在本地緩沖區(qū)中收集較小的字符串,并在本地緩沖區(qū)滿了以后,將結(jié)果傳遞給Lua(通過(guò)lua_pushlstring);
  2. 使用lua_concat或其它算法來(lái)連接多次緩沖區(qū)填滿后的結(jié)果。

為了更好的描述輔助庫(kù)的緩沖機(jī)制,來(lái)看一段string.upper的源代碼,可以去Lua源代碼中的lstrlib.c文中查看。

static int str_upper (lua_State *L) {
  size_t l;
  size_t i;
  luaL_Buffer b;
  const char *s = luaL_checklstring(L, 1, &l);
  luaL_buffinit(L, &b);
  for (i=0; i<l; i++)
    luaL_addchar(&b, toupper(uchar(s[i])));
  luaL_pushresult(&b);
  return 1;
}

不要驚訝,Lua的代碼你可以隨心所欲的閱讀,偉大的開源,分享的力量。使用緩沖區(qū)分為以下幾步:

  1. 聲明一個(gè)luaL_Buffer變量;
  2. 使用luaL_buffinit來(lái)初始化它;
  3. 調(diào)用luaL_add*系列函數(shù)向緩沖區(qū)添加字符或字符串;
  4. 調(diào)用luaL_pushresult更新緩沖區(qū),將最終的結(jié)果字符串留在棧頂。

在調(diào)用luaL_buffinit初始化以后,這個(gè)變量中就會(huì)保留一份狀態(tài)L的副本,所以在后續(xù)調(diào)用luaL_add*系列函數(shù)時(shí),就不用傳遞lua_State參數(shù)了。

通過(guò)使用這些函數(shù),就可以使用緩沖機(jī)制,我們也不用再去關(guān)心緩沖的分配、溢出等細(xì)節(jié)了。另外,這種連接算法也非常高效。用str_upper函數(shù)處理大型的字符串也不會(huì)有什么問(wèn)題。

總結(jié)

這篇《再說(shuō)C模塊的編寫(1)》到這里就結(jié)束了。由于篇幅不夠,還有一部分內(nèi)容,且非常重要的內(nèi)容沒(méi)有總結(jié),在下一篇《再說(shuō)C模塊的編寫(2)》中,就對(duì)剩下的那部分非常重要的內(nèi)容,單獨(dú)進(jìn)行總結(jié)。希望我這里總結(jié)的內(nèi)容,大家能看懂,能明白;同時(shí),我也希望大家能和我進(jìn)行更多的交流,大家互相提升。

2014年8月26日 于深圳。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多