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]
luaL_error
,该函数会抛出异常(或执行 longjmp
,这与之类似)。调用者有责任通过使用 lua_cpcall
或设置 lua_atpanic
来捕获此错误。
lua_gencallA
,并用 lua_cpcall
包裹。如果发生错误,异常将被捕获,错误消息将作为函数结果输出。否则,函数将返回 NULL
。
lua_gencallA
的 Unicode 或宽字符版本。它的两个字符串参数(脚本代码块和格式)必须是 wchar_t* 类型。它将把它们转换为 char* 对应物并调用 lua_gencallA
。格式字符串始终包含 ASCII 字符,因此转换很简单。脚本字符串可以包含任意字符;最常见的是本地化语言中的文件名和用户消息。函数支持两种转换方法,在编译时选择。可以使用 wctomb
和 mbtowc
(基于当前区域设置的系统函数),也可以使用自定义代码转换为 UTF-8。
lua_gencallW
的保护版本。如果发生错误,消息字符串将被转换回宽字符并返回。
lua_gencall
重定向到 lua_gencallA
或 lua_gencallW
,具体取决于是否定义了 UNICODE
。也可以在没有宽字符支持的情况下编译库文件。
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 ]"
Directives
、Inputs
和 Outputs
是类似于 printf
和 scanf
格式的字符串,由以百分号开头的格式项组成。Directives
和 Outputs
是可选部分。如果存在,则必须用 < 或 > 字符与 Inputs
分隔。Inputs
也可以是空字符串。
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
为前 2 个表中所述的基本 C 类型。除了 '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**
luaL_openlibs
初始化
lua_close
释放
lgencall.c
和一个头文件 lgencall.h
。还有一个测试文件 testwin.cpp
,其中包含下一章的所有测试示例,包括 Windows 头文件 tchar.h
。使用此实用程序头文件,可以编写可在 ANSI 和 Unicode 平台上编译的代码。
主 C 文件包含 ANSI 标准文件和公共 Lua API 头文件。与其他标准 Lua 库一样,不使用任何私有功能,该文件可以在 C 和 C++ 语言中编译。但是,它需要新的 C99 包含文件 stdint.h
来定义固定大小的整数。如果您的编译器不支持此功能,WWW 上有几个免费版本可供使用。 [1] [2]
源文件可以与应用程序一起编译,也可以放在 Lua 共享库中(如果您有能力重新编译它)。
lgencall.h
中定义了 3 个编译宏,用于为您的平台定制库。每个参数可以在文件本身中更改,也可以在编译器的命令行中指定。头文件中提供了一个简短的解释,列出了可能的值。此外,编译还受以下标准宏的影响:__cplusplus
、INT_MAX
、UINT_MAX
和 __STDC_VERSION__
。
"Hello World"
程序
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
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
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
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);
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', }
-- PatrickRapin