From: William S F. <ws...@fu...> - 2006-01-12 22:01:52
|
Marcelo Matus wrote: > William S Fulton wrote: > >> >> [Moving this thread from swig-cvs to swig-devel.] >> >> I'm not sure if you can access the C++ standard, but it says that the >> exception specification lists exceptions that the function might >> directly or indirectly throw. 'Might' isn't clear enough to say >> whether or not you can put classes into the exception specification >> that cannot possibly be thrown. Personally, I think you can quite >> validly have the foo() throw(E1,EBase) declaration you showed above >> even though in the code it can only throw an E1. The compilers I tried >> accept the declaration above, probably because exception >> specifications are a run time check, not a static/compile time check. >> >> The way that you've documented %catches confirms what I observed when >> I had a quick play with with this feature. That is %catches completely >> replaces the exception specification, in that SWIG generates catch >> handlers depending on the exception specification (or %catches if >> specified). So SWIG is treating %catches as a replacement for the >> exception specification, ie it doesn't add to any real exception >> specification actually declared on the function. The only way that I >> see it not behaving as an exception specification is the acceptance of >> (...). SWIG issues a warning that there is no throws typemap for >> '...'. How does one declare one btw? I suspect there is no way as >> '...' is not a type and I don't think we should modify typemaps to >> work for non-types. > > We don't need to modify the typemap system, this work perfectly fine: > > %typemap(throws) (...) { > SWIG_exception(SWIG_RuntimeError,"unknown exception"); > } > > the thing is that swig recognize (...) as a valid type, ie, a varglist. > You can try it now in Java, csharp, > or any other language that support the expection.i mechanism. > > >> So if exception specifications and %catches generates code taken from >> throws typemaps, then we cannot accept '...' in the %catches list - >> the warning that SWIG generates is a good one. > > but that is only because is was not defined, now it generate a valid > manager of the '...' case > for all the target languages that support the exception.i mechanism. > > Check the exception_order.i example in the test-suite. > > In languages where exception.i is not supported, you will get the proper > message, as for any other > undefined typemap. > > >> Gosh, hope you are following all this, but it looks to me as though >> %catches really is a replacement for the exception specification, and >> that is a good thing as it will be more easily documented and simpler >> to comprehend!! In the light of this, %throw or %throws seems the best >> name to me. Nice new feature btw. > > > the problem I see is that it is easy to confuse, what the %cathes list > represent and whath the throw specification is. And they are very easy > to confuse since swig, by default, use the same throw > specification list to generate the catch list. This is a very natural > default behavior, ie, try to > catch and rethrow, in a literal way, all the exceptions found in the > throw specification, but from the C++ point of view, that is only one of > your options. You can, for example, decide to catch and rethrow > some of them, ignore others, or 'translate' some of the exceptions in to > different ones. > > The main point, wath you catch and rethrow doesn't has to be the same > you throw. It can be, > as swig is doing now, but it doesn't has to. And deciding to do > something different, doesn't > change the throw specifications. > > lets review this example: > > %catches(E1) foo; > int foo() throw(E1,E2,E3) > > is not equivalent to replace the throw specification, as doing > > int foo() throw(E1); > > since, well, that is not the porpuse, we just want to define wath we > are interesting in catching > and rethrowing to the target language is 'E1'. > > 'foo' still can throw E1, E2, and E3, and the %cacthes directive > will only take care of E1. If we don't add any other safeguards, and > 'foo' throw E2, the > program will terminate. > > To avoid the previous case, for example, we can combinae %catches and > %exception: > > %exception { > try { $action } > catch (...) { <do something> } > } > %catches(E1) foo; > int foo() throw(E1,E1,E3); > > swig will generate > > /* catch code generated by %exception */ > try { > /* catch code generated by %catches */ > try { > result = foo(); > } catch (E1 &_e1) { > <rethrow E1 to the target language via the throws typemap> > } > } catch (...) { > <do something > > } > > here, %cathes will only catch and rethrow the E1 exception, but foo > still > can throw E2 and E3, which will be catched by the %exception code. > > Note that replacing the throw declaration, which could be done with > something like > > int foo_wrap() throw(E1) { return foo(); } > > would have a different behavior, since if 'foo' throws E2 or E3, C++ > will trigger > an unknown exception event , which can't be catched via try/catch, > ie, the program > will be terminated. > > Other difference between %catches and replacing the exception list is > clearly that > we can define the (...) case, as we discussed before, which is > impossible to do with something like > > int foo_wrap() throw(...) { return foo(); } > > So, I agree that %catches could be a bad name, but using %throw/%throws > will be even worse. > > Other name options: > > %catchlist > %rethrowlist > %rethrows > %rethrow > %lrethrow > > I like %rethrow since it is more explicit to describe wath we are doing, > catching a list > of exceptions and rethrowing them into the target language. > > Yup I'm in broad agreement except, rethrow seems incorrect as it is very much a c++ term meaning rethrow the exception. What is usually happening is the C++ exception is "converted" or "translated" as you put it into a target language error/exception. But, also as you point out the code in the throws typemap does not necessarily convert the C++ exception, it might swallow it, abort, log a message etc etc. So rethrow seems quite inaccurate and presumptious to me. What the types in the %catches list is telling SWIG is that these are some Exception types which the function might *throw*. SWIG will then generate code that will *catch* the exceptions *thrown* from the function. The code will come from the *throws" typemap. (...) is a strange type, but if SWIG already accepts then so be it. I still like %throws, but the problem with using it is this quirky (...) 'type' so the term "catches" might be more appropriate as it results in catch handlers. Not sure there is a perfect name, but I'd choose one of %throws or %catches. You can choose! BTW, getting really anal and thrashing this topic to death, I think that there *is* a strong correlation between the way SWIG handles the exception specification (or %catches/%throws) and the way the compiler handles exception specifications. - SWIG will look at these types and generate code to handle them well. If a type other than one in the list is thrown all hell will break loose and the program will terminate with a seg fault. - The compiler will look at these types and if a type other than one in the list is thrown, it will generate code so that all hell will break loose and the program will terminate. So both SWIG and the compiler will generate code that will fail catastrophically if a type not in the list is thrown. Of course if someone uses %exception [SWIG], or writes a custom terminate() function [compiler] then behaviour might not be so bad, but both are additional customisations. William |