Is there any tidy way to handle exceptions during the
cleanup/destruction of arguments to wrapped methods (as opposed to
during the method calls themselves) ?
See attached test.i for a full example, which can be run (at least on my
Linux machine) with something like:
swig -c++ -python test.i && g++ -shared -o _test.so test_wrap.cxx -fPIC
-I/usr/include/python2.5 && echo "import test; test.mytest(1)" | python
In this example, my C++ method expects to get a Foo object which
provides a method do_something(). In my SWIG interface I provide an
implementation of Foo which acts as an adapter, delegating to a wrapped
Python object. (The Foo object is created and destroyed during the
lifetime of the wrapping function, with in and freearg typemaps.) If
do_something() raises an exception, all is OK, because I use %exception
to catch that and raise an equivalent Python exception.
But what if a cleanup action is required? (For example, Foo may buffer
its actions to reduce the number of Python calls - for instance if
do_something() is called millions of times - so a cleanup method is
needed to flush the buffer, possibly called from the destructor.) In the
example cleanup is called by the freearg typemap. This doesn't work
because if cleanup throws an exception, it is not caught (plus freearg
is called by SWIG_fail as well, and we probably shouldn't call cleanup()
if the wrapped method failed).
Ideally, I would like to call cleanup immediately after the method, so
that test_wrap.cxx looks like
{
try {
mytest(arg1);
arg1->cleanup();
} catch (...) {
SWIG_exception(SWIG_RuntimeError, "Error caught");
}
}
Unfortunately, there don't seem to be any typemaps
("cleanup_arg_after_method"?) which get inserted into the $action code
and thus are wrapped by %exception.
I could generate the desired code with %exception or %feature("action")
but unfortunately these match on the method name (mytest) not on the
type of the argument (Foo). So I'd have to write a bunch of these, one
for every method that takes a Foo*, and keep everything in sync.
Right now I handle things by abusing the argout typemap, with something like
%typemap(argout) Foo* {
try {
$1->cleanup();
} catch (...) {
...
Py_DECREF($result);
}
}
This is better than using freearg, because it is only called if the
method succeeds, but it is a bit ugly because the result object has
already been created, so we have to manually free that if the cleanup
fails (plus, if there are other argout arguments before this one, they
may have created other results, and argout arguments after this one
won't be processed, potentially leading to a memory leak).
Any other possible solutions?
Ben
--
ben@... http://salilab.org/~ben/
"It is a capital mistake to theorize before one has data."
- Sir Arthur Conan Doyle