乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

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

       quasiceo 2016-07-15

      再說C模塊的編寫(1)

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

      前言

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

      數(shù)組操作

      在Lua中應(yīng)該不能叫數(shù)組,而是一種table的東西;而在C語言中,沒有table這種東西,只有數(shù)組。Lua中的table可以使關(guān)聯(lián)的,也就是key=>value鍵值對,而C中,數(shù)組不是關(guān)聯(lián)的,下標(biāo)是從0開始的。當(dāng)然了,Lua中的數(shù)組表示,只是table的一個子集,就是因?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ù),出于以下兩個原因:

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

      API為數(shù)組操作提供了兩個函數(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ù)中涉及到兩個索引,index表示table在棧中的位置,key表示元素在table中的元素。這兩個函數(shù)都是原始操作,比涉及元表的table訪問更快。通常,作為數(shù)組使用的table很少會用到元表。

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

      static int array_walk(lua_State *L)
      {
      	// 和寫別的函數(shù)一行,先檢查參數(shù)的合法性
      	// 第一個參數(shù)必須是一個table
      	luaL_checktype(L, 1, LUA_TTABLE);
      
      	// 第二個參數(shù)必須是一個用戶定義的函數(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對應(yīng)的值壓入棧
      		lua_rawgeti(L, 1, i);
      
      		// 調(diào)用用戶定義的函數(shù)
      		lua_call(L, 1, 1);
      		lua_rawseti(L, 1, i);
      	}
      	// 沒有返回值壓入棧中
      	return 0;
      }

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

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

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

      字符串操作

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

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

      1. 不要在訪問字符串時,從棧中彈出它;
      2. 不要修改字符串。

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

      標(biāo)準(zhǔn)API為兩種常用的字符串操作提供了支持:提取子串和字符串連接。lua_pushlstring支持提取子串,它接受一個額外的字符串長度參數(shù),這就好比我們在壓入棧時,對字符串進(jìn)行了一個截取操作。下面我先來完成一個簡單的功能,根據(jù)指定的切割符號來切割字符串,將子串保存在一個table中,然后向Lua返回這個table。來吧?。。?/p>

      static int split(lua_State *L)
      {
      	// 傳進(jìn)來兩個參數(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++);
      
      		// 跳過分隔符
      		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)代碼貼上來了。無需多解釋,慢慢看,能看懂的。Lua測試代碼如下:

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

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

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

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

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

      為了更好的描述輔助庫的緩沖機(jī)制,來看一段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. 聲明一個luaL_Buffer變量;
      2. 使用luaL_buffinit來初始化它;
      3. 調(diào)用luaL_add*系列函數(shù)向緩沖區(qū)添加字符或字符串;
      4. 調(diào)用luaL_pushresult更新緩沖區(qū),將最終的結(jié)果字符串留在棧頂。

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

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

      總結(jié)

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

      2014年8月26日 于深圳。

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多