aboutsummaryrefslogtreecommitdiff
path: root/Doc/Manual
diff options
context:
space:
mode:
authorMarvin Greenberg <public.marvin@gmail.com>2013-10-22 20:31:14 +0100
committerWilliam S Fulton <wsf@fultondesigns.co.uk>2013-10-22 20:44:35 +0100
commit6736e74127180f012dab11379a2159cd073461d4 (patch)
tree07f0d845670f9d77ce303a9e1fdc5f139b1f7dbe /Doc/Manual
parentec1d5a5be1c7cdaaa8dedc3ba76a5792127cef0f (diff)
downloadswig-6736e74127180f012dab11379a2159cd073461d4.tar.gz
Add feature director:except for improved director exception handling in Java
Closes #91
Diffstat (limited to 'Doc/Manual')
-rw-r--r--Doc/Manual/Java.html276
-rw-r--r--Doc/Manual/Warnings.html1
2 files changed, 277 insertions, 0 deletions
diff --git a/Doc/Manual/Java.html b/Doc/Manual/Java.html
index f3d8a1684..0e9ba75de 100644
--- a/Doc/Manual/Java.html
+++ b/Doc/Manual/Java.html
@@ -87,6 +87,7 @@
<li><a href="#Java_directors_example">Simple directors example</a>
<li><a href="#Java_directors_threading">Director threading issues</a>
<li><a href="#Java_directors_performance">Director performance tuning</a>
+<li><a href="#Java_exceptions_from_directors">Java Exceptions from Directors</a>
</ul>
<li><a href="#Java_allprotected">Accessing protected members</a>
<li><a href="#Java_common_customization">Common customization features</a>
@@ -3555,6 +3556,281 @@ However, if all director methods are expected to usually be overridden by Java s
The disadvantage is that invocation of director methods from C++ when Java doesn't actually override the method will require an additional call up into Java and back to C++. As such, this option is only useful when overrides are extremely common and instantiation is frequent enough that its performance is critical.
</p>
+<H3><a name="Java_exceptions_from_directors"></a>24.5.7 Java Exceptions from Directors</H3>
+
+<p>
+With directors routing method calls to Java, and proxies routing them
+to C++, the handling of exceptions is an important concern. In Swig
+2.0, the director class methods ignored java exceptions that occurred
+during method calls dispatched to the Java director class and simply
+returned '$null' to the C++ caller. The default behavior now throws a
+Swig-defined <code>DirectorException</code> C++ exception. A facility
+is now provided to allow translation of thrown Java exceptions into
+C++ exceptions. This can be done in two different ways using
+the <code>%feature("director:except")</code> directive. In the
+simplest approach, a code block is attached to each director method to
+handle the mapping of java exceptions into C++ exceptions.
+</p>
+
+<div class="code">
+<pre>
+// All the rules to associate a feature with an element apply
+%feature("director:except") MyClass::method(int x) {
+ jthrowable $error = jenv->ExceptionOccurred();
+ if ($error) {
+ jenv->ExceptionClear();
+ if (Swig::ExceptionMatches(jenv,$error,"java/lang/IndexOutOfBoundsException"))
+ throw std::out_of_range(Swig::JavaExceptionMessage(jenv,$error).message());
+ else if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyJavaException"))
+ throw MyCppException(Swig::JavaExceptionMessage(jenv,$error).message());
+ else
+ throw std::runtime_error("Unexpected exception thrown by MyClass::method");
+ }
+}
+
+class MyClass {
+ void method(int x); /* on C++ side, may get std::runtime_error or MyCppException */
+}
+</pre>
+</div>
+<p>
+This approach allows mapping java exceptions thrown by director methods into
+C++ exceptions, to match the exceptions expected by a C++ caller. There
+need not be any exception specification on the method. This approach gives
+complete flexibility to map exceptions thrown by a java director
+implementation into desired C++ exceptions. The
+function <code>Swig::ExceptionMatches</code>
+and class <code>Swig::JavaExceptionMessage</code> are provided to simplify
+writing code for wrappers that use <code>director:except</code>feature. These simplify
+testing the type of the java exception and constructing C++ exceptions. The
+function <code>Swig::ExceptionMatches</code> matches the type of the
+<code>jthrowable</code> thrown against a <b>fully qualified</b> JNI style class
+name, like <code>"java/lang/IOError"</code>. If the throwable class is the same
+type, or derives from the given type, it returns true. Care must be taken to
+provide the correct fully qualified name, since for wrapped exceptions the
+generated proxy class will have additional package qualification, depending on
+the '-package' argument and use of <a href="#Java_namespaces">nspace
+feature</a>. The variable $error is simply a unique variable name to allow
+assignment of the exception that occurred. The variable $packagepath is
+replaced by the outer package provided for swig generation by the -package
+option. The class <code>Swig::JavaExceptionMessage</code> is a holder
+object giving access to the message from the thrown java exception.
+The message() method returns the exception message as a <code>const char *</code>,
+which is only valid during the lifetime of the holder. Any code using this message
+needs to copy it, for example into a std::string or a newly constructed C++ exception.
+</p>
+
+<p>
+If many methods may throw different exceptions, using this approach to
+write handlers for a large number of methods will result in
+duplication of the code in the <code>director:except</code> feature
+code blocks, and will require separate feature definitions for every
+method. So an alternative approach is provided, using typemaps in a
+fashion analagous to
+the <a href="Typemaps.html#throws_typemap">"throws" typemap.</a> The
+"throws" typemap provides an approach to automatically map all the C++
+exceptions listed in a method's defined exceptions (either from
+an <em>exception specification</em> or a <code>%catches</code>
+feature) into Java exceptions, for the generated proxy classes. To
+provide the inverse mapping, the <code>directorthrows</code> typemap
+is provided.
+
+<p>Using directorthrows typemaps allows a
+generic <code>director:except</code> feature to be combined with
+method-specific handling to achieve the desired result. The
+default <code>director:except</code> feature, in combination
+with <code>directorthrows</code> typemaps generate exception mapping
+to C++ exceptions for all the exceptions defined for each method. The
+default definition is shown below.</p>
+
+<div class="code">
+<pre>
+%feature("director:except") %{
+ jthrowable $error = jenv->ExceptionOccurred();
+ if ($error) {
+ jenv->ExceptionClear();
+ $directorthrowshandlers
+ throw Swig::DirectorException(jenv, $error);
+ }
+%}
+</pre>
+</div>
+
+<p>The code generated using the <code>director:except</code> feature
+replaces the <code>$directorthrowshandlers</code> with code that throws
+appropriate C++ exceptions from <code>directorthrows</code> typemaps
+for each exception defined for the method. Just as with
+the <a href="Typemaps.html#throws_typemap">"throws" typemap</a>, the
+possible exceptions may be defined either with an exception
+specification (<code> throw(MyException,std::runtime_error)</code> ) or
+using the <code>%catches</code> feature applied to the method.</p>
+
+<p><em>Note: Using the %catches feature to define the
+handled exceptions is preferred to using exception specifications. If
+the interface is defined with an exception specification the generated
+swig proxy classes will have the same exception specification. In C++
+if exceptions other than those in the specification are thrown, the
+program will be terminated. </em></p>
+
+<p>Because this default definition maps any unhandled java exceptions to
+Swig::DirectorException, any director methods that define exception
+specifications will cause program termination. To simply ignore
+unexpected exceptions, the default can be changed to:
+
+<div class="code">
+<pre>
+%feature("director:except") %{
+ jthrowable $error = jenv->ExceptionOccurred();
+ if ($error) {
+ jenv->ExceptionClear();
+ $directorthrowshandlers
+ return $null;
+ }
+%}
+</pre>
+</div>
+</p>
+
+<p>Alternatively an exception compatible with the existing director
+method exception specifications may be thrown. Assuming that all
+methods allow std::runtime_error to be thrown,
+the <code>return&nbsp;$null;</code> could be changed to:
+
+<div class="code">
+<pre>
+ throw std::runtime_error(Swig::JavaExceptionMessage(jenv,$error).message());
+</pre>
+</div>
+</p>
+
+<p>In more complex situations, a separate <code>director:except</code> feature
+may need to be attached to specific methods.
+</p>
+
+<p>Below is a complete example demonstrating the use
+of <code>directorthrows</code> typemaps. The <code>directorthrows</code> typemap
+provides a code fragment to test for a pending java exception type, and the
+resulting C++ exception that will be thrown. In this example, a
+generic directorthrows typemap is appropriate for all three exceptions - all
+take single string constructors. If the constructors had different constructors,
+it would be neccessary to have separate typemaps for each exception type.
+
+
+<!-- All the DEFINE_ and DECLARE_EXCEPTIONS CAN BE OMITTED to make
+ this more succinct. They are included to make this a complete
+ example interface that could be generated and built. -->
+<div class="code">
+<pre>
+// Define exceptions in header section using std::runtime_error
+%define DEFINE_EXCEPTION(NAME)
+%{
+ #include &lt;exception&gt;
+ namespace MyNS {
+ struct NAME : public std::runtime_error { NAME(const std::string& what):runtime_error(what) {}; };
+ }
+%}
+%enddef
+// Expose c++ exceptions as java Exceptions with getMessage
+%define DECLARE_EXCEPTION(NAME)
+%typemap(javabase) MyNS::NAME "java.lang.Exception";
+%rename(getMessage,fullname=1) MyNS::NAME::what;
+namespace MyNS {
+ struct NAME {
+ NAME(const std::string& what);
+ const char * what();
+ };
+}
+%enddef
+
+DEFINE_EXCEPTION(ExceptionA)
+DEFINE_EXCEPTION(ExceptionB)
+DEFINE_EXCEPTION(Unknown)
+
+// Mark three methods to map director-thrown exceptions.
+// Standard rules for feature matching apply
+%feature("director:except") MyClass::meth1(int);
+%feature("director:except") MyClass::meth2;
+%feature("director:except") meth3;
+
+%typemap(directorthrows) MyNS::ExceptionA, MyNS::ExceptionB, MyNS::Unexpected %{
+ if (Swig::ExceptionMatches(jenv,$error,"$packagepath/$javaclassname"))
+ throw $1_type(Swig::JavaExceptionMessage(jenv,$error).message());
+%}
+
+DECLARE_EXCEPTION(ExceptionA)
+DECLARE_EXCEPTION(ExceptionB)
+DECLARE_EXCEPTION(Unexpected)
+
+%catches(MyNS::ExceptionA, MyNS::ExceptionB, MyNS::Unexpected) MyClass::meth2();
+
+%inline {
+ class MyClass {
+ public:
+ virtual void meth1(int x) throw(MyNS::ExceptionA, MyNS::ExceptionB) = 0;
+ virtual void meth2() = 0; /* throws MyNS::ExceptionA, MyNS::ExceptionB, MyNS::Unexpected */
+ virtual void meth3(float x) throw(MyNS::Unexpected) = 0;
+ virtual ~MyClass() {};
+ };
+}
+</pre>
+</div>
+
+<p>
+In this case the three different <code>directorthrows</code> typemaps will be used
+to generate the three different exception handlers for
+<code>meth1</code>, <code>meth2</code> and <code>meth3</code>. The generated
+handlers will have "if" blocks for each exception type specified, in
+the exception specification or <code>%catches</code> feature. The code block
+in the directorthrows typemap should always throw a c++ exception.
+</p>
+
+<p>Note that the <code>directorthrows</code> typemaps are important
+only if it is important for the the exceptions passed through the C++
+layer to be mapped to distinct C++ exceptions. If director methods
+are being called by C++ code that is itself wrapped in a
+Swig-generated java wrapper and access is always through this wrapper,
+the default Swig::DirectorException class provides enough information
+to reconstruct the original exception. In this case removing the
+$directorthrowshandlers replacement variable from the
+default <code>director:except</code> feature and simply always
+throwing a Swig::DirectorException will achieve the desired result.
+Along with this a generic exception feature is added to convert any
+caught <code>Swig::DirectorException</code>s back into the underlying
+java exceptions, for each method which may get a generic
+DirectorException from a wrapped director method.</p>
+
+<div class="code">
+<pre>
+%feature ("except",throws="Exception") MyClass::myMeth %{
+ try { $action }
+ catch (Swig::DirectorException & direxcp) {
+ // jenv always available in JNI code
+ // raise the java exception that originally caused the DirectorException
+ direxcp.raiseJavaException(jenv);
+ return $null;
+ }
+%}
+</pre>
+</div>
+
+<p>The <code>throws="Exception"</code> attribute on the exception
+feature is necessary if any of the translated exceptions will be
+checked exceptions, since the java compiler will otherwise assert that
+no checked exceptions can be thrown by the method. This may be more
+specific that the completely generic "Exception" class, of course. A
+similar feature must be added to director methods to allow checked
+exceptions to be thrown from the director method implementations.
+Here, no actual exception handling is needed - the feature simply
+is being used to add a generic checked exception signature to the
+generated director method wrapper. </p>
+
+<div class="code">
+<pre>
+%feature ("except",throws="Exception") MyDirectorClass::myDirMeth %{ %}
+</pre>
+</div>
+
+
<H2><a name="Java_allprotected"></a>24.6 Accessing protected members</H2>
diff --git a/Doc/Manual/Warnings.html b/Doc/Manual/Warnings.html
index aac415952..5b507c123 100644
--- a/Doc/Manual/Warnings.html
+++ b/Doc/Manual/Warnings.html
@@ -499,6 +499,7 @@ example.i(4) : Syntax error in input.
<li>474. Method <em>method</em> usage of the optimal attribute ignored in the out typemap as the following cannot be used to generate optimal code: <em>code</em>
<li>475. Multiple calls to <em>method</em> might be generated due to optimal attribute usage in the out typemap.
<li>476. Initialization using std::initializer_list.
+<li>477. Feature director:except on <em>Class::method</em> with $directorthrowshandlers requires directorthrows typemap for exception <em>Exception</em>
</ul>