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