diff --git a/CHANGES.current b/CHANGES.current index 6f0b92c43..ad8321e73 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -1,6 +1,11 @@ Version 1.3.35 (in progress) ============================ +03/06/2008: mgossage + [Lua] Updated documentation for Lua exceptions. + Added Examples/lua/exception and Examples/lua/embed2. + Small updates to the typemaps. + 03/04/2008: wsfulton [Java, C#] Add char *& typemaps. diff --git a/Doc/Manual/Lua.html b/Doc/Manual/Lua.html index 3f56be1fc..91a44d657 100644 --- a/Doc/Manual/Lua.html +++ b/Doc/Manual/Lua.html @@ -33,13 +33,14 @@
-If a variable is marked with the %immutable directive then any attempts to set this variable will cause an lua error. Given a global variable: +If a variable is marked with the %immutable directive then any attempts to set this variable will cause an Lua error. Given a global variable:
%module example
@@ -331,7 +332,7 @@ stack traceback:
[C]: ?
-For those people who would rather that SWIG silently ignore the setting of immutables (as previous versions of the lua bindings did), adding a -DSWIGLUA_IGNORE_SET_IMMUTABLE compile option will remove this. +For those people who would rather that SWIG silently ignore the setting of immutables (as previous versions of the Lua bindings did), adding a -DSWIGLUA_IGNORE_SET_IMMUTABLE compile option will remove this.
Unlike earlier versions of the binding, it is now possible to add new functions or variables to the module, just as if it were a normal table. This also allows the user to rename/remove existing functions and constants (but not linked variables, mutable or immutable). Therefore users are recommended to be careful when doing so.
@@ -949,13 +950,133 @@ If you ever need to access the underlying pointer returned by operator->(
> f = p:__deref__() -- Returns underlying Foo *
-22.3.15 Writing your own custom wrappers
+22.3.15 C++ Exceptions
+
+Lua does not natively support exceptions, but it has errors which are similar. When a Lua function terminates with an error +it returns one value back to the caller. SWIG automatically maps any basic type which is thrown into a Lua error. +Therefore for a function: +
+
+int message() throw(const char *) {
+ throw("I died.");
+ return 1;
+}
++SWIG will automatically convert this to a Lua error. +
++> message() +I died. +stack traceback: + [C]: in function 'message' + stdin:1: in main chunk + [C]: ? +> +
-Sometimes, it may be neccesary to add your own special functions, which bypass the normal SWIG wrappering method, and just use the native lua-c API calls. These 'native' functions allow direct adding of your own code into the module. This is performed with the %native directive as follows: +And similarly for numeric types, enums, chars, char*'s and std::string's.
-%native(my_func) int native_function(lua_State*L); // registers it with SWIG ++However its not so simple for objects. Thrown objects are not valid outside the 'catch' block. Therefore they cannot be +returned to the interpreter. +The obvious ways to overcome this would be to either return a copy of the object, or so convert the object to a string and +return that. Though it seems obvious to perform the former, in some cases this is not possible, most notably when +SWIG has no information about the object, or the object is not copyable/creatable. +
++Therefore by default SWIG converts all thrown object into strings and returns them. So given a function: +
+ +++void throw_A() throw(A*) { + throw new A(); +} ++SWIG will just convert it (poorly) to a string and use that as its error. (Yes its not that useful, but it always works). +
+ +++> throw_A() +object exception:A * +stack traceback: + [C]: in function 'unknown' + stdin:1: in main chunk + [C]: ? +> ++To get a more useful behaviour out of SWIG you must either: provide a way to convert your exceptions into strings, or +only throw objects which can be copied. +
++SWIG has typemaps for std::exception and its children already written, so a function which throws any of these will +automatically have its exception converted into an error string. +
++If you have your own class which you want output as a string you will need to add a typemap something like this: +
+++%typemap(throws) my_except +%{ + lua_pushstring(L,$1.what()); // assuming what() returns a const char* message + SWIG_fail; // trigger the error handler +%} ++If you wish your exception to be returned to the interpreter, it must firstly be copyable. Then you must have and additional +%apply statement, to inform SWIG to return a copy of this object to the interpreter. For example: +
+++%apply SWIGTYPE EXCEPTION_BY_VAL {Exc}; // tell SWIG to return Exc by value to interpreter + +class Exc { +public: + Exc(int c, const char *m) { + code = c; + strncpy(msg,m,256); + } + int code; + char msg[256]; +}; + +void throw_exc() throw(Exc) { + throw(Exc(42,"Hosed")); +} ++Then the following code can be used (note: we use pcall to catch the error so we can process the exception). +
+++> ok,res=pcall(throw_exc) +> print(ok) +false +> print(res) +userdata: 0003D880 +> print(res.code,res.msg) +42 Hosed +> ++Note: is is also possible (though tedious) to have a function throw several different kinds of exceptions. To process this +will require a pcall, followed by a set of if statements checking the type of the error. +
++All of this code assumes that your C++ code uses exception specification (which a lot doesn't). +If it doesn't consult the "Exception handling with %catches" section +and the "Exception handling with %exception" section, for more details on how to +add exception specification to functions or globally (respectively). +
+ + +22.3.16 Writing your own custom wrappers
++Sometimes, it may be neccesary to add your own special functions, which bypass the normal SWIG wrappering method, and just use the native Lua API calls. These 'native' functions allow direct adding of your own code into the module. This is performed with the %native directive as follows: +
+%native(my_func) int native_function(lua_State*L); // registers native_function() with SWIG ... %{ int native_function(lua_State*L) // my native code @@ -968,7 +1089,7 @@ int native_function(lua_State*L) // my native code The %native directive in the above example, tells SWIG that there is a function int native_function(lua_State*L); which is to be added into the module under the name 'my_func'. SWIG will not add any wrappering for this function, beyond adding it into the function table. How you write your code is entirely up to you. -22.4 Details on the Lua binding
+22.4 Details on the Lua binding
@@ -979,7 +1100,7 @@ The %native directive in the above example, tells SWIG that there is a
-22.4.1 Binding global data into the module.
+22.4.1 Binding global data into the module.
@@ -995,7 +1116,7 @@ SWIG will effectively generate the pair of functions double Foo_get();
-At initialisation time, it will then add to the interpreter a table called 'example', which represents the module. It will then add all its functions to the module. (Note: older versions of SWIG actually added the Foo_set() and Foo_get() functions, current implementation does not add these functions and more.) But it also adds a metatable to this table, which has two functions (__index and __newindex) as well as two tables (.get and .set) The following Lua code will show these hidden features. +At initialisation time, it will then add to the interpreter a table called 'example', which represents the module. It will then add all its functions to the module. (Note: older versions of SWIG actually added the Foo_set() and Foo_get() functions, current implementation does not add these functions any more.) But it also adds a metatable to this table, which has two functions (__index and __newindex) as well as two tables (.get and .set) The following Lua code will show these hidden features.
> print(example) @@ -1039,7 +1160,7 @@ endThat way when you call 'a=example.Foo', the interpreter looks at the table 'example' sees that there is no field 'Foo' and calls __index. This will in turn check in '.get' table and find the existence of 'Foo' and then return the value of the C function call 'Foo_get()'. Similarly for the code 'example.Foo=10', the interpreter will check the table, then call the __newindex which will then check the '.set' table and call the C function 'Foo_set(10)'.
-22.4.2 Userdata and Metatables
+22.4.2 Userdata and Metatables
@@ -1119,7 +1240,7 @@ Note: Both the opaque structures (like the FILE*) and normal wrappered classes/s
Note: Operator overloads are basically done in the same way, by adding functions such as '__add' & '__call' to the classes metatable. The current implementation is a bit rough as it will add any member function beginning with '__' into the metatable too, assuming its an operator overload.
-22.4.3 Memory management
+22.4.3 Memory management
diff --git a/Examples/lua/check.list b/Examples/lua/check.list index f61e2544f..d28b1342a 100644 --- a/Examples/lua/check.list +++ b/Examples/lua/check.list @@ -3,6 +3,8 @@ class constants dual embed +embed2 +exception funcptr3 functest functor diff --git a/Examples/lua/embed/embed.c b/Examples/lua/embed/embed.c index 2f8d6555e..55ea099be 100644 --- a/Examples/lua/embed/embed.c +++ b/Examples/lua/embed/embed.c @@ -1,85 +1,85 @@ -/* embed.c a simple test for an embeded interpreter - -The idea is that we wrapper a few simple function (example.c) -and write our own app to call it. - -What it will do is load the wrappered lib, load runme.lua and then call some functions. -To make life easier, all the printf's have either [C] or [Lua] at the start -so you can see where they are coming from. - -We will be using the luaL_dostring()/lua_dostring() function to call into lua - -*/ - -#include
-#include - -#include -#include -#include - -/* the SWIG wrappered library */ -extern int luaopen_example(lua_State*L); - -/* a really simple way of calling lua from C - just give it a lua state & a string to execute -Unfortunately lua keeps changing its API's. -In lua 5.0.X its lua_dostring() -In lua 5.1.X its luaL_dostring() -so we have a few extra compiles -*/ -int dostring(lua_State *L, char* str) -{ - int ok; -#if (defined(LUA_VERSION_NUM) && (LUA_VERSION_NUM>=501)) - ok=luaL_dostring(L,str); /* looks like this is lua 5.1.X or later, good */ -#else - ok=lua_dostring(L,str); /* might be lua 5.0.x, using lua_dostring */ -#endif - if (ok!=0) - printf("[C] ERROR in dostring: %s\n",lua_tostring(L,-1)); - return ok; -} - - -int main(int argc,char* argv[]) -{ - lua_State *L; - int ok; - printf("[C] Welcome to the simple embedded lua example\n"); - printf("[C] We are in C\n"); - printf("[C] opening a lua state & loading the libraries\n"); - L=lua_open(); - luaopen_base(L); - luaopen_string(L); - luaopen_math(L); - printf("[C] now loading the SWIG wrappered library\n"); - luaopen_example(L); - printf("[C] all looks ok\n"); - printf("\n"); - printf("[C] lets load the file 'runme.lua'\n"); - printf("[C] any lua code in this file will be executed\n"); - if (luaL_loadfile(L, "runme.lua") || lua_pcall(L, 0, 0, 0)) - { - printf("[C] ERROR: cannot run lua file: %s",lua_tostring(L, -1)); - exit(3); - } - printf("[C] We are now back in C, all looks ok\n"); - printf("\n"); - printf("[C] lets call the function 'do_tests()'\n"); - ok=dostring(L,"do_tests()"); - printf("[C] We are back in C, the dostring() function returned %d\n",ok); - printf("\n"); - printf("[C] Lets call lua again, but create an error\n"); - ok=dostring(L,"no_such_function()"); - printf("[C] We are back in C, the dostring() function returned %d\n",ok); - printf("[C] it should also have returned 1 and printed an error message\n"); - printf("\n"); - printf("[C] Lets call lua again, calling the greeting function\n"); - ok=dostring(L,"call_greeting()"); - printf("[C] This was C=>Lua=>C (getting a bit complex)\n"); - printf("\n"); - printf("[C] all finished, closing the lua state\n"); - lua_close(L); - return 0; -} +/* embed.c a simple test for an embeded interpreter + +The idea is that we wrapper a few simple function (example.c) +and write our own app to call it. + +What it will do is load the wrappered lib, load runme.lua and then call some functions. +To make life easier, all the printf's have either [C] or [Lua] at the start +so you can see where they are coming from. + +We will be using the luaL_dostring()/lua_dostring() function to call into lua + +*/ + +#include +#include + +#include +#include +#include + +/* the SWIG wrappered library */ +extern int luaopen_example(lua_State*L); + +/* a really simple way of calling lua from C + just give it a lua state & a string to execute +Unfortunately lua keeps changing its API's. +In lua 5.0.X its lua_dostring() +In lua 5.1.X its luaL_dostring() +so we have a few extra compiles +*/ +int dostring(lua_State *L, char* str) { + int ok; +#if (defined(LUA_VERSION_NUM) && (LUA_VERSION_NUM>=501)) + + ok=luaL_dostring(L,str); /* looks like this is lua 5.1.X or later, good */ +#else + + ok=lua_dostring(L,str); /* might be lua 5.0.x, using lua_dostring */ +#endif + + if (ok!=0) + printf("[C] ERROR in dostring: %s\n",lua_tostring(L,-1)); + return ok; +} + + +int main(int argc,char* argv[]) { + lua_State *L; + int ok; + printf("[C] Welcome to the simple embedded lua example\n"); + printf("[C] We are in C\n"); + printf("[C] opening a lua state & loading the libraries\n"); + L=lua_open(); + luaopen_base(L); + luaopen_string(L); + luaopen_math(L); + printf("[C] now loading the SWIG wrappered library\n"); + luaopen_example(L); + printf("[C] all looks ok\n"); + printf("\n"); + printf("[C] lets load the file 'runme.lua'\n"); + printf("[C] any lua code in this file will be executed\n"); + if (luaL_loadfile(L, "runme.lua") || lua_pcall(L, 0, 0, 0)) { + printf("[C] ERROR: cannot run lua file: %s",lua_tostring(L, -1)); + exit(3); + } + printf("[C] We are now back in C, all looks ok\n"); + printf("\n"); + printf("[C] lets call the function 'do_tests()'\n"); + ok=dostring(L,"do_tests()"); + printf("[C] We are back in C, the dostring() function returned %d\n",ok); + printf("\n"); + printf("[C] Lets call lua again, but create an error\n"); + ok=dostring(L,"no_such_function()"); + printf("[C] We are back in C, the dostring() function returned %d\n",ok); + printf("[C] it should also have returned 1 and printed an error message\n"); + printf("\n"); + printf("[C] Lets call lua again, calling the greeting function\n"); + ok=dostring(L,"call_greeting()"); + printf("[C] This was C=>Lua=>C (getting a bit complex)\n"); + printf("\n"); + printf("[C] all finished, closing the lua state\n"); + lua_close(L); + return 0; +} diff --git a/Examples/lua/embed/example.c b/Examples/lua/embed/example.c index 9ca71ee04..c6c6d7ba1 100644 --- a/Examples/lua/embed/example.c +++ b/Examples/lua/embed/example.c @@ -1,23 +1,22 @@ -/* File : example.c */ - -#include - -/* A global variable */ -double Foo = 3.0; - -/* Compute the greatest common divisor of positive integers */ -int gcd(int x, int y) { - int g; - g = y; - while (x > 0) { - g = x; - x = y % x; - y = g; - } - return g; -} - -void greeting() -{ - printf("Hello from the C function 'greeting'\n"); -} +/* File : example.c */ + +#include + +/* A global variable */ +double Foo = 3.0; + +/* Compute the greatest common divisor of positive integers */ +int gcd(int x, int y) { + int g; + g = y; + while (x > 0) { + g = x; + x = y % x; + y = g; + } + return g; +} + +void greeting() { + printf("Hello from the C function 'greeting'\n"); +} diff --git a/Examples/lua/embed2/Makefile b/Examples/lua/embed2/Makefile new file mode 100644 index 000000000..5f267d94d --- /dev/null +++ b/Examples/lua/embed2/Makefile @@ -0,0 +1,18 @@ +TOP = ../.. +SWIG = $(TOP)/../preinst-swig +TARGET = embed2 +SRCS = example.c +INTERFACE = example.i +LUA_INTERP = embed2.c + +# this is a little different to normal as we have our own special interpreter +# which we want to static link +all:: + $(MAKE) -f $(TOP)/Makefile $(SWIGLIB) SRCS='$(SRCS)' SWIG='$(SWIG)' \ + SWIGOPT='$(SWIGOPT)' TARGET='$(TARGET)' INTERFACE='example.i' LUA_INTERP='$(LUA_INTERP)' lua_static + +clean:: + $(MAKE) -f $(TOP)/Makefile lua_clean + +check: all + diff --git a/Examples/lua/embed2/embed2.c b/Examples/lua/embed2/embed2.c new file mode 100644 index 000000000..12c81735e --- /dev/null +++ b/Examples/lua/embed2/embed2.c @@ -0,0 +1,221 @@ +/* embed2.c some more test for an embeded interpreter + +This will go a bit further as it will pass values to and from the lua code. +It uses less of the SWIG code, and more of the raw lua API's + +What it will do is load the wrappered lib, load runme.lua and then call some functions. +To make life easier, all the printf's have either [C] or [Lua] at the start +so you can see where they are coming from. + +We will be using the luaL_dostring()/lua_dostring() function to call into lua + +*/ + +#include +#include + +#include +#include +#include +#include + + +/* the SWIG wrappered library */ +extern int luaopen_example(lua_State*L); + +/* This is an example of how to call the Lua function + int add(int,int) + its very tedious, but gives you an idea of the issues involded. + (look below for a better idea) +*/ +int call_add(lua_State *L,int a,int b,int* res) { + int top; + /* ok, here we go: + push a, push b, call 'add' check & return res + */ + top=lua_gettop(L); /* for later */ + lua_pushstring(L, "add"); /* function name */ + lua_gettable(L, LUA_GLOBALSINDEX); /* function to be called */ + if (!lua_isfunction(L,-1)) { + printf("[C] error: cannot find function 'add'\n"); + lua_settop(L,top); // reset + return 0; + } + lua_pushnumber(L,a); + lua_pushnumber(L,b); + if (lua_pcall(L, 2, 1, 0) != 0) /* call function with 2 arguments and 1 result */ + { + printf("[C] error running function `add': %s\n",lua_tostring(L, -1)); + lua_settop(L,top); // reset + return 0; + } + // check results + if (!lua_isnumber(L,-1)) { + printf("[C] error: returned value is not a number\n"); + lua_settop(L,top); // reset + return 0; + } + *res=(int)lua_tonumber(L,-1); + lua_settop(L,top); /* reset stack */ + return 1; // ok +} + +/* This is a variargs call function for calling from C into Lua. +Original Code from Programming in Lua (PIL) by Roberto Ierusalimschy +ISBN 85-903798-1-7 +http://www.lua.org/pil/25.3.html +This has been modified slightly to make it compile, and its still a bit rough. +But it gives the idea of how to make it work. +*/ +int call_va (lua_State *L,const char *func, const char *sig, ...) { + va_list vl; + int narg, nres; /* number of arguments and results */ + int top; + top=lua_gettop(L); /* for later */ + + va_start(vl, sig); + lua_getglobal(L, func); /* get function */ + + /* push arguments */ + narg = 0; + while (*sig) { /* push arguments */ + switch (*sig++) { + + case 'd': /* double argument */ + lua_pushnumber(L, va_arg(vl, double)); + break; + + case 'i': /* int argument */ + lua_pushnumber(L, va_arg(vl, int)); + break; + + case 's': /* string argument */ + lua_pushstring(L, va_arg(vl, char *)); + break; + + case '>': + goto endwhile; + + default: + printf("invalid option (%c)\n", *(sig - 1)); + goto fail; + } + narg++; + /* do we need this?*/ + /* luaL_checkstack(L, 1, "too many arguments"); */ + } +endwhile: + + /* do the call */ + nres = strlen(sig); /* number of expected results */ + if (lua_pcall(L, narg, nres, 0) != 0) /* do the call */ + { + printf("error running function `%s': %s\n",func, lua_tostring(L, -1)); + goto fail; + } + + /* retrieve results */ + nres = -nres; /* stack index of first result */ + while (*sig) { /* get results */ + switch (*sig++) { + + case 'd': /* double result */ + if (!lua_isnumber(L, nres)) { + printf("wrong result type\n"); + goto fail; + } + *va_arg(vl, double *) = lua_tonumber(L, nres); + break; + + case 'i': /* int result */ + if (!lua_isnumber(L, nres)) { + printf("wrong result type\n"); + goto fail; + } + *va_arg(vl, int *) = (int)lua_tonumber(L, nres); + break; + + case 's': /* string result */ + if (!lua_isstring(L, nres)) { + printf("wrong result type\n"); + goto fail; + } + strcpy(va_arg(vl, char *),lua_tostring(L, nres));/* WARNING possible buffer overflow */ + break; + + default: { + printf("invalid option (%c)", *(sig - 1)); + goto fail; + } + } + nres++; + } + va_end(vl); + + lua_settop(L,top); /* reset stack */ + return 1; /* ok */ +fail: + lua_settop(L,top); /* reset stack */ + return 0; /* error */ +} + +int main(int argc,char* argv[]) { + lua_State *L; + int ok; + int res; + char str[80]; + printf("[C] Welcome to the simple embedded Lua example\n"); + printf("[C] We are in C\n"); + printf("[C] opening a Lua state & loading the libraries\n"); + L=lua_open(); + luaopen_base(L); + luaopen_string(L); + luaopen_math(L); + printf("[C] now loading the SWIG wrappered library\n"); + luaopen_example(L); + printf("[C] all looks ok\n"); + printf("\n"); + printf("[C] lets load the file 'runme.lua'\n"); + printf("[C] any lua code in this file will be executed\n"); + if (luaL_loadfile(L, "runme.lua") || lua_pcall(L, 0, 0, 0)) { + printf("[C] ERROR: cannot run lua file: %s",lua_tostring(L, -1)); + exit(3); + } + printf("[C] We are now back in C, all looks ok\n"); + printf("\n"); + printf("[C] lets call the Lua function 'add(1,1)'\n"); + printf("[C] using the C function 'call_add'\n"); + ok=call_add(L,1,1,&res); + printf("[C] the function returned %d with value %d\n",ok,res); + printf("\n"); + printf("[C] lets do this rather easier\n"); + printf("[C] we will call the same Lua function using a generic C function 'call_va'\n"); + ok=call_va(L,"add","ii>i",1,1,&res); + printf("[C] the function returned %d with value %d\n",ok,res); + printf("\n"); + printf("[C] we will now use the same generic C function to call 'append(\"cat\",\"dog\")'\n"); + ok=call_va(L,"append","ss>s","cat","dog",str); + printf("[C] the function returned %d with value %s\n",ok,str); + printf("\n"); + printf("[C] we can also make some bad calls to ensure the code doesn't fail\n"); + printf("[C] calling adds(1,2)\n"); + ok=call_va(L,"adds","ii>i",1,2,&res); + printf("[C] the function returned %d with value %d\n",ok,res); + printf("[C] calling add(1,'fred')\n"); + ok=call_va(L,"add","is>i",1,"fred",&res); + printf("[C] the function returned %d with value %d\n",ok,res); + printf("\n"); + printf("[C] Note: no protection if you mess up the va-args, this is C\n"); + printf("\n"); + printf("[C] Finally we will call the wrappered gcd function gdc(6,9):\n"); + printf("[C] This will pass the values to Lua, then call the wrappered function\n"); + printf(" Which will get the values from Lua, call the C code \n"); + printf(" and return the value to Lua and eventually back to C\n"); + printf("[C] Certainly not the best way to do it :-)\n"); + ok=call_va(L,"gcd","ii>i",6,9,&res); + printf("[C] the function returned %d with value %d\n",ok,res); + printf("\n"); + printf("[C] all finished, closing the lua state\n"); + lua_close(L); + return 0; +} diff --git a/Examples/lua/embed2/example.c b/Examples/lua/embed2/example.c new file mode 100644 index 000000000..c6c6d7ba1 --- /dev/null +++ b/Examples/lua/embed2/example.c @@ -0,0 +1,22 @@ +/* File : example.c */ + +#include + +/* A global variable */ +double Foo = 3.0; + +/* Compute the greatest common divisor of positive integers */ +int gcd(int x, int y) { + int g; + g = y; + while (x > 0) { + g = x; + x = y % x; + y = g; + } + return g; +} + +void greeting() { + printf("Hello from the C function 'greeting'\n"); +} diff --git a/Examples/lua/embed2/example.i b/Examples/lua/embed2/example.i new file mode 100644 index 000000000..7784b8e3c --- /dev/null +++ b/Examples/lua/embed2/example.i @@ -0,0 +1,8 @@ +/* File : example.i */ +%module example + +%inline %{ +extern int gcd(int x, int y); +extern double Foo; +extern void greeting(); +%} \ No newline at end of file diff --git a/Examples/lua/embed2/runme.lua b/Examples/lua/embed2/runme.lua new file mode 100644 index 000000000..73af9c5f2 --- /dev/null +++ b/Examples/lua/embed2/runme.lua @@ -0,0 +1,27 @@ +print "[lua] This is runme.lua" +-- test program for embeded lua +-- we do not need to load the library, as it was already in the intrepreter +-- but lets check anyway +assert(type(example)=='table',"Don't appear to have loaded the example module") + +-- note: we will copy the functions from example table into global +-- this will help us later +for k,v in pairs(example) do _G[k]=v end + +-- our add function +-- we will be calling this from C +function add(a,b) + print("[lua] this is function add(",a,b,")") + c=a+b + print("[lua] returning",c) + return c +end + +function append(a,b) + print("[lua] this is function append(",a,b,")") + c=a..b + print("[lua] returning",c) + return c +end + + diff --git a/Examples/lua/exception/Makefile b/Examples/lua/exception/Makefile new file mode 100644 index 000000000..8657f1922 --- /dev/null +++ b/Examples/lua/exception/Makefile @@ -0,0 +1,19 @@ +TOP = ../.. +SWIG = $(TOP)/../preinst-swig +CXXSRCS = +TARGET = example +INTERFACE = example.i +LIBS = -lm + +all:: + $(MAKE) -f $(TOP)/Makefile CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \ + TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' lua_cpp + +static:: + $(MAKE) -f $(TOP)/Makefile CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \ + TARGET='mylua' INTERFACE='$(INTERFACE)' lua_cpp_static + +clean:: + $(MAKE) -f $(TOP)/Makefile lua_clean + +check: all diff --git a/Examples/lua/exception/example.h b/Examples/lua/exception/example.h new file mode 100644 index 000000000..5148a5962 --- /dev/null +++ b/Examples/lua/exception/example.h @@ -0,0 +1,53 @@ +/* File : example.h */ + +#include +#ifndef SWIG +struct A { +}; +#endif + +class Exc { +public: + Exc(int c, const char *m) { + code = c; + strncpy(msg,m,256); + } + int code; + char msg[256]; +}; + +#if defined(_MSC_VER) + #pragma warning(disable: 4290) // C++ exception specification ignored except to indicate a function is not __declspec(nothrow) +#endif + +class Test { +public: + int simple() throw(int&) { + throw(37); + return 1; + } + int message() throw(const char *) { + throw("I died."); + return 1; + } + int hosed() throw(Exc) { + throw(Exc(42,"Hosed")); + return 1; + } + int unknown() throw(A*) { + static A a; + throw &a; + return 1; + } + int multi(int x) throw(int, const char *, Exc) { + if (x == 1) throw(37); + if (x == 2) throw("Bleah!"); + if (x == 3) throw(Exc(42,"No-go-diggy-die")); + return 1; + } +}; + +#if defined(_MSC_VER) + #pragma warning(default: 4290) // C++ exception specification ignored except to indicate a function is not __declspec(nothrow) +#endif + diff --git a/Examples/lua/exception/example.i b/Examples/lua/exception/example.i new file mode 100644 index 000000000..09cd9e812 --- /dev/null +++ b/Examples/lua/exception/example.i @@ -0,0 +1,17 @@ +/* File : example.i */ +%module example + +%{ +#include "example.h" +%} + +%include "std_string.i" + +// we want to return Exc objects to the interpreter +// therefore we add this typemap +// note: only works if Exc is copyable +%apply SWIGTYPE EXCEPTION_BY_VAL {Exc}; + +/* Let's just grab the original header file here */ +%include "example.h" + diff --git a/Examples/lua/exception/runme.lua b/Examples/lua/exception/runme.lua new file mode 100644 index 000000000..63299888e --- /dev/null +++ b/Examples/lua/exception/runme.lua @@ -0,0 +1,63 @@ +-- file: example.lua + +---- importing ---- +if string.sub(_VERSION,1,7)=='Lua 5.0' then + -- lua5.0 doesnt have a nice way to do this + lib=loadlib('example.dll','luaopen_example') or loadlib('example.so','luaopen_example') + assert(lib)() +else + -- lua 5.1 does + require('example') +end + +-- throw a lot of exceptions: +-- you must catch exceptions using pcall and then checking the result + +t = example.Test() + +print "calling t:unknown()" +ok,res=pcall(function() t:unknown() end) +if ok then + print " that worked! Funny" +else + print(" call failed with error:",res) +end + +print "calling t:simple()" +ok,res=pcall(function() t:simple() end) +if ok then + print " that worked! Funny" +else + print(" call failed with error:",res) +end + +print "calling t:message()" +ok,res=pcall(function() t:message() end) +if ok then + print " that worked! Funny" +else + print(" call failed with error:",res) +end + +print "calling t:hosed()" +ok,res=pcall(function() t:hosed() end) +if ok then + print " that worked! Funny" +else + print(" call failed with error:",res.code,res.msg) +end + +-- this is a rather strange way to perform the multiple catch of exceptions +print "calling t:mutli()" +for i=1,3 do + ok,res=pcall(function() t:multi(i) end) + if ok then + print " that worked! Funny" + else + if swig_type(res)=="Exc *" then + print(" call failed with Exc exception:",res.code,res.msg) + else + print(" call failed with error:",res) + end + end +end diff --git a/Lib/lua/lua.swg b/Lib/lua/lua.swg index 59687cc5a..65e95503e 100644 --- a/Lib/lua/lua.swg +++ b/Lib/lua/lua.swg @@ -15,6 +15,7 @@ %include /* The typemaps */ %include /* The runtime stuff */ +//%include /* ----------------------------------------------------------------------------- * constants typemaps * ----------------------------------------------------------------------------- */ @@ -154,51 +155,75 @@ Therefore currently its just enough to get a few test cases running ok note: if you wish to throw anything related to std::exception use %include instead */ + +// number as number+error %typemap(throws) int,unsigned int,signed int, long,unsigned long,signed long, short,unsigned short,signed short, - bool,float,double, + float,double, long long,unsigned long long, - char, unsigned char, signed char, - enum SWIGTYPE -%{lua_pushfstring(L,"numeric exception:%f",(double)$1);SWIG_fail; %} + unsigned char, signed char, + int&,unsigned int&,signed int&, + long&,unsigned long&,signed long&, + short&,unsigned short&,signed short&, + float&,double&, + long long&,unsigned long long&, + unsigned char&, signed char& +%{lua_pushnumber(L,(lua_Number)$1);SWIG_fail; %} + +%typemap(throws) bool,bool& +%{lua_pushboolean(L,(int)($1==true));SWIG_fail; %} + +// enum as number+error +%typemap(throws) enum SWIGTYPE +%{lua_pushnumber(L,(lua_Number)(int)$1);SWIG_fail; %} // strings are just sent as errors %typemap(throws) char*, const char* %{lua_pushstring(L,$1);SWIG_fail;%} +// char is changed to a string +%typemap(throws) char +%{lua_pushfstring(L,"%c",$1);SWIG_fail;%} + /* Throwing object is a serious problem: Assuming some code throws a 'FooBar' There are a few options: - return a pointer to it: but its unclear how long this will last for. - return a copy of it: but not all objects are copyable - (see exception_partial_info in the test suite for a case where you cannot) + (see exception_partial_info in the test suite for a case where you cannot do this) - convert to a string & throw that its not so useful, but it works (this is more lua like). The third option (though not nice) is used For a more useful solution: see std_except for more details */ + +// basic typemap for structs, classes, pointers & references +// convert to string and error %typemap(throws) SWIGTYPE -{ - (void)$1; - lua_pushfstring(L,"object exception:%s",SWIG_TypePrettyName($1_descriptor)); - SWIG_fail; -} - -// old code: fails under exception_partial_info -// if you have a function which throws a FooBar & you want SWIG to throw the actual object -// then use -// %apply SWIGTYPE BY_VAL {FooBar}; -%typemap(throws) SWIGTYPE BY_VAL -{ - SWIG_NewPointerObj(L,(void *)new $1_ltype(($1_ltype &) $1),$&1_descriptor,1); - SWIG_fail; -} - +%{(void)$1; /* ignore it */ +lua_pushfstring(L,"object exception:%s",SWIG_TypePrettyName($1_descriptor)); +SWIG_fail;%} +// code to make a copy of the object and return this +// if you have a function which throws a FooBar & you want SWIG to return a copy of the object as its error +// then use one of the below +// %apply SWIGTYPE EXCEPTION_BY_VAL {FooBar}; +// %apply SWIGTYPE& EXCEPTION_BY_VAL {FooBar&}; // note: need & twice +%typemap(throws) SWIGTYPE EXCEPTION_BY_VAL +%{SWIG_NewPointerObj(L,(void *)new $1_ltype(($1_ltype &) $1),$&1_descriptor,1); +SWIG_fail;%} + +// similar for object reference +// note: swig typemaps seem a little confused around here, therefore we use $basetype +%typemap(throws) SWIGTYPE& EXCEPTION_BY_VAL +%{SWIG_NewPointerObj(L,(void *)new $basetype($1),$1_descriptor,1); +SWIG_fail;%} +// note: no support for object pointers +// its not clear how long the pointer is valid for, therefore not supporting it /* ----------------------------------------------------------------------------- * extras diff --git a/Lib/lua/luatypemaps.swg b/Lib/lua/luatypemaps.swg index 51f96b037..9f32fe92b 100644 --- a/Lib/lua/luatypemaps.swg +++ b/Lib/lua/luatypemaps.swg @@ -73,15 +73,15 @@ %{$1 = (lua_toboolean(L, $input)!=0);%} %typemap(out) bool -%{ lua_pushboolean(L,(int)$1); SWIG_arg++;%} +%{ lua_pushboolean(L,(int)($1==true)); SWIG_arg++;%} // for const bool&, SWIG treats this as a const bool* so we must dereference it %typemap(in,checkfn="lua_isboolean") const bool& (bool temp) -%{temp=lua_toboolean(L, $input) ? true : false; +%{temp=(lua_toboolean(L, $input)!=0); $1=&temp;%} %typemap(out) const bool& -%{ lua_pushboolean(L,(int)*$1); SWIG_arg++;%} +%{ lua_pushboolean(L,(int)(*$1==true)); SWIG_arg++;%} // strings (char* and char[]) %typemap(in,checkfn="lua_isstring") const char*, char* diff --git a/Lib/lua/std_except.i b/Lib/lua/std_except.i index 95cbe8e49..ce148ef63 100644 --- a/Lib/lua/std_except.i +++ b/Lib/lua/std_except.i @@ -27,7 +27,7 @@ namespace std }; } -// normally object whihc are thrown are returned to interpreter as errors +// normally object which are thrown are returned to interpreter as errors // (which potentally may have problems if they are not copied) // therefore all classes based upon std::exception are converted to their strings & returned as errors %typemap(throws) std::bad_exception "SWIG_exception(SWIG_RuntimeError, $1.what());" diff --git a/Lib/lua/std_string.i b/Lib/lua/std_string.i index 50e7dae10..fa58f10bb 100644 --- a/Lib/lua/std_string.i +++ b/Lib/lua/std_string.i @@ -62,7 +62,7 @@ Not using: lua_tolstring() as this is only found in Lua 5.1 & not 5.0.2 // for throwing of any kind of string, string ref's and string pointers // we convert all to lua strings -%typemap(throws) std::string,const std::string& +%typemap(throws) std::string,std::string&,const std::string& %{ lua_pushlstring(L,$1.data(),$1.size()); SWIG_fail;%} %typemap(throws) std::string*,const std::string* %{ lua_pushlstring(L,$1->data(),$1->size()); SWIG_fail;%}