【前言】
在《lua「控制」c》中對lua呼叫c函式做了初步的學習,而這篇才是重中之重,這篇文章會重點的總結c模組編寫過程中遇到的一些問題,比如陣列操作、字串操作和c函式的狀態儲存等問題。現在就開始吧。
【陣列操作】
在lua中應該不能叫陣列,而是一種table的東西;而在c語言中,沒有table這種東西,只有陣列。lua中的table可以使關聯的,也就是key=>value鍵值對,而c中,陣列不是關聯的,下標是從0開始的。當然了,lua中的陣列表示,只是table的乙個子集,就是因為這種關係,就有了c陣列和lua table的互動關係了。
比如lua_settable和lua_gettable這種操作table的api(其實之前我一直用的都是lua_setfield和lua_getfield),也可以運算元組。然而,api為陣列操作提供了專門的函式,出於以下兩個原因:
效能;我們一般使用c語言來擴充套件lua,都是用來做一些lua難以做到,而c卻非常容易做到的事情,比如一些追求效率的演算法;如果提高了訪問陣列的效率,那就能提高整個演算法的效能了;
便利;整數key是非常常用的,所以提供專門的api也會非常便利的。
api為陣列操作提供了兩個函式:
void lua_rawgeti(lua_state *l, int index, intkey);
void lua_rawseti(lua_state *l, int index, int key);
lua_rawgeti和lua_rawseti的引數中涉及到兩個索引,index表示table在棧中的位置,key表示元素在table中的元素。這兩個函式都是原始操作,比涉及元表的table訪問更快。通常,作為陣列使用的table很少會用到元表。
下面就來乙個例項,看看如何使用上面的兩個api函式,不知道你會不會php,在php中,有乙個array_walk函式,這個函式允許使用者定義乙個函式,然後對陣列中的每個函式都應用這個函式。我現在就來實現這個功能。把重點**貼上來:
staticint array_walk(lua_state *l)
//沒有返回值壓入棧中
return0;
}
lua_call執行在無保護的模式下,這個是它和lua_pcall最大的區別,所以它在發生錯誤時,會傳播錯誤,而不是簡單的返回乙個錯誤**。在我們的實際程式設計開發中,在乙個應用程式中編寫主函式時,不應該使用lua_call,因為這樣需要捕獲所有的錯誤;而編寫c函式時,通常可以用lua_call,當錯誤發生時,就應該讓錯誤顯示出來。
【字串操作】
實際開發中,我們都是在和各種字串打交道,現在我們就來完成這個功能,lua傳進乙個字串到c模組中,c模組進行字串處理。
當乙個c函式從lua接收到乙個字串引數時,必須遵守兩條規則:
不要在訪問字串時,從棧中彈出它;
不要修改字串。
當乙個c函式需要建立乙個字串返回給lua時,c**還必須處理字串緩衝的分配和釋放等問題。lua api也提供了一些函式來幫助完成這些任務。
標準api為兩種常用的字串操作提供了支援:提取子串和字串連線。lua_pushlstring支援提取子串,它接受乙個額外的字串長度引數,這就好比我們在壓入棧時,對字串進行了乙個擷取操作。下面我先來完成乙個簡單的功能,根據指定的切割符號來切割字串,將子串儲存在乙個table中,然後向lua返回這個table。來吧!!!
staticint split(lua_state *l)
//把最後一部分壓入table中
//現在把cg放到結果表中
lua_pushstring(l, psrc);
lua_rawseti(l, -2
, index);
return1;
}
把重點**貼上來了。無需多解釋,慢慢看,能看懂的。lua測試**如下:
require"split
"local str = "
abc,de,fg
"local strsep = ","
local tbret =mysplit.split(str, strsep)
for _, v in
pairs(tbret) do
(v)end
為了連線字串,lua api提供了乙個叫lua_concat的函式。它類似於lua中的「..」操作符。不過,它可以同時連線多個字串,呼叫lua_concat(l, n)連線(並彈出)棧頂的n個值,然後壓入結果。此外,這個函式會將數字轉換為字串,並在需要的時候呼叫元方法(__tostring)。還有另外乙個有用的函式是lua_pushfstring,這個函式和c中的sprintf有點類似,它們都會根據乙個格式字串和一些額外的引數來建立乙個新字串;但是與sprintf不同的是,無需提供這個新字串的緩衝。lua會動態的建立乙個足夠大的緩衝區來存放字串,確保不會有緩衝溢位的問題。這個函式會將結果字串壓入棧中,並返回乙個指向它的指標,當前這個函式接受的指示符只有以下幾種:
%%,表示字元%;
%s,表示字串;
%d,表示整數;
%f,表示lua中的數字, 即雙精度浮點數;
%c,接受乙個整數,並將它格式化為乙個字元,和string.char功能類似。
除了上述列出的指示符以外,它不接受任何其它選項。
如果只是連線一些字串的話,這樣簡單的工作,lua_concat和lua_pushfstring就能夠很簡單的完成;但是,如果要連線很多字串的話,為了提高效率,我們可以使用輔助庫,也就是lauxlib.h中定義的api函式來完成這項工作。輔助庫提供了什麼呢?它提供了一種緩衝機制,包含了兩個層面的緩衝:
在本地緩衝區中收集較小的字串,並在本地緩衝區滿了以後,將結果傳遞給lua(通過lua_pushlstring);
使用lua_concat或其它演算法來連線多次緩衝區填滿後的結果。
為了更好的描述輔助庫的緩衝機制,來看一段string.upper的源**,可以去lua源**中的lstrlib.c文中檢視。
staticint str_upper (lua_state *l)
宣告乙個lual_buffer變數;
使用lual_buffinit來初始化它;
呼叫lual_add*系列函式向緩衝區新增字元或字串;
呼叫lual_pushresult更新緩衝區,將最終的結果字串留在棧頂。
在呼叫lual_buffinit初始化以後,這個變數中就會保留乙份狀態l的副本,所以在後續呼叫lual_add*系列函式時,就不用傳遞lua_state引數了。
通過使用這些函式,就可以使用緩衝機制,我們也不用再去關心緩衝的分配、溢位等細節了。另外,這種連線演算法也非常高效。用str_upper函式處理大型的字串也不會有什麼問題。
再說C模組的編寫(2)
前言 在 再說c模組的編寫 1 中主要總結了lua呼叫c函式時,對陣列和字串的操作,而這篇文章將重點總結如何在c函式中儲存狀態。什麼叫做在c函式中儲存狀態?比如你現在使用lua呼叫了c函式func1,但是func1中有一些資料在呼叫完以後儲存下來,供以後使用。而這些資料就是所謂的狀態,也就是我們需要...
核心模組的編寫1
include includemodule license dual bsd gpl 是用來告知核心,該模組帶有乙個自由的許可證 沒有這樣的說明,在模組載入時核心會抱怨.static int hello init void static void hello exit void module ini...
Asterisk模組編寫(1)
是否有過想編寫asterisk模組的想法?在asterisk中有的模組相當的複雜,但是其結構卻非常的簡單,讓我們來從 hello world asterisk模組開始 res helloworld.該模組是基於asterisk1.6的,為asterisk1.4編寫模組幾乎一樣。建立的檔名為res h...