[lua] Added a typemap DISOWN for SWIGTYPE* and SWIGTYPE[], and support for %delobject feature.

git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@10326 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
Mark Gossage 2008-03-27 01:58:30 +00:00
commit 11647d87e8
14 changed files with 377 additions and 19 deletions

View file

@ -1,6 +1,10 @@
Version 1.3.35 (in progress)
============================
03/27/2008: mgossage
[Lua] Added a typemap DISOWN for SWIGTYPE* and SWIGTYPE[], and support for %delobject feature.
Added Examples/lua/owner which demonstrates the use of the memory management.
03/26/2008: wsfulton
[Java] Apply patch #1844301 from Monty Taylor to suppress enum constructor
unused warnings.

View file

@ -980,10 +980,32 @@ stack traceback:
</pre></div>
<p>
And similarly for numeric types, enums, chars, char*'s and std::string's.
If you want to catch an exception, you must use either pcall() or xpcall(), which are documented in the Lua manual.
Using xpcall will allow you to obtain additional debug information (such as a stacktrace).
</p>
<div class="targetlang"><pre>
> function a() b() end -- function a() calls function b()
> function b() message() end -- function b() calls C++ function message(), which throws
> ok,res=pcall(a) -- call the function
> print(ok,res)
false I died.
> ok,res=xpcall(a,debug.traceback) -- call the function
> print(ok,res)
false I died.
stack traceback:
[C]: in function 'message'
runme.lua:70: in function 'b'
runme.lua:67: in function &lt;runme.lua:66&gt;
[C]: in function 'xpcall'
runme.lua:95: in main chunk
[C]: ?
</pre></div>
<p>
However its not so simple for objects. Thrown objects are not valid outside the 'catch' block. Therefore they cannot be
SWIG is able to throw numeric types, enums, chars, char*'s and std::string's without problem.
However its not so simple for to throw 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
@ -1013,7 +1035,7 @@ stack traceback:
</pre></div>
<p>
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.
throw objects which can be copied.
</p>
<p>
SWIG has typemaps for std::exception and its children already written, so a function which throws any of these will
@ -1025,7 +1047,7 @@ If you have your own class which you want output as a string you will need to ad
<div class="code"><pre>
%typemap(throws) my_except
%{
lua_pushstring(L,$1.what()); // assuming what() returns a const char* message
lua_pushstring(L,$1.what()); // assuming my_except::what() returns a const char* message
SWIG_fail; // trigger the error handler
%}
</pre></div>
@ -1063,6 +1085,7 @@ userdata: 0003D880
42 Hosed
>
</pre></div>
<p>
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.
@ -1129,7 +1152,7 @@ define a different behaviour should the code fail.
</p>
<p>
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.
See Examples/lua/arrays for an example of this code.
</p>
<H2><a name="Lua_nn25"></a>22.4 Details on the Lua binding</H2>

View file

@ -10,6 +10,7 @@ funcptr3
functest
functor
import
owner
pointer
simple
variables

View file

@ -164,7 +164,7 @@ int main(int argc,char* argv[]) {
int ok;
int res;
char str[80];
printf("[C] Welcome to the simple embedded Lua example\n");
printf("[C] Welcome to the simple embedded Lua example v2\n");
printf("[C] We are in C\n");
printf("[C] opening a Lua state & loading the libraries\n");
L=lua_open();

View file

@ -61,3 +61,36 @@ for i=1,3 do
end
end
end
-- this is a bit crazy, but it shows obtaining of the stacktrace
function a()
b()
end
function b()
t:message()
end
print [[
Now lets call function a()
which calls b()
which calls into C++
which will throw an exception!]]
ok,res=pcall(a)
if ok then
print " that worked! Funny"
else
print(" call failed with error:",res)
end
print "Now lets do the same using xpcall(a,debug.traceback)"
ok,res=xpcall(a,debug.traceback)
if ok then
print " that worked! Funny"
else
print(" call failed with error:",res)
end
print "As you can see, the xpcall gives a nice stacktrace to work with"
ok,res=pcall(a)
print(ok,res)
ok,res=xpcall(a,debug.traceback)
print(ok,res)

View file

@ -19,6 +19,6 @@ for i=0,100 do
a(i) -- Note: function call
b(math.sqrt(i)) -- Note: function call
end
print("int sum 0..100 is",a:result(),"(expected 5050")
print("int sum 0..100 is",a:result(),"(expected 5050)")
print("double sum 0..100 is",b:result(),"(expected ~771.46)")

View file

@ -0,0 +1,19 @@
TOP = ../..
SWIG = $(TOP)/../preinst-swig
CXXSRCS = example.cxx
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

View file

@ -0,0 +1,67 @@
/* File : example.c */
#include "example.h"
#define M_PI 3.14159265358979323846
/* Move the shape to a new location */
void Shape::move(double dx, double dy) {
x += dx;
y += dy;
}
int Shape::nshapes = 0;
double Circle::area(void) {
return M_PI*radius*radius;
}
double Circle::perimeter(void) {
return 2*M_PI*radius;
}
double Square::area(void) {
return width*width;
}
double Square::perimeter(void) {
return 4*width;
}
Circle* createCircle(double w)
{
return new Circle(w);
}
Square* createSquare(double w)
{
return new Square(w);
}
ShapeOwner::ShapeOwner() {printf(" ShapeOwner(%p)\n",this);}
ShapeOwner::~ShapeOwner()
{
printf(" ~ShapeOwner(%p)\n",this);
for(unsigned i=0;i<shapes.size();i++)
delete shapes[i];
}
void ShapeOwner::add(Shape* ptr) // this method takes ownership of the object
{
shapes.push_back(ptr);
}
Shape* ShapeOwner::get(int idx) // this pointer is still owned by the class (assessor)
{
if (idx<0 || idx>=shapes.size())
return NULL;
return shapes[idx];
}
Shape* ShapeOwner::remove(int idx) // this method returns memory which must be deleted
{
if (idx<0 || idx>=shapes.size())
return NULL;
Shape* ptr=shapes[idx];
shapes.erase(shapes.begin()+idx);
return ptr;
}

View file

@ -0,0 +1,54 @@
/* File : example.h */
#include <vector>
class Shape {
public:
Shape() {
nshapes++;
}
virtual ~Shape() {
nshapes--;
};
double x, y;
void move(double dx, double dy);
virtual double area(void) = 0;
virtual double perimeter(void) = 0;
static int nshapes;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) { };
virtual double area(void);
virtual double perimeter(void);
};
class Square : public Shape {
private:
double width;
public:
Square(double w) : width(w) { };
virtual double area(void);
virtual double perimeter(void);
};
Circle* createCircle(double w); // this method creates a new object
Square* createSquare(double w); // this method creates a new object
class ShapeOwner {
private:
std::vector<Shape*> shapes;
ShapeOwner(const ShapeOwner&); // no copying
ShapeOwner& operator=(const ShapeOwner&); // no copying
public:
ShapeOwner();
~ShapeOwner();
void add(Shape* ptr); // this method takes ownership of the object
Shape* get(int idx); // this pointer is still owned by the class (assessor)
Shape* remove(int idx); // this method returns memory which must be deleted
};

View file

@ -0,0 +1,35 @@
/* File : example.i */
%module example
%{
#include "example.h"
%}
// before we grab the header file, we must warn SWIG about some of these functions.
// these functions create data, so must be managed
%newobject createCircle;
%newobject createSquare;
// this method returns as pointer which must be managed
%newobject ShapeOwner::remove;
// you cannot use %delobject on ShapeOwner::add()
// as this disowns the ShapeOwner, not the Shape (oops)
//%delobject ShapeOwner::add(Shape*); DO NOT USE
// either you can use a new function (such as this)
/*%delobject add_Shape;
%inline %{
void add_Shape(Shape* s,ShapeOwner* own){own->add(s);}
%}*/
// or a better solution is a typemap
%apply SWIGTYPE *DISOWN {Shape* ptr};
//%ignore ShapeOwner::add;
// now we can grab the header file
%include "example.h"

View file

@ -0,0 +1,104 @@
-- Operator overloading example
---- 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
print "ok, lets test Lua's ownership of C++ objects"
print("Currently there are",example.Shape_nshapes,"shapes (there should be 0)")
print "\nLets make a couple"
a=example.Square(10)
b=example.Circle(1)
print("Currently there are",example.Shape_nshapes,"shapes (there should be 2)")
print "\nNote lets use the createX functions"
c=example.createCircle(5)
d=example.createSquare(3)
print("Currently there are",example.Shape_nshapes,"shapes (there should be 4)")
print "\nWe will run the garbage collector & see if they are till here"
collectgarbage()
print("Currently there are",example.Shape_nshapes,"shapes (there should be 4)")
print "\nLets get rid of them all, collect garbage & see if they are till here"
a,b,c,d=nil,nil,nil,nil
collectgarbage()
print("Currently there are",example.Shape_nshapes,"shapes (there should be 0)")
print "\nLets start putting stuff into the ShapeOwner"
print "The ShapeOwner now owns the shapes, but Lua still has pointers to them"
o=example.ShapeOwner()
a=example.Square(10)
b=example.Circle(1)
o:add(a)
o:add(b)
o:add(example.createSquare(5))
print("Currently there are",example.Shape_nshapes,"shapes (there should be 3)")
print "\nWe will nil our references,run the garbage collector & see if they are till here"
print "they should be, as the ShapeOwner owns them"
a,b=nil,nil
collectgarbage()
print("Currently there are",example.Shape_nshapes,"shapes (there should be 3)")
print "\nWe will access them and check that they are still valid"
a=o:get(0)
b=o:get(1)
print(" Area's are",a:area(),b:area(),o:get(2):area())
collectgarbage()
print("Currently there are",example.Shape_nshapes,"shapes (there should be 3)")
print "\nWe will remove one from the C++ owner & pass its ownership to Lua,"
print " then check that they are still unchanged"
a,b=nil,nil
a=o:remove(0) -- a now owns it
collectgarbage()
print("Currently there are",example.Shape_nshapes,"shapes (there should be 3)")
print "\nDelete the ShapeOwner (this should destroy two shapes),"
print " but we have one left in Lua"
o=nil
collectgarbage()
print("Currently there are",example.Shape_nshapes,"shapes (there should be 1)")
print "\nFinal tidy up "
a=nil
collectgarbage()
print("Currently there are",example.Shape_nshapes,"shapes (there should be 0)")
print "Final test, we will create some Shapes & pass them around like mad"
print "If there is any memory leak, you will see it in the memory usage"
io.flush()
sh={}
-- make some objects
for i=0,10 do
a=example.Circle(i)
b=example.Square(i)
sh[a]=true
sh[b]=true
end
o=example.ShapeOwner()
for i=0,10000 do
for k,_ in pairs(sh) do
o:add(k)
end
sh={} -- clear it
while true do
a=o:remove(0)
if a==nil then break end
sh[a]=true
end
if i%100==0 then collectgarbage() end
end
print "done"
o,sh=nil,nil
collectgarbage()
print("Currently there are",example.Shape_nshapes,"shapes (there should be 0)")
print "thats all folks!"

View file

@ -600,11 +600,15 @@ SWIGRUNTIME int SWIG_Lua_ConvertPtr(lua_State* L,int index,void** ptr,swig_type
usr=(swig_lua_userdata*)lua_touserdata(L,index); /* get data */
if (usr)
{
if (!type) /* special cast void*, no casting fn */
{
*ptr=usr->ptr;
return SWIG_OK; /* ok */
}
if (flags & SWIG_POINTER_DISOWN) /* must disown the object */
{
usr->own=0;
}
if (!type) /* special cast void*, no casting fn */
{
*ptr=usr->ptr;
return SWIG_OK; /* ok */
}
cast=SWIG_TypeCheckStruct(usr->type,type); /* performs normal type checking */
if (cast)
{

View file

@ -120,14 +120,14 @@
// the checkfn SWIG_isptrtype will work for both userdata and nil's
%typemap(in,checkfn="SWIG_isptrtype") SWIGTYPE*,SWIGTYPE[]
%{
if (!SWIG_IsOK(SWIG_ConvertPtr(L,$input,(void**)&$1,$descriptor,0))){
if (!SWIG_IsOK(SWIG_ConvertPtr(L,$input,(void**)&$1,$descriptor,$disown))){
SWIG_fail_ptr("$symname",$argnum,$descriptor);
}
%}
%typemap(in,checkfn="lua_isuserdata") SWIGTYPE&
%{
if (!SWIG_IsOK(SWIG_ConvertPtr(L,$input,(void**)&$1,$descriptor,0))){
if (!SWIG_IsOK(SWIG_ConvertPtr(L,$input,(void**)&$1,$descriptor,$disown))){
SWIG_fail_ptr("$symname",$argnum,$descriptor);
}
%}
@ -165,6 +165,17 @@
%{temp=($*ltype)SWIG_MustGetPtr(L,$input,$*descriptor,0,$argnum,"$symname");
$1=&temp;%}
// DISOWN-ing typemaps
// if you have an object pointer which must be disowned, use this typemap
// eg. for void destroy_foo(Foo* toDie);
// use %apply SWIGTYPE* DISOWN {Foo* toDie};
// you could just use %delobject, but this is more flexible
%typemap(in,checkfn="SWIG_isptrtype") SWIGTYPE* DISOWN,SWIGTYPE DISOWN[]
%{ if (!SWIG_IsOK(SWIG_ConvertPtr(L,$input,(void**)&$1,$descriptor,SWIG_POINTER_DISOWN))){
SWIG_fail_ptr("$symname",$argnum,$descriptor);
}
%}
// Primitive types--return by value
// must make a new object, copy the data & return the new object

View file

@ -450,7 +450,11 @@ public:
Replaceall(tm, "$target", ln);
Replaceall(tm, "$input", source);
Setattr(p, "emit:input", source);
if (Getattr(p, "wrap:disown") || (Getattr(p, "tmap:in:disown"))) {
Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
} else {
Replaceall(tm, "$disown", "0");
}
/* NEW LANGUAGE NOTE:***********************************************
look for a 'checkfn' typemap
this an additional parameter added to the in typemap
@ -1057,8 +1061,7 @@ public:
* ------------------------------------------------------------ */
virtual int destructorHandler(Node *n) {
//have_destructor = 1;
//return SWIG_NOWRAP;
//REPORT("destructorHandler", n);
current = DESTRUCTOR;
Language::destructorHandler(n);
current = NO_CPP;
@ -1145,12 +1148,12 @@ public:
*/
void EscapeCode(String* str)
{
Printf(f_runtime,"/* original luacode:[[[\n%s\n]]]\n*/\n",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);
//Printf(f_runtime,"/* hacked luacode:[[[\n%s\n]]]\n*/\n",str);
}
};