diff --git a/CHANGES.current b/CHANGES.current index e5697ca7b..a5f75897a 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -1,6 +1,11 @@ Version 1.3.35 (in progress) ============================ +03/17/2008: mgossage + [Lua] Added %luacode feature to add source code into wrappers. + Updated documentation to document this. + Added Examples/lua/arrays to show its use (and typemaps) + 03/17/2008: olly Fix nonportable sed usage which failed on Mac OS X (and probably other platforms). Fixes SF#1903612. diff --git a/Doc/Manual/Lua.html b/Doc/Manual/Lua.html index 91a44d657..35a4d9b12 100644 --- a/Doc/Manual/Lua.html +++ b/Doc/Manual/Lua.html @@ -35,12 +35,13 @@
  • C++ Smart Pointers
  • C++ exceptions
  • Writing your own custom wrappers +
  • Adding additional Lua code -
  • Details on the Lua binding +
  • Details on the Lua binding @@ -1089,7 +1090,43 @@ 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.3.17 Adding additional Lua code

    +

    +As well as adding additional C/C++ code, its also possible to add your own Lua code to the module as well. +This code is executed once all other initialisation, including the %init code has been called. +

    +

    +The directive %luacode adds code into the module which is executed upon loading. Normally you would +use this to add your own functions to the module. Though you could easily perform other tasks. +

    +
    %module example;
    +
    +%luacode {
    +  function example.greet() 
    +    print "hello world" 
    +  end
    +
    +  print "Module loaded ok"
    +}
    +...
    +%}
    +
    +

    +Notice that the code is not part of the module table. Therefore any references to the module must have the +module name added. +

    +

    +Should there be an error in the Lua code, this will not stop loading of the module. +The default behaviour of SWIG is to print a error message to stderr and then continue. +It is possible to change this behaviour by using a #define SWIG_DOSTRING_FAIL(STR) to +define a different behaviour should the code fail. +

    +

    +Good uses for this feature is adding of new code, or writing helper functions to simplify some of the code. +See Examples/lua/arrays, for an example of this code. +

    + +

    22.4 Details on the Lua binding

    @@ -1100,7 +1137,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.

    @@ -1160,7 +1197,7 @@ end

    That 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

    @@ -1240,7 +1277,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/arrays/Makefile b/Examples/lua/arrays/Makefile new file mode 100644 index 000000000..bb9cf0b3b --- /dev/null +++ b/Examples/lua/arrays/Makefile @@ -0,0 +1,18 @@ +TOP = ../.. +SWIG = $(TOP)/../preinst-swig +SRCS = example.c +TARGET = example +INTERFACE = example.i + +all:: + $(MAKE) -f $(TOP)/Makefile SRCS='$(SRCS)' SWIG='$(SWIG)' \ + TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' lua + +static:: + $(MAKE) -f $(TOP)/Makefile SRCS='$(SRCS)' SWIG='$(SWIG)' \ + TARGET='mylua' INTERFACE='$(INTERFACE)' lua_static + +clean:: + $(MAKE) -f $(TOP)/Makefile lua_clean + +check: all diff --git a/Examples/lua/arrays/example.c b/Examples/lua/arrays/example.c new file mode 100644 index 000000000..40ed12c79 --- /dev/null +++ b/Examples/lua/arrays/example.c @@ -0,0 +1,25 @@ +/* File : example.c */ + +#include + +/* we are using the qsort function, which needs a helper function to sort */ +int compare_int(const void * a, const void * b) +{ + return ( *(int*)a - *(int*)b ); +} + +void sort_int(int* arr, int len) +{ + qsort(arr, len, sizeof(int), compare_int); +} + +// ditto doubles +int compare_double(const void * a, const void * b) +{ + return ( *(double*)a - *(double*)b ); +} + +void sort_double(double* arr, int len) +{ + qsort(arr, len, sizeof(double), compare_double); +} diff --git a/Examples/lua/arrays/example.i b/Examples/lua/arrays/example.i new file mode 100644 index 000000000..197a5a21b --- /dev/null +++ b/Examples/lua/arrays/example.i @@ -0,0 +1,42 @@ +/* File : example.i */ +%module example + +/* in this file there are two sorting functions +and three different ways to wrap them. + +See the lua code for how they are called +*/ + +%include // array helpers + +// this declares a batch of function for manipulating C integer arrays +%array_functions(int,int) + +// this adds some lua code directly into the module +// warning: you need the example. prefix if you want it added into the module +// addmittedly this code is a bit tedious, but its a one off effort +%luacode { +function example.sort_int2(t) + local len=table.maxn(t) -- the len + local arr=example.new_int(len) + for i=1,len do + example.int_setitem(arr,i-1,t[i]) -- note: C index is one less then lua indea + end + example.sort_int(arr,len) -- call the fn + -- copy back + for i=1,len do + t[i]=example.int_getitem(arr,i-1) -- note: C index is one less then lua indea + end + example.delete_int(arr) -- must delete it +end +} + +// this way uses the SWIG-Lua typemaps to do the conversion for us +// the %apply command states to apply this wherever the argument signature matches +%include +%apply (double *INOUT,int) {(double* arr,int len)}; + +%inline %{ +extern void sort_int(int* arr, int len); +extern void sort_double(double* arr, int len); +%} \ No newline at end of file diff --git a/Examples/lua/arrays/runme.lua b/Examples/lua/arrays/runme.lua new file mode 100644 index 000000000..b0f5cfc96 --- /dev/null +++ b/Examples/lua/arrays/runme.lua @@ -0,0 +1,74 @@ +---- 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 + +-- a helper to print a Lua table +function print_table(t) + print(table.concat(t,",")) +end + +-- a helper to print a C array +function print_array(arr,len) + for i=0,len-1 do + io.write(example.int_getitem(arr,i),",") + end + io.write("\n") +end + +math.randomseed(0) -- init random + + +--[[ version 1: passing a C array to the code +lets test call sort_int() +this requires a C array, so is the hardest to use]] +ARRAY_SIZE=10 +arr=example.new_int(ARRAY_SIZE) +for i=0,ARRAY_SIZE-1 do + example.int_setitem(arr,i,math.random(1000)) +end +print "unsorted" +print_array(arr,ARRAY_SIZE) +example.sort_int(arr,ARRAY_SIZE) +print "sorted" +print_array(arr,ARRAY_SIZE) +example.delete_int(arr) -- must delete it +print "" + +--[[ version 2: using %luacode to write a helper +a simpler way is to use a %luacode +which is a lua function added into the module +this can do the conversion for us +so we can just add a lua table directly +(what we do is move the lua code into the module instead) +]] +t={} +for i=1,ARRAY_SIZE do + t[i]=math.random(1000) +end +print "unsorted" +print_table(t) +example.sort_int2(t) -- calls lua helper which then calls C +print "sorted" +print_table(t) +print "" + +--[[ version 3: use a typemap +this is the best way +it uses the SWIG-Lua typemaps to do the work +one item of note: the typemap creates a copy, rather than edit-in-place]] +t={} +for i=1,ARRAY_SIZE do + t[i]=math.random(1000)/10 +end +print "unsorted" +print_table(t) +t=example.sort_double(t) -- replace t with the result +print "sorted" +print_table(t) + diff --git a/Examples/lua/check.list b/Examples/lua/check.list index d28b1342a..c8e09c493 100644 --- a/Examples/lua/check.list +++ b/Examples/lua/check.list @@ -1,4 +1,5 @@ # see top-level Makefile.in +arrays class constants dual diff --git a/Lib/lua/lua.swg b/Lib/lua/lua.swg index 65e95503e..b6d888670 100644 --- a/Lib/lua/lua.swg +++ b/Lib/lua/lua.swg @@ -228,5 +228,8 @@ SWIG_fail;%} /* ----------------------------------------------------------------------------- * extras * ----------------------------------------------------------------------------- */ +// this %define is to allow insertion of lua source code into the wrapper file +#define %luacode %insert("luacode") + /* ------------------------------ end lua.swg ------------------------------ */ diff --git a/Lib/lua/luarun.swg b/Lib/lua/luarun.swg index 751e062f0..f042bc6b0 100644 --- a/Lib/lua/luarun.swg +++ b/Lib/lua/luarun.swg @@ -671,12 +671,6 @@ SWIGRUNTIME const char *SWIG_Lua_typename(lua_State *L, int tp) /* lua callable function to get the userdata's type */ SWIGRUNTIME int SWIG_Lua_type(lua_State* L) { -/* swig_lua_userdata* usr; - if (!lua_isuserdata(L,1)) /* just in case */ - /* return 0; /* nil reply */ - /*usr=(swig_lua_userdata*)lua_touserdata(L,1); /* get data */ - /*lua_pushstring(L,usr->type->name); - return 1;*/ lua_pushstring(L,SWIG_Lua_typename(L,1)); return 1; } @@ -698,9 +692,6 @@ SWIGRUNTIME int SWIG_Lua_equal(lua_State* L) return 1; } - - - /* ----------------------------------------------------------------------------- * global variable support code: class/struct typemap functions * ----------------------------------------------------------------------------- */ @@ -747,6 +738,35 @@ SWIG_Lua_InstallConstants(lua_State* L, swig_lua_const_info constants[]) { } } +/* ----------------------------------------------------------------------------- + * executing lua code from within the wrapper + * ----------------------------------------------------------------------------- */ + +#ifndef SWIG_DOSTRING_FAIL /* Allows redefining of error function */ +#define SWIG_DOSTRING_FAIL(S) fprintf(stderr,"%s\n",S) +#endif +/* Executes a C string in Lua a really simple way of calling lua from C +Unfortunately lua keeps changing its API's, so we need a conditional compile +In lua 5.0.X its lua_dostring() +In lua 5.1.X its luaL_dostring() +*/ +SWIGINTERN int +SWIG_Lua_dostring(lua_State *L, const char* str) { + int ok,top; + if (str==0 || str[0]==0) return 0; /* nothing to do */ + top=lua_gettop(L); /* save stack */ +#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) { + SWIG_DOSTRING_FAIL(lua_tostring(L,-1)); + } + lua_settop(L,top); /* restore the stack */ + return ok; +} + #ifdef __cplusplus } #endif diff --git a/Lib/lua/luaruntime.swg b/Lib/lua/luaruntime.swg index 04f1adf89..b82cd56d7 100644 --- a/Lib/lua/luaruntime.swg +++ b/Lib/lua/luaruntime.swg @@ -16,7 +16,7 @@ /* Forward declaration of where the user's %init{} gets inserted */ void SWIG_init_user(lua_State* L ); - + #ifdef __cplusplus extern "C" { #endif diff --git a/Lib/lua/typemaps.i b/Lib/lua/typemaps.i index 284078081..bccfbe120 100644 --- a/Lib/lua/typemaps.i +++ b/Lib/lua/typemaps.i @@ -94,7 +94,7 @@ and quite a few functions defined assuming we have functions void process_array(int arr[3]); // nice fixed size array void process_var_array(float arr[],int len); // variable sized array -void process_var_array_inout(double arr*,int len); // variable sized array +void process_var_array_inout(double* arr,int len); // variable sized array // data passed in & out void process_enum_inout_array_var(enum Days *arrinout, int len); // using enums void return_array_5(int arrout[5]); // out array only diff --git a/Source/Modules/lua.cxx b/Source/Modules/lua.cxx index 4e529bcd7..bb141f926 100644 --- a/Source/Modules/lua.cxx +++ b/Source/Modules/lua.cxx @@ -102,6 +102,7 @@ private: String *s_const_tab; // table of global constants String *s_methods_tab; // table of class methods String *s_attr_tab; // table of class atributes + String *s_luacode; // luacode to be called during init int have_constructor; int have_destructor; @@ -137,7 +138,7 @@ public: f_initbeforefunc = 0; PrefixPlusUnderscore = 0; - s_cmd_tab = s_var_tab = s_const_tab = 0; + s_cmd_tab = s_var_tab = s_const_tab = s_luacode = 0; current=NO_CPP; } @@ -240,6 +241,10 @@ public: s_var_tab = NewString(""); // s_methods_tab = NewString(""); s_const_tab = NewString(""); + + s_luacode = NewString(""); + Swig_register_filebyname("luacode", s_luacode); + current=NO_CPP; /* Standard stuff for the SWIG runtime section */ @@ -256,6 +261,7 @@ public: Printf(f_header, "#define SWIG_name \"%s\"\n", module); Printf(f_header, "#define SWIG_init luaopen_%s\n", module); Printf(f_header, "#define SWIG_init_user luaopen_%s_user\n\n", module); + Printf(f_header, "#define SWIG_LUACODE luaopen_%s_luacode\n\n", module); Printf(s_cmd_tab, "\nstatic const struct luaL_reg swig_commands[] = {\n"); Printf(s_var_tab, "\nstatic swig_lua_var_info swig_variables[] = {\n"); @@ -265,6 +271,7 @@ public: /* %init code inclusion, effectively in the SWIG_init function */ Printf(f_init, "void SWIG_init_user(lua_State* L)\n{\n"); Language::top(n); + Printf(f_init,"/* exec Lua code if applicable */\nSWIG_Lua_dostring(L,SWIG_LUACODE);\n"); Printf(f_init, "}\n"); Printf(f_wrappers, "#ifdef __cplusplus\n}\n#endif\n"); @@ -280,14 +287,18 @@ public: this basically combines several of the strings together and then writes it all to a file NEW LANGUAGE NOTE:END ************************************************/ - /* Close all of the files */ - Delete(s_cmd_tab); - Delete(s_var_tab); - Delete(s_const_tab); Dump(f_header, f_runtime); Dump(f_wrappers, f_runtime); Dump(f_initbeforefunc, f_runtime); + /* for the Lua code it needs to be properly excaped to be added into the C/C++ code */ + EscapeCode(s_luacode); + Printf(f_runtime, "const char* SWIG_LUACODE=\n \"%s\";\n\n",s_luacode); Wrapper_pretty_print(f_init, f_runtime); + /* Close all of the files */ + Delete(s_luacode); + Delete(s_cmd_tab); + Delete(s_var_tab); + Delete(s_const_tab); Delete(f_header); Delete(f_wrappers); Delete(f_init); @@ -1123,6 +1134,24 @@ public: String *defaultExternalRuntimeFilename() { return NewString("swigluarun.h"); } + + /* --------------------------------------------------------------------- + * helpers + * --------------------------------------------------------------------- */ + + /* This is to convert the string of Lua code into a proper string, which can then be + emitted into the C/C++ code. + Basically is is a lot of search & replacing of odd sequences + */ + void EscapeCode(String* str) + { + Printf(f_runtime,"/* original luacode:[[[\n%s\n]]]\n*/\n",str); + Chop(str); // trim + Replace(str,"\\","\\\\",DOH_REPLACE_ANY); // \ to \\ (this must be done first) + Replace(str,"\"","\\\"",DOH_REPLACE_ANY); // " to \" + Replace(str,"\n","\\n\"\n \"",DOH_REPLACE_ANY); // \n to \n"\n" (ie quoting every line) + Printf(f_runtime,"/* hacked luacode:[[[\n%s\n]]]\n*/\n",str); + } }; /* NEW LANGUAGE NOTE:*********************************************** @@ -1146,7 +1175,7 @@ swig_module modules[] = { {"-guile", swig_guile, "Guile"}, {"-java", swig_java, "Java"}, //etc,etc,etc... - {"-Lua", swig_lua, "Lua"}, // this is my code + {"-lua", swig_lua, "Lua"}, // this is my code {NULL, NULL, NULL} // this must come at the end of the list }; ======= end change ==========