再說C模組的編寫(2)

2022-07-01 20:57:10 字數 2936 閱讀 9306

【前言】

在《再說c模組的編寫(1)》中主要總結了lua呼叫c函式時,對陣列和字串的操作,而這篇文章將重點總結如何在c函式中儲存狀態。

什麼叫做在c函式中儲存狀態?比如你現在使用lua呼叫了c函式func1,但是func1中有一些資料在呼叫完以後儲存下來,供以後使用。而這些資料就是所謂的狀態,也就是我們需要儲存的東東。根據目前總結的所有內容,是無法做到在c函式中儲存狀態的。有人就會說了,lua呼叫c時,把所有的需要儲存的狀態都返回到lua中,當呼叫下乙個函式時,將需要的狀態當做引數再傳進去,不錯,是乙個辦法,但是很麻煩。而這裡我將總結3種比較方便,但是稍微有點難理解的方法在c函式中儲存狀態。

登錄檔是乙個全域性的table,它只能被c**訪問。通常,可以用它來儲存那種需要在幾個模組中共享的資料;但是,如果需要儲存乙個模組的私有資料,那麼應該使用環境,與lua函式一樣,每個c函式都有自己的環境table,通常情況下,乙個模組內的所有函式共享同乙個環境table,由此它們可以共享資料。最後,c函式也可以擁有upvalue,upvalue是一種與特定函式相關聯的lua值。現在我就逐一開始分析,總結。let』s go.

【登錄檔】

登錄檔總是位於乙個「偽索引」上,這個索引值由lua_registryindex定義。偽索引就像是乙個棧中的索引,但它所關聯的值不在棧中;所完這句話,你想到了什麼?c++中,使用new開闢空間,這個空間是在堆上開闢的,而指向這個堆的變數卻是存放在棧上的。偽索引和這個意思差不多。lua api中的大多數函式都能接受偽索引,但像lua_remove和lua_insert這種操作棧本身的函式卻只能使用普通索引。

登錄檔是乙個普通的lua  table,可以使用任何lua值(nil除外)來索引它。例如,要獲取登錄檔中key為「jellythink」的值,可以這麼做:

lua_getfield(l, lua_registryindex, "

jellythink

");

現在就出現了乙個很棘手的問題,由於所有的c模組共享同乙個登錄檔,為了避免使用衝突,必須謹慎的選擇key的值,為了保證key的唯一性,避免衝突,建議使用uuid作為key值。

在登錄檔中,不要使用數字型別的key,因為這種key是被「引用系統」所保留的。「引用系統」是由輔助庫中的一系列函式組成的,它可以在向乙個table儲存value時,忽略如何建立乙個唯一的key。例如以下呼叫:

int r = lual_ref(l, lua_registryindex);

會從棧中彈出乙個值,然後用乙個新分配的整數key來將這個值儲存到登錄檔中,最後返回這個整數key。這個key被稱為「引用」。

你問我什麼情況下使用登錄檔?登錄檔是乙個全域性的table,它可以在多個c模組中共享資料,在乙個c模組中註冊了乙個lua值的引用,在其它c模組照樣可以使用這個引用。光說不練,雁過還的拔毛呢,下面就來乙個例項,做以下試驗:

準備cmodule和cmodule2兩個c模組;

lua呼叫cmodule,傳入乙個table,在cmodule中註冊,然後在cmodule2中列印這個table中的值;

lua呼叫cmodule,傳入乙個function,在cmodule中註冊,然後在cmodule2中呼叫這個lua function;

lua呼叫cmodule,傳入乙個string值,在cmodule中註冊,然後在cmodule2中列印這個string的值。

引用系統將nil視為一種特殊情況。為乙個nil值呼叫lual_ref時,並不會建立新引用,而是返回乙個常量引用lua_refnil。對lua_refnil使用lua_rawgeti時,會向棧中壓入乙個nil。下面就拿註冊乙個function為例子,簡單分析以下,主要還是要理解**:

static

int registerfunc(lua_state *l)

【環境】

從5.1開始,在lua中註冊的所有c函式都有自己的環境table。乙個函式可以像訪問登錄檔那樣,通過乙個偽索引來訪問它的環境table。環境table的偽索引是lua_environindex。

這種使用環境的方法與在lua模組中使用環境的方法差不多,都是先為模組建立乙個新的table,然後使模組中的所有函式都共享這個table。只不過,在lua中使用了乙個setfenv函式,而在c模組中,只不過是設定table為lua_environindex。下面就來看一段環境的**。

int luaopen_environindexdemo(lua_state *l)

這個註冊函式比以前寫的註冊函式要多兩行**,先要建立乙個新的table,然後呼叫lua_replace將新的table作環境table。然後呼叫lual_register時,所有新建的函式都會繼承當前環境。

static

int setvalue(lua_state *l)

//從環境中取出對應的值

static

int getvalue(lua_state *l)

【upvalue】

登錄檔提供了全域性變數的儲存,環境提供了模組變數的儲存,而upvalue機制則實現了一種類似於c語言中靜態變數的機制。對upvalue不熟悉的夥計,可以看看《lua中的閉包》這篇文章。而這種upvalue機制,可以讓我們定義乙個只在特定的函式中可見的變數。每當在lua中建立乙個函式時,都可以將任意數量的upvalue與這個函式相關聯。每個upvalue都可以儲存乙個lua值。以後,在呼叫這個函式時,就可以通過偽索引來訪問這些upvalue了。

將這種c函式與upvalue的關聯稱為closure(也叫閉包,多麼熟悉的名字)。乙個c closure類似於lua closure。closure可以用同乙個函式**來建立多個closure,每個closure可以擁有不同的upvalue。接下來,來乙個簡單的例項,上**:

static

int count(lua_state *l)

static

int newcount(lua_state *l)

再說C模組的編寫(1)

前言 在 lua 控制 c 中對lua呼叫c函式做了初步的學習,而這篇才是重中之重,這篇文章會重點的總結c模組編寫過程中遇到的一些問題,比如陣列操作 字串操作和c函式的狀態儲存等問題。現在就開始吧。陣列操作 在lua中應該不能叫陣列,而是一種table的東西 而在c語言中,沒有table這種東西,只...

C 模組呼叫 C 編寫模組

c 模組呼叫 c 編寫模組 涉及知識點 1 ifdef cplusplus extern c 處理其中的 要明白為何使用extern c 還得從cpp 中對函式的過載處理開始說起。在c 中,為了支援過載機制,在編譯生成的彙編碼中,要對函式的名字進行一些處理,加入比如函式的返 回型別等等.而在c 中,...

c編寫Python模組

附 為何要編寫c擴充套件 保護核心 解決效能瓶頸 建立c一些特有的東西 環境 linux 13.9 300.fc27.x86 64 python 3.6.3 開始 目標 這次我麼來實習製作python3的擴充套件,實現兩個功能 求乙個整數的絕對值 求乙個字串的逆序 用c實現這個功能 int my a...