Improved Java director exceptions documentation

This commit is contained in:
William S Fulton 2017-11-16 20:03:58 +00:00
parent 7aa28e37ec
commit 077bb0b04f
3 changed files with 86 additions and 23 deletions

View File

@ -3825,17 +3825,20 @@ The disadvantage is that invocation of director methods from C++ when Java doesn
<p>
With directors routing method calls to Java, and proxies routing them
to C++, the handling of exceptions is an important concern.
The default behavior from SWIG 3.0
onwards is to convert the thrown Java exception into a SWIG defined
<code>DirectorException</code> C++ exception.
SWIG 2.0 and earlier versions didn't provide any mechanism to handle the Java director method exceptions in C++.
The default behavior for Java exceptions thrown in a director method overridden in Java is
to convert the thrown Java exception into a SWIG defined
<code>DirectorException</code> C++ class exception in the C++ layer and then throw this C++ exception.
</p>
<p>
Converting Java exceptions into C++ exceptions can be done in two different ways using
The conversion of Java exceptions into C++ exceptions can be customized in two different ways using
the <code>director:except</code> <a href="Customization.html#Customization_features">feature</a>.
In the simplest approach, a code block is attached to each director method to
handle the mapping of Java exceptions into C++ exceptions.
The example below converts a
<tt>java.lang.IndexOutOfBoundsException</tt> into a C++ <tt>std::out_of_range</tt> exception and converts a
user's Java <tt>MyJavaException</tt> into a C++ <tt>MyCppException</tt> exception.
If the Java exception doesn't match either of these, a fallback <tt>std::runtime_error</tt> C++ exception is thrown.
</p>
<div class="code">
@ -3858,6 +3861,61 @@ class MyClass {
</pre>
</div>
<p>
A few special variables are expanded from the <tt>director:except</tt> feature when generated.
The special variable <code>$error</code> is expanded by SWIG into a unique variable name and
should be used for the
assignment of the exception that occurred. The special variable <code>$packagepath</code> is
replaced by the outer package provided for SWIG generation by the -package option.
</p>
<p>
Utility functions/classes in director.swg are provided to aid the exception conversion as follows:
</p>
<div class="code">
<pre>
namespace Swig {
// Helper method to determine if a Java throwable matches a particular Java class type
bool ExceptionMatches(JNIEnv *jenv, jthrowable throwable, const char *classname);
// Helper class to extract the exception message from a Java throwable
class JavaExceptionMessage {
public:
JavaExceptionMessage(JNIEnv *jenv, jthrowable throwable);
// Return a C string of the exception message in the jthrowable passed in the constructor
// If no message is available, null_string is return instead
const char *message(const char *null_string =
"Could not get exception message in JavaExceptionMessage") const;
};
// C++ Exception class for handling Java exceptions thrown during a director method Java upcall
class DirectorException : public std::exception {
public:
// Construct exception from a Java throwable
DirectorException(JNIEnv *jenv, jthrowable throwable);
// More general constructor for handling as a java.lang.RuntimeException
DirectorException(const char *msg);
// Return exception message extracted from the Java throwable
const char *what() const throw();
// Reconstruct and raise/throw the Java Exception that caused the DirectorException
// Note that any error in the JNI exception handling results in a Java RuntimeException
void raiseJavaException(JNIEnv *jenv) const;
// Create and throw the DirectorException
static void raise(JNIEnv *jenv, jthrowable throwable);
};
}
</pre>
</div>
<p>
This approach allows a flexible mapping of Java exceptions thrown by director methods into
C++ exceptions expected by a C++ caller. There
@ -3872,11 +3930,8 @@ type, or derives from the given type, <code>Swig::ExceptionMatches</code> will r
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 the <a href="#Java_namespaces">nspace
feature</a>. The special variable <code>$error</code> is expanded by SWIG into a unique variable name and
should be used for the
assignment of the exception that occurred. The special variable <code>$packagepath</code> is
replaced by the outer package provided for SWIG generation by the -package
option. The utility class <code>Swig::JavaExceptionMessage</code> is a holder
feature</a>.
The utility class <code>Swig::JavaExceptionMessage</code> is a holder
providing access to the message from the thrown Java exception.
The <code>message()</code> 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
@ -3884,20 +3939,20 @@ needs to copy it, for example into a std::string or a newly constructed C++ exce
</p>
<p>
Using the above approach to
Using the above simple approach to
write handlers for a large number of methods will require
repetitive duplication of the <code>director:except</code> feature code.
To mitigate this, an alternative approach is provided via 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++
the <a href="Typemaps.html#throws_typemap">"throws" typemap</a>.
The "throws" typemap provides the second approach to map all the C++
exceptions listed in a method's defined exceptions (either from
a C++ <em>exception specification</em> or a <code>%catches</code>
feature) into Java exceptions.
The "directorthrows" typemap provides the inverse mapping and should contain
code to convert a suitably matching Java exception into a C++ exception.
The example below converts a Java <code>java.lang.IndexOutOfBoundsException</code> exception
to the typemap's type, that is <code>std::out_of_range</code>:
to the typemap's type, that is a <code>std::out_of_range</code> C++ exception:
<div class="code">
<pre>
@ -3928,7 +3983,7 @@ is used in the feature code. Consider the following, which also happens to be th
</div>
<p>
where <tt>Swig::DirectorException::raise</tt> is a helper method in the DirectorException class to throw a C++ exception (implemented in director.swg):
where <tt>Swig::DirectorException::raise</tt> is the helper method to throw a C++ <tt>Swig::DirectorException</tt>.
</p>
<div class="code">
@ -3948,7 +4003,7 @@ specification or <code>%catches</code> as described for the
</p>
<p>
Consider the following director method:
Let's try all this together and consider the following director method:
</p>
<div class="code">
@ -3997,8 +4052,8 @@ unexpected exceptions, the default handling can be changed by adding:
%feature("director:except") %{
jthrowable $error = jenv-&gt;ExceptionOccurred();
if ($error) {
jenv-&gt;ExceptionClear();
$directorthrowshandlers
jenv-&gt;ExceptionClear();
return $null; // exception is ignored
}
%}

View File

@ -2901,12 +2901,18 @@ For example:
PyErr_SetString(PyExc_RuntimeError, $1);
SWIG_fail;
%}
void bar() throw (const char *);
// Either an exception specification on the method
void bar() throw (const char *);
// Or a %catches feature attached to the method
%catches(const char *) bar();
void bar();
</pre>
</div>
<p>
As can be seen from the generated code below, SWIG generates an exception handler
As can be seen from the resulting generated code below, SWIG generates an exception handler
with the catch block comprising the "throws" typemap content.
</p>
@ -2915,8 +2921,7 @@ with the catch block comprising the "throws" typemap content.
...
try {
bar();
}
catch(char const *_e) {
} catch(char const *_e) {
PyErr_SetString(PyExc_RuntimeError, _e);
SWIG_fail;
}
@ -2925,8 +2930,8 @@ catch(char const *_e) {
</div>
<p>
Note that if your methods do not have an exception specification yet they do throw exceptions, SWIG cannot know how to deal with them.
For a neat way to handle these, see the <a href="Customization.html#Customization_exception">Exception handling with %exception</a> section.
Note that if your methods do not have an exception specification but they do throw exceptions and you are not using <tt>%catches</tt>, SWIG cannot know how to deal with them.
Please also see the <a href="Customization.html#Customization_exception">Exception handling with %exception</a> section for another way to handle exceptions.
</p>
<H2><a name="Typemaps_nn39">11.6 Some typemap examples</a></H2>

View File

@ -259,6 +259,8 @@ namespace Swig {
JavaExceptionMessage(JNIEnv *jenv, jthrowable throwable) : message_(jenv, exceptionMessageFromThrowable(jenv, throwable)) {
}
// Return a C string of the exception message in the jthrowable passed in the constructor
// If no message is available, null_string is return instead
const char *message(const char *null_string = "Could not get exception message in JavaExceptionMessage") const {
return message_.c_str(null_string);
}
@ -369,6 +371,7 @@ namespace Swig {
}
}
// Create and throw the DirectorException
static void raise(JNIEnv *jenv, jthrowable throwable) {
throw DirectorException(jenv, throwable);
}