Lua Generic Call |
|
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
or dlsym
. However, for this job, it may be preferable to follow EasyManualLibraryLoad guideline.
The source code can be found here [lgencall.zip]
luaL_error
that throws an exception (or makes a longjmp
, which is similar). It is the responsibility of the caller to catch this error, by using lua_cpcall
, or by setting lua_atpanic
.
lua_gencallA
, enclosed with lua_cpcall
. In case of errors, the exception is trapped and the error message is output as the function result. Otherwise, the function returns NULL
.
lua_gencallA
. Its both string arguments (script chunk and format) must be of type wchar_t*. It will convert these into their char* counterpart and call lua_gencallA
. The format string always contains ASCII characters, so the transformation is straightforward. The script string may contain arbitrary characters; most typically file names and user messages in localized languages. Two conversion methods are supported by the function, selected at compile time. Either using wctomb
and mbtowc
(system functions based on current locale) or converting to UTF-8 with custom code.
lua_gencallW
. In case of errors, the message string is converted back to wide characters and returned.
lua_gencall
to either lua_gencallA
or lua_gencallW
depending whether UNICODE
is defined or not. It is also possible to compile the library file without wide character support.
NULL
, the function automatically calls lua_newstate
to create a new instance, and will also call lua_close
to release its memory, unless the pointer to the instance is retrieved with %S option (see after). If the state is freed and an error occurred before, the function allocates a buffer to copy the error message, which must be closed with free
.
local var1, var2, var3 = ...;
and ends with returning results: return res1, res2, res2
. If the pointer is NULL
, it is equivalent to the empty script "".
printf
or scanf
format strings, using the % character to describe the variable types of input and output values. If the pointer is NULL
, it is equivalent to the empty format "".
For performance reasons, there is a cache of already compiled chunks. It is implemented as a Lua table in the Lua registry, indexed by chunk strings. So if you call several times lua_genpcall
with the same script string, it is compiled only the first time. All successive calls will reuse the cached version. The cache table is not a weak table, to avoid having to recompile several times the same script, because of garbage collection going on. A drawback if that the cache may grow indefinitely when the script chunks can change arbitrary at runtime. This can for example happen on a server interpreter executing Lua chunks coming from a client program. But you can explicitly clear the cache by specifying it on the format string.
As with printf
and even more with scanf
, you must be very careful with the types of the arguments and the corresponding format specifications. Any mismatch can lead to unexpected results, or even worse, to an application crash.
The general form of the format string is this one:
"[ Directives < ] Inputs [ > Outputs ]"
Directives
, Inputs
and Outputs
are strings similar to printf
and scanf
formats, consisting in format items beginning with a percent sign. Directives
and Outputs
are optional parts. If they are present, there must be separated from Inputs
with a < or a > character. Inputs
can also be an empty string.
printf
format specifications, each input or output format item consists of up to 6 fields, as in the next example. Directive items have fewer options and are explained later. Any blank character (space, tabulation, carriage return and line feed) is ignored and may be used to increase clarity in the format string. Other characters are either interpreted as explained in this chapter, or throw a Lua error when invalid.
%#12.4Ls
The flag argument may be one of these characters:
lua_newstate
, which is by default implemented by calling standard realloc
and free
functions). You will need to free
it after use.
The width parameter is used with strings, string lists and arrays. It represents the number of elements or characters of the memory buffer. It can be one of the following forms: the following forms:
strlen
. For a string list, it means the list end up after two consecutive zero characters.
The precision argument is used with numerical types to indicate the size in bytes of the C type. It is important for numerical arrays, and for output values. This is because in both cases, a pointer to a variable and not the value itself it passed. It has one of these forms:
The size modifier argument is an alternative to the numerical precision value, and changes the expected C type of the value. It can be one of these characters. Please refer to size table below for correspondences:
Most importantly, the conversion character specifies the type of data. It must be one of the following characters:
For numerical values, here are the default and modified underlying C types listed in the following table:
(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* (*) ---
Other object values have the following associated C types:
ptr
will receive its address. The pointer function has one of these two prototypes, depending of the data direction:
TYPE
the basic C type as stated in previous 2 tables. Except for 'n' and 'k' conversion characters, the composed types are:
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
at the end of the call
lgencall.c
and one header file lgencall.h
. There is also a testing file testwin.cpp
, which includes all test examples of the next chapter, including Windows header file tchar.h
. Using this utility header, it is possible to write code that compile for both ANSI and Unicode platforms.
The main C file includes ANSI standard files, and the public Lua API header files. Like other standard Lua libraries, no private feature is used, and the file can be compiled in both C and C++ languages. However, it requires the new C99 include file stdint.h
to define fixed size integers. If your compiler does not support this, there are several free versions available on the WWW. [1] [2]
The source file can either be compiled together with the application, or placed inside Lua shared library if you can afford to recompile it.
lgencall.h
are defined 3 compilation macros which are used to customize the library for your platform. Each parameter can either be changed in the file itself, or specified on the compiler's command line. A small explanation for it is present in the header file, listing the possible values. Also, the compilation is affected by the following standard macros: __cplusplus
, INT_MAX
, UINT_MAX
and __STDC_VERSION__
.
"Hello World"
program of course:
lua_genpcall(NULL, "print 'Hello World!'", "%O<");
print
function. Since no %S option is passed, Lua state is freed at the end of the call.
We haven't tested the return value, which is the error message.
Here is the same example, but as a more complete and realistic implementation:
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);
Again the same example, but using only 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. Numbers
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
by Lua). Because in Lua all numbers are stored as double, there is a truncation error in the value of Pi
for the float argument. Please note the difference in behaviour between %d and %u for the value 0xFFFFFFFF
. For the double parameter, it is not necessary to specify %lf instead of %f here, because floating point numbers are always converted to double when passed to a variable argument function in C. Integers smaller than int are also automatically converted to int.
2. Boolean, nil, simple strings and light userdata
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
and false
if compiled in C++ or in C99 languages. The nil parameter %n is only present in the format string, no parameter is associated (it is not printed because function pairs
skips nil values). The string is here supposed to be zero terminated, and the last parameter L
(the Lua state) is just an example of a generic pointer.
3. C functions and call-backs
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
. The second argument is a call-back parameter consisting of the user function pushMessage
and a string. Please note that the call-back function receives a pointer to the argument and not the argument itself!
4. Numerical arrays
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. Advanced strings
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 '\\' ...
), and second for Lua. The first argument is a zero terminated string; the second is a string containing a binary 0, specified by its length. The third argument is a binary array of data, for which the length is passed by argument. And the last argument is a wide character string, which is converted to a UTF-8 string or another form of multi-byte string, depending on how the module was compiled.
6. String lists
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
expects wide character strings. The last list is a wide character version specifying its length with an addition '*' parameter.
1. Numbers
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
and var3
are signed integers, and the last 2 are floating point numbers. In this case, the %lf format is mandatory!
2. Boolean, nil, simple strings and light userdata
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'
string is received through Lua stack with %+s idiom. And the last result value, a userdatum value, is got by address into a generic pointer.
3. C functions and call-backs
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
program. The first return value is a pointer to a Lua registered C function, the global print
function. The second value, a simple string, is retrieved through a callback function, which receives as its ptr
argument the address of variable msg
. Then we can print the message by pushing the message onto Lua stack and call the print
function directly by C (which is certainly not a good practice in normal situations).
4. Numerical arrays
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
must be initialized to the size of the array before the call, since we are using a C stack allocated buffer. Because the buffer is larger than the returned array, its last two values will remain not initialized.
5. Advanced strings
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. String lists
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
is just here to display the retrieved string lists in a readable form. The first string list is of type char*, whether or not the Unicode version is used, which is not the case for str2
and str3
. The first list is allocated on Lua stack; the second one also, with in addition the retrieval of the string list length (counted without the last zero byte). The buffer of str3
is on the C stack; so its size is passed as an additional parameter instructed by the '*' flag. The last string list is a wide character version; its buffer is allocated with Lua allocator, requiring a call to free
after use.
You have certainly noted the strong similarities between string and string list parameters.
-- PatrickRapin