Lua 通用呼叫 |
|
const char* errmsg = NULL; double result; lua_settop(L, 0); if(luaL_loadstring(L, "local a,b = ...; return a*b")) errmsg = lua_tostring(L, -1); else { lua_pushinteger(L, 3); lua_pushnumber(L, 2.5); if(lua_pcall(L, 2, 1, 0)) errmsg = lua_tostring(L, -1); else result = lua_tonumber(L, -1); }
double result; const char* errmsg = lua_genpcall(L, "local a,b = ...; return a*b", "%d %f > %lf", 3, 2.5, &result);
GetProcAddress
或 dlsym
。然而,對於這項工作,最好遵循 EasyManualLibraryLoad 指南。可以在這裡找到原始碼 [lgencall.zip]
longjmp
)的 luaL_error
。由呼叫者透過使用 lua_cpcall
或設定 lua_atpanic
來捕捉這個錯誤。lua_cpcall
包住的 lua_gencallA
。在錯誤的情況下,例外會被捕捉,並且錯誤訊息會作為函數結果輸出。否則,函數會傳回 NULL
。lua_gencallA
。它的兩個字串參數(指令區塊和格式)必須是 wchar_t* 型式。它會將這些轉換成對應的 char* 並呼叫 lua_gencallA
。格式字串總是包含 ASCII 字元,因此轉換很簡單。指令碼字串可以包含任意字元;最典型的例子是檔案名稱和在地化語言中的使用者訊息。函數支援兩種轉換方法,可在編譯時選取。可以使用 wctomb
和 mbtowc
(基於目前區域設定的系統函數),或使用自訂程式碼轉換成 UTF-8。lua_gencallW
的受保護版本。在錯誤的情況下,訊息字串會轉換回寬字元並傳回。UNICODE
,自動將 lua_gencall
重新導向至 lua_gencallA
或 lua_gencallW
。也可以在沒有寬字元支援的情況下編譯函式庫檔案。
NULL
,函數自動呼叫 lua_newstate
來建立新實例,且也會呼叫 lua_close
以釋出其記憶體,除非實例的指標已使用 %S 選項擷取(見下)。若狀態已釋放且先前發生錯誤,函數將配置一個緩衝區來複製錯誤訊息,且必須使用 free
關閉該緩衝區。local var1, var2, var3 = ...;
並以傳回結果結尾:return res1, res2, res2
。若指標為 NULL
,則等同於空指令碼 ""。printf
或 scanf
格式字串的字串,使用 % 字元來描述輸入和輸出值的變數類型。若指標為 NULL
,則等同於空格式 ""。基於效能因素考量,已編譯程式塊有快取。此快取實作為 Lua 註冊表中的一個 Lua 表格,其索引為程式塊字串。因此,如果您使用同一個指令碼字串呼叫 lua_genpcall
數次,它只會在第一次編譯。所有後續呼叫會重複使用快取版本。快取表格不是弱表格,以避免讓同一指令碼重新編譯多次,因為垃圾收集正在執行中。其缺點是,當指令碼程式塊在執行階段可以任意變更時,快取可能會無限擴增。例如,這會發生在伺服器詮譯器執行來自客戶端程式的 Lua 程式塊時。不過,您可以明確清除快取,方法是將其指定於格式字串中。
就像 printf
,甚至更像 scanf
,您必須非常仔細處理參數的類型以及對應的格式說明。任何不符都會導致預期之外的結果,更糟的是,這會讓應用程式崩潰。
格式字串的共通用法為以下格式
"[ Directives < ] Inputs [ > Outputs ]"
指令
、輸入值
和輸出值
是類似於 printf
和 scanf
格式的字串,包含以百分比符號開頭的格式項目。指令
和輸出值
是選用部分。如果它們出現在此,則必須使用 < 或 > 字元與 輸入值
分隔。輸入值
也可以是空字串。
printf
格式規格相同,每個輸入或輸出格式項目最多包含 6 個欄位,如下一個範例所示。指令項目有較少的選項,並會在後面說明。任何空白字元(空白、製表符、換行符和行送)都將被忽略,可以用來增加格式字串的清晰度。本節說明的其他字元會依說明進行詮釋,或在無效時拋出 Lua 錯誤。%#12.4Ls
標記引數可以是下列其中一個字元
lua_newstate
,其預設實作為呼叫標準的 realloc
和 free
功能)以配置輸出字串或陣列。使用後,您需要 free
該資料。寬度參數是用於字串、字串清單和陣列。它代表記憶體緩衝區的元素或字元的數目。它可以採用下列形式:
strlen
判斷。對於字串清單,它表示在兩個連續的零字元後清單結束。精度引數用於數字類型以表示 C 類型的位元組大小。對於數字陣列和輸出值,它很重要。這是因為在兩種情況下,傳遞的都是變數的指標,而不是值本身。它的形式如下
大小修改器參數是數值精準度值的替代方案,並且會變更值的預期 C 型態。它可以是下列字元之一。請參閱下表中的大小表以取得對應項目
最重要的是,轉換字元指定資料型態。它必須是下列字元之一
對於數值,以下是預設和修改後的基礎 C 型態,列在下列表格中
(default) 'hh' 'h' 'l' 'L' 'f' float --- float double long double (*) 'd','i' int char short long int64_t (*) 'u' unsigned unsigned unsigned unsigned uint64_t (*) int char short long 'b' C89: int --- char int --- C99: _Bool C++: bool 's','z' gencallA: char* gencallW: wchar_t* (*) --- char* wchar_t* (*) ---
其他物件值具有下列相關的 C 型態
ptr
將收到其位址。視資料方向而定,指標函式具有這兩個原型之一TYPE
是基本 C 型態,如同前 2 個表格中所述。除了 'n' 和 'k' 轉換字元,組成的型態為Width argument (none) number, '*' or '&' number, '*' or '&' Flag (don't care) (none) '#' or '+' Input TYPE const TYPE* const TYPE* Output TYPE* TYPE* TYPE**
主要 C 檔案包含 ANSI 標準檔案,以及公開的 Lua API 標頭檔案。與其他標準 Lua 函式庫一樣,不會使用私有功能,而檔案可以在 C 和 C++ 語言中編譯。不過,它需要新的 C99 包含檔案 `stdint.h` 來定義固定大小的整數。如果您的編譯器不支援這點,WWW 上有多個免費版本可以使用。 [1] [2]
原始檔可以與應用程式一起編譯,或者如果您有能力重新編譯的話,可以放入 Lua 共用函式庫。
__cplusplus
、INT_MAX
、UINT_MAX
和 __STDC_VERSION__
。
lua_genpcall(NULL, "print 'Hello World!'", "%O<");
print
函式。由於沒有傳遞 %S 選項,因此 Lua 狀態會在呼叫結束時釋放。我們未測試回傳值,那是一個錯誤訊息。以下是相同的範例,但作為一個更完整且更實際的實作
lua_State* L = lua_open(); luaL_openlibs(L); const char* errmsg = lua_genpcall(L, "print 'Hello World!'", ""); if(errmsg) fprintf(stderr, "Lua error: %s\n", errmsg); lua_close(L);
再次提供相同的範例,但只使用 lua_genpcall
lua_State* L; lua_Alloc falloc; lua_genpcall(NULL, NULL, "%O %S %&M<", &L, &falloc); char* errmsg = lua_genpcall(L, "print 'Hello World!'", "%C<"); if(errmsg) { fprintf(stderr, "Lua error: %s\n", errmsg); falloc(NULL, errmsg, 10, 0); }
1. 數字
lua_genpcall(L, "for k,v in pairs{...} do print(k, type(v), v) end", "%i %d %u %f %f", -4, 0xFFFFFFFF, 0xFFFFFFFF, 3.1415926535f, 3.1415926535); --> 1 number -4 2 number -1 3 number 4294967295 4 number 3.1415927410126 5 number 3.1415926535
number
)。由於在 Lua 中所有數字都儲存為 double,因此浮點參數的 Pi
值存在截斷錯誤。請注意 %d 和 %u 在值 0xFFFFFFFF
的行為差異。對於 double 參數,不必在此指定 %lf 而不是 %f,因為浮點 числа在傳遞給 C 中的變數參數函式時,會一律轉換為 double。小於 int 的整數也會自動轉換為 int。2. 布林值、nil、簡單字串和輕便使用者資料
lua_genpcall(L, "for k,v in pairs{...} do print(k, type(v), v) end", "%b %b %n %s %p", 0, 1, "Hello", L); --> 1 boolean false 2 boolean true 4 string Hello 5 userdata userdata: 0096CE70
true
和 false
。格式字串中只存在 nil 參數 %n,並未關聯到任何參數(不會列印出來,因為函式 pairs
會略過 nil 值)。字串在此假設以零結尾,而最後的參數 L
(Lua 狀態)只是一個通用指標範例。3. C 函式和回呼
int cFunction(lua_State* L) { printf("%s\n", luaL_checkstring(L, 1)); return 1; } void pushMessage(lua_State* L, const void* ptr) { lua_pushstring(L, *(const char**)ptr); } ... lua_genpcall(L, "local fct, msg = ...; fct(msg)", "%c %k", cFunction, pushMessage, "Hello from C!");
cFunction
的指標。第二個參數是一個回呼參數,包含使用者函式 pushMessage
和一個字串。請注意,回呼函式接收參數的指標,而不是參數本身!4. 數值陣列
short array[] = { 1,2,3 }; lua_genpcall(L, "for k,v in pairs{...} do print(k, #v, table.concat(v, ', ')) end", "%2hd %5.1u %*.*d", array, "Hello", sizeof(array)/sizeof(array[0]), sizeof(array[0]), array); --> 1 2 1, 2 2 5 72, 101, 108, 108, 111 3 3 1, 2, 3
5. 進階字串
unsigned char data[] = { 200, 100, 0, 3, 5, 0 }; lua_genpcall(L, "for k,v in pairs{...} do print(k, v:gsub('.', " "function(c) return '\\\\'.. c:byte() end)) end", "%s %6s %*s %ls", "Hello", "P1\0P2", sizeof(data), data, L"�t�"); --> 1 \72\101\108\108\111 5 2 \80\49\0\80\50\0 6 3 \200\100\0\3\5\0 6 4 \195\169\116\195\169 5
... return '\\' ...
),第二次是針對 Lua。第一個參數是以零結尾的字串;第二個字串包含一個二進位 0,並透過其長度指定。第三個參數是資料的二進位陣列,其長度透過參數傳遞。最後一個參數則是寬字元字串,它會轉換為 UTF-8 字串或其他形式的多位元組字串,具體取決於模組編譯方式。6. 字串清單
lua_genpcall(L, "for k,v in pairs{...} do print(k, #v, table.concat(v, ',')) end", "%z %7z %hz %*lz", "s1\0s2\0s3\0", "s4\0\0s5\0", "c1\0c2\0c3\0", 7, L"w1\0\0w2\0" ); --> 1 3 s1,s2,s3 2 3 s4,,s5 3 3 c1,c2,c3 4 3 w1,,w2
lua_genpcallW
預期寬字元字串。最後一個清單是寬字元版本,並使用額外的 '*' 參數指定長度。
1. 數字
char var1; unsigned short var2; int var3; float var4; double var5; lua_genpcall(L, "return 1, 2, 3, 4, 5", ">%hhd %hu %d %f %lf", &var1, &var2, &var3, &var4, &var5); printf("%d %u %d %f %f\n", var1, var2, var3, var4, var5); --> 1 2 3 4.000000 5.000000
var1
和 var3
是帶正負號的整數,最後 2 個是浮點數。在此情況下,%lf 格式是強制性的!2. 布林值、nil、簡單字串和輕便使用者資料
bool bool1; int bool2; const char* str; void* ptr; lua_genpcall(L, "return true, false, 'dummy', 'Hello', io.stdin", ">%hb %lb %n %+s %p", &bool1, &bool2, &str, &ptr); printf("%d %d %s %p\n", bool1, bool2, str, ptr); --> 1 0 Hello 00975598
'Hello'
字串。最後一個結果值(使用者資料值)會透過地址取得一個通用指標。3. C 函式和回呼
void getMessage(lua_State* L, int idx, void* ptr) { *(const char**)ptr = lua_tostring(L, idx); } ... lua_CFunction fct; const char* msg; lua_genpcall(L, "return print, 'Hello World!'", ">%c %k", &fct, getMessage, &msg); lua_pushstring(L, msg); fct(L);
Hello World
程式。第一個傳回值是指向 Lua 註冊之 C 函式的指標,即全域 print
函式。第二個值(簡單字串)會透過 callback 函式擷取,其會接收變數 msg
的地址作為其 ptr
參數。然後,我们可以通過將訊息推送到 Lua 佇列並直接由 C 呼叫 print
函式來列印訊息(在正常情況下,這肯定不是一個好方法)。4. 數值陣列
unsigned int int_a[3]; bool bool_a[4]; char* str; short* pshort; int short_len; int bool_len = sizeof(bool_a)/sizeof(bool_a[0]); lua_genpcall(L, "return {1,2,3,4},{72,101,108,108,111,0}, {5,6,7}, {false,true}", ">%3u %+.1d %#&hd %&.*b", &int_a, &str, &short_len, &pshort, &bool_len, sizeof(bool_a[0]), &bool_a); printf("int_a = {%u,%u,%u}\nstr = %s\npshort[%d]=%d\nbool_a = #%d:{%d,%d,%d,%d}\n", int_a[0], int_a[1], int_a[2], str, short_len-1, pshort[short_len-1], bool_len, bool_a[0], bool_a[1], bool_a[2], bool_a[3]); free(pshort); --> int_a = {1,2,3} str = Hello pshort[2]=7 bool_a = #2:{0,1,204,204}
bool_len
的值初始化為陣列大小,因為我們正在使用 C 堆疊分配的緩衝區。由於緩衝區大於傳回的陣列,因此其最後兩個值將保持未初始化的狀態。5. 進階字串
const char *str1; char *str2; char str3[10]; unsigned char data[6]; int len = sizeof(data); wchar_t* wstr; lua_genpcall(L, "return 'Hello', ' Wor', 'ld!', '\\0\\5\\200\\0', 'Unicode'", ">%+s %#s %*s %&s %+ls", &str1, &str2, sizeof(str3), str3, &len, data, &wstr); printf("%s%s%s\ndata (%d bytes): %02X %02X %02X %02X %02X\nwstr = %S\n", str1, str2, str3, len, data[0],data[1],data[2],data[3],data[4], wstr); free(str2); --> Hello World! data (4 bytes): 00 05 C8 00 00 wstr = Unicode
6. 字串清單
void print_string_list(const char* title, const void* data, int fchar){ printf("%-4s = {", title); if(fchar) { const char* str = (const char*)data; while(*str){ printf("'%s', ", str); str += strlen(str) + 1; } } else { const wchar_t* str = (const wchar_t*)data; while(*str) { printf("'%S', ", str); str += wcslen(str) + 1; } } printf("}\n"); } � const char *str1; char *str2; char str3[10]; int len; wchar_t* wstr; lua_genpcall(L, "return {1,2,3},{4,5,6},{10,9,8,7},{11,12}", ">%+hz %+&z %*z %#lz", &str1, &len, &str2, sizeof(str3)/sizeof(str3[0]), &str3, &wstr ); print_string_list("str1", str1, 1); print_string_list("str2", str2, 1); printf("len = %d\n", len); print_string_list("str3", str3, 1); print_string_list("wstr", wstr, 0); free(wstr); --> str1 = {'1', '2', '3', } str2 = {'4', '5', '6', } len = 6 str3 = {'10', '9', '8', '7', } wstr = {'11', '12', }
print_string_list
只是用來以可讀形式顯示擷取的字串清單。第一個字串清單的類型為 char*,不論是否使用 Unicode 版本,但 str2
和 str3
不是如此。第一個清單分配在 Lua 堆疊上;第二個清單也是,此外還擷取了字串清單長度(未計算最後一個零位元組)。str3
緩衝區在 C 堆疊上;因此其大小會傳遞為一個額外參數,由「*」標記指示。最後一個字串清單是寬字元版本;其緩衝區使用 Lua 分配器分配,使用後需要呼叫 free
。你一定已經注意到字串和字串清單參數之間的強大相似性。-- PatrickRapin