Yeah, -classic worked, but when I did that for some reason I couldn't
call any of my static class methods. So I just used
%feature("classic") FIX::Exception;, which is my base exception class,
and then I could throw all of my exceptions and the static methods
worked fine again. So this is all very good.
I actually have everything technically working as I want it now, but
I'm a little worried I'm doing something not quite right or not the
best way, and am also worried about a possible resource leak.
The last thing I needed to accomplish was to intercept a wrapper
exception thrown from python, get the C++ exception from it, and then
throw it. I'm doing this with %feature("director:except"). From what
I gathered, right now there is no way to get swig to do this
automatically, and there was little documentation on how to do this, so
I've tried to solve this myself and have come up with something that
works. If there is a better more swig-like way to do this, let me
know, but that's not my main issue. First let me show you an example
of some of the generated code. I'll explain what my concern is below
the code sample:
obj0 = SWIG_NewPointerObj(nc_tmp_arg0, SWIGTYPE_p_FIX__Message, 0);
obj1 = SWIG_NewPointerObj(nc_tmp_arg1, SWIGTYPE_p_FIX__SessionID,
0);
result = PyObject_CallMethod(swig_get_self(), "fromApp", "(OO)"
,obj0,obj1);
if (result == NULL) {
PyObject *error = PyErr_Occurred();
{
if( error != NULL ) {
PyObject *ptype, *pvalue, *ptraceback;
PyErr_Fetch( &ptype, &pvalue, &ptraceback );
void *result;
SWIG_ConvertPtr(pvalue, &result,
SWIGTYPE_p_FIX__Exception, 0);
Exception* e = (Exception*)result;
DoNotSend doNotSend;
FieldNotFound fieldNotFound;
IncorrectDataFormat incorrectDataFormat;
IncorrectTagValue incorrectTagValue;
RejectLogon rejectLogon;
UnsupportedMessageType unsupportedMessageType;
DoNotSend* pDoNotSend = dynamic_cast<DoNotSend*>(e);
FieldNotFound* pFieldNotFound =
dynamic_cast<FieldNotFound*>(e);
IncorrectDataFormat* pIncorrectDataFormat =
dynamic_cast<IncorrectDataFormat*>(e);
IncorrectTagValue* pIncorrectTagValue =
dynamic_cast<IncorrectTagValue*>(e);
RejectLogon* pRejectLogon =
dynamic_cast<RejectLogon*>(e);
UnsupportedMessageType* pUnsupportedMessageType =
dynamic_cast<UnsupportedMessageType*>(e);
if( pDoNotSend ) doNotSend = *pDoNotSend;
if( pFieldNotFound ) fieldNotFound = *pFieldNotFound;
if( pIncorrectDataFormat ) incorrectDataFormat =
*pIncorrectDataFormat;
if( pIncorrectTagValue ) incorrectTagValue =
*pIncorrectTagValue;
if( pRejectLogon ) rejectLogon = *pRejectLogon;
if( pUnsupportedMessageType ) unsupportedMessageType =
*pUnsupportedMessageType;
Py_XDECREF(ptype);
Py_XDECREF(pvalue);
Py_XDECREF(ptraceback);
if( pDoNotSend ) throw doNotSend;
if( pFieldNotFound ) throw fieldNotFound;
if( pIncorrectDataFormat ) throw incorrectDataFormat;
if( pIncorrectTagValue ) throw incorrectTagValue;
if( pRejectLogon ) throw rejectLogon;
if( pUnsupportedMessageType ) throw
unsupportedMessageType;
std::cerr << "Unhandled python exception thrown from
callback!" << std::endl;
// print out stack information and exit
}
}
}
Py_XDECREF(obj0);
Py_XDECREF(obj1);
Py_XDECREF(result);
Ok, so as you can see I;m using SWIG_ConvertPtr and casting the result
to my base Exception class. I was trying to figure out how to query
swig for the correct type of the exception, but was not having a lot of
luck, so as you can see I'm using dynamic_casts and relying on RTTI to
figure it out. Not my proudest moment and quite wasteful, but during
normal use exceptions should rarely be thrown so it doesn't bother me
too much. If you a better way to do this please let me know and add it
to the documentation as well (unless it is already somewhere that I
missed?).
In any case my real concern is with the Py_XDECREF's that occur after
my generated code block. If I throw an exception the arguments will
never get decremented and this looks like a resource leak to me. I'm
not really sure what to do about this. In the documentation that you
do have, you appear to be throwing a SwigDirector exception, but make
no mention of how this affects the reference counting on these objects.
Is this something that was overlooked or am I missing something?
--oren
On May 29, 2004, at 5:42 PM, Marcelo Matus wrote:
It seems is a Python feature:
http://mail.python.org/pipermail/python-list/2003-July/175603.html
where no 'object' derived class can be used as an exception.
Therefore, one way to solve the problem is to use the "-classic"
option for your entire application, and avoid the use of 'object'
as the base class for all the shadow classes, or even better,
do it only with the classes you want to use as exception, using
the "classic" feature
%feature("classic") MyException1;
%feature("classic") MyException2;
....
But swig is suppose to capture most of the exception classes,
so, use the "classic" feature in the meantime while I see
if there is something wrong with the director part.
Marcelo
Oren Miller wrote:
Thanks. In the meantime I had changed my throw specifiers and that
made a big difference. All of the catch(...) statements went away
and were replaced with specific catch clauses that raised the python
wrappers for those exceptions. So the throws typemaps weren't
necessary at all. Fantastic. There are just two more issues I'm
trying to resolve before I have all the exception handling perfect,
and I think that will wrap up my python interface (at least enough to
be useful and expose pretty much all the functionality). I'll start
with one of the questions and follow up with the second, thanks for
everyone's help and patience so far.
So when I call a method that throws a FieldNotFound exception, this
code is generated:
catch(FIX::FieldNotFound &_e) {
{
FIX::FieldNotFound * temp = new FIX::FieldNotFound(_e);
if (SWIGTYPE_p_FIX__FieldNotFound->clientdata) {
PyErr_SetObject((PyObject *)
(SWIGTYPE_p_FIX__FieldNotFound->clientdata),
SWIG_NewPointerObj(temp,SWIGTYPE_p_FIX__FieldNotFound,1));
} else {
PyErr_SetString(PyExc_RuntimeError,"FIX::FieldNotFound");
/*
PyErr_SetObject(PyExc_RuntimeError,
SWIG_NewPointerObj(temp,SWIGTYPE_p_FIX__FieldNotFound,1));
*/
}
SWIG_fail;
}
}
Looks fine and seems to work well. When I print out sys.exc_type I
get <class 'quickfix.FieldNotFoundPtr'>, and if I print out
sys.exc_value I get the contents of the what() clause since I
extended the __str__ method to do so. All very good.
The problem is when I try to raise this exception from Python itself.
I will do a raise quickfix.FieldNotFoundPtr( 1 ), or raise
quickfix.FieldNotFound( 1 ) and I get the following TypeError:
"exceptions must be strings, classes, or instances, not
FieldNotFoundPtr"
So is there anything special I need to do to raise one of the
exceptions from Python?
On May 29, 2004, at 10:13 AM, William S Fulton wrote:
There isn't anything illegal about exception specifications that
contain references. The ISO C++ spec doesn't say anything about
restricting the types in the exception specification except that a
pointer or reference to an *incomplete* type is not allowed. The
reason seems to be that a temporary copy is made of the object being
thrown, thus the type must be complete in order to call the copy
constructor.
Anyway try the CVS version now, I've fixed it so that SWIG handles
references.
William
Oren Miller wrote:
It doesn't really matter either way, the same code is generated (at
least on gcc, for sure on MSVC since they ignore it completely :).
I always declared it as a reference because it seems to be better
documentation for people who aren't used to exceptions (there are
many out there!), and they tend to copy the value they see in the
specifier. Just seems sort of symmetrical I guess. I guess I'll
change my throw specifiers to get around this. In any case this
syntax is probably rare, but I've never encountered a compiler that
doesn't support it, so you may or may not want to as well. Thanks.
--oren
On May 28, 2004, at 10:44 PM, Marcelo Matus wrote:
Shouldn't your function be defined as
virtual void fromAdmin( const Message&, const SessionID& )
throw( FieldNotFound, IncorrectDataFormat, IncorrectTagValue,
RejectLogon) = 0;
ie, with no references '&' ?
The reference in the catching part makes sense
catch(FIX::FieldNotFound &_e) {
....
}
but does it make sense in the throw definition?
Marcelo
Oren Miller wrote:
Ok. I defined a typemap, but now I'm getting some different
strange behavior. Maybe you can tell me if I'm doing something
wrong. So I defined a simple ypemap like so:
%typemap(throws) FIX::FieldNotFound& {
SWIG_fail;
}
But that generated this, note the extra '&'
catch(FIX::FieldNotFound &&_e) {
{
SWIG_fail;
}
}
So I thought maybe I needed to change it to this:
%typemap(throws) FIX::FieldNotFound {
SWIG_fail;
}
But then it went right back to generating as before:
catch(...) {
throw;
}
Am I not doing this correctly?
On May 28, 2004, at 2:58 PM, William S Fulton wrote:
Doesn't SWIG emit a warning about not having a throws typemap?
This problem doesn't seem to be director specific. You can
either define a throws typemap for each of the different types
in your exception specification or define a generic SWIGTYPE&
typemap for all of them. You could base it off the SWIGTYPE
throws typemap in python.swg (SWIG-1.3.21).
William
Oren Miller wrote:
I have some virtual classes with throw specifiers that indicate
multiple possible exceptions, such as this one:
virtual void fromAdmin( const Message&, const SessionID& )
throw( FieldNotFound&, IncorrectDataFormat&,
IncorrectTagValue&, RejectLogon& ) = 0;
The code that is generated looks like this (for python):
try {
(arg1)->fromAdmin((FIX::Message const
&)*arg2,(FIX::SessionID const &)*arg3);
}
catch(...) {
throw;
}
catch(...) {
throw;
}
catch(...) {
throw;
}
catch(...) {
throw;
}
Now this does not cause any errors (at least not in gcc 3.2, I
can't speak for other compilers), but it does cause several
warnings (since ... should be the last thing to be caught, and
three of them are not last). With multiple directors which can
have several methods like this, the warnings can add up. Of
course this can make errors more difficult to spot. I would
suggest that the generated code either look like so:
try {
(arg1)->fromAdmin((FIX::Message const
&)*arg2,(FIX::SessionID const &)*arg3);
}
catch(FieldNotFound&) {
throw;
}
catch(IncorrectDataFormat&) {
throw;
}
catch(IncorrectTagValue&) {
throw;
}
catch(RejectLogon&) {
throw;
}
or just simply consolidated into:
try {
(arg1)->fromAdmin((FIX::Message const
&)*arg2,(FIX::SessionID const &)*arg3);
}
catch(...) {
throw;
}
On May 28, 2004, at 12:21 PM, David Beazley wrote:
Marcelo Matus writes:
Ok, the thing is that "C arrays mapping" is a strange case.
The problem with C arrays is that the have a known size, and
the '\0' ending char is just a coincidence, ie you can define
char hi_a [] = {'h','e','l','l','o'};
char hi_b [] = "hello";
and both will be different
sizeof(hi_a) -> 5
sizeof(hi_b) -> 6
strlen(hi_a) -> undefined
strlen(hi_b) -> 5
and in the python side you will get
hi_a -> 'hello'
hi_b -> 'hello\0'
note that you can also put '0' char in between
char hi_a [] = {'h','e',0,'l','o'};
sizeof(hi_a) -> 5
strlen(hi_a) -> 2
So, in your case, maybe is better to use
const char* const BeginString_FIX44 = "FIX.4.4";
since that will be properly understood in C,C++ and in the
python side
(and you don't need the std::string). In that case the use of
'strlen'
is explicit, and in the python side you will not see the '\0'
ending
character.
Any definitions of the form
char foo[N] = "whatever";
char bar[] = "whatever";
should *always* be treated as NULL-terminated strings by
default in
SWIG. If someone wants different behavior than that, they
should
write their own typemap to do it.
Why is this suddenly broken? I thought issues concerning C
character
arrays were resolved long ago.
-- Dave
_______________________________________________
Swig maillist - Swig@xxxxxxxxxxxxxxx
http://mailman.cs.uchicago.edu/mailman/listinfo/swig
_______________________________________________
Swig maillist - Swig@xxxxxxxxxxxxxxx
http://mailman.cs.uchicago.edu/mailman/listinfo/swig
_______________________________________________
Swig maillist - Swig@xxxxxxxxxxxxxxx
http://mailman.cs.uchicago.edu/mailman/listinfo/swig
_______________________________________________
Swig maillist - Swig@xxxxxxxxxxxxxxx
http://mailman.cs.uchicago.edu/mailman/listinfo/swig