Documentation edit for the director:except feature and directorthrows typemap

This commit is contained in:
William S Fulton 2013-11-07 22:48:17 +00:00
commit d73f04e925
2 changed files with 200 additions and 131 deletions

View file

@ -3556,99 +3556,108 @@ 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>
<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.
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++.
</p>
<p>
Converting Java exceptions into C++ exceptions can be done 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.
</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();
jthrowable $error = jenv-&gt;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");
jenv-&gt;ExceptionClear();
if (Swig::ExceptionMatches(jenv, $error, "java/lang/IndexOutOfBoundsException"))
throw std::out_of_range(Swig::JavaExceptionMessage(jenv, $error).message());
if (Swig::ExceptionMatches(jenv, $error, "$packagepath/MyJavaException"))
throw MyCppException(Swig::JavaExceptionMessage(jenv, $error).message());
throw std::runtime_error("Unexpected exception thrown in MyClass::method");
}
}
class MyClass {
void method(int x); /* on C++ side, may get std::runtime_error or MyCppException */
/** Throws either a std::out_of_range or MyCppException on error */
void method(int x);
}
</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>
This approach allows a flexible mapping of Java exceptions thrown by director methods into
C++ exceptions expected by a C++ caller. There
need not be any C++ <em>exception specifications</em> on the C++ method. The
utility 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
writing code for wrappers that use the <code>director:except</code> feature. 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
name, such as <code>"java/lang/IOError"</code>. If the throwable class is the same
type, or derives from the given type, <code>Swig::ExceptionMatches</code> will return 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>,
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
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
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
Using the above 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++
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.
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>:
<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>
%typemap(directorthrows) std::out_of_range %{
if (Swig::ExceptionMatches(jenv, $error, "java/lang/IndexOutOfBoundsException")) {
throw std::out_of_range(Swig::JavaExceptionMessage(jenv, $error).message());
}
%}
</pre>
</div>
<p>
The "directorthrows" typemap is then used in conjunction with the
<code>director:except</code> feature if the <code>$directorthrowshandlers</code> special variable
is used in the feature code. Consider the following, which also happens to be the default:
</p>
<div class="code">
<pre>
%feature("director:except") %{
jthrowable $error = jenv->ExceptionOccurred();
jthrowable $error = jenv-&gt;ExceptionOccurred();
if ($error) {
jenv->ExceptionClear();
jenv-&gt;ExceptionClear();
$directorthrowshandlers
throw Swig::DirectorException(jenv, $error);
}
@ -3657,34 +3666,66 @@ default definition is shown below.</p>
</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>
replaces the <code>$directorthrowshandlers</code> special variable with the code in
the "directorthrows" typemaps, for each and every exception defined for the method.
The possible exceptions can be defined either with a C++ exception
specification or <code>%catches</code> as described for the
<a href="Typemaps.html#throws_typemap">"throws" typemap</a>.
</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>
Consider the following director method:
</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>
...
virtual void doSomething(int index) throw (std::out_of_range);
...
</pre>
</div>
<p>
When combined with the default <code>director:except</code> feature and the "directorthrows" typemap above,
the resulting code generated in the director method after calling up to Java will be:
</p>
<div class="code">
<pre>
jthrowable swigerror = jenv-&gt;ExceptionOccurred();
if (swigerror) {
jenv-&gt;ExceptionClear();
if (Swig::ExceptionMatches(jenv, swigerror, "java/lang/IndexOutOfBoundsException")) {
throw std::out_of_range(Swig::JavaExceptionMessage(jenv, swigerror).message());
}
throw Swig::DirectorException(jenv, swigerror);
}
</pre>
</div>
<p><em>
Note: Beware of using exception specifications as the SWIG director methods
will be generated with the same exception specifications and if the
director method throws an exception that is not specified it is likely
to terminate your program. See the C++ standard for more details.
Using the %catches feature instead to define the handled exceptions does not suffer
this potential fate.
</em></p>
<p>Because the default code generation maps any unhandled Java exceptions to
<code>Swig::DirectorException</code>, any director methods that have exception
specifications may cause program termination. To simply ignore
unexpected exceptions, the default handling can be changed with:
<div class="code">
<pre>
%feature("director:except") %{
jthrowable $error = jenv->ExceptionOccurred();
jthrowable $error = jenv-&gt;ExceptionOccurred();
if ($error) {
jenv->ExceptionClear();
jenv-&gt;ExceptionClear();
$directorthrowshandlers
return $null;
return $null; // exception is ignored
}
%}
</pre>
@ -3692,27 +3733,25 @@ unexpected exceptions, the default can be changed to:
</p>
<p>Alternatively an exception compatible with the existing director
method exception specifications may be thrown. Assuming that all
method exception specifications can be thrown. Assuming that all
methods allow std::runtime_error to be thrown,
the <code>return&nbsp;$null;</code> could be changed to:
</p>
<div class="code">
<pre>
throw std::runtime_error(Swig::JavaExceptionMessage(jenv,$error).message());
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,
of the "directorthrows" typemaps. In this example, a
generic "directorthrows" typemap is appropriate for all three exceptions - all
take single string constructors. If the exceptions had different constructors,
it would be neccessary to have separate typemaps for each exception type.
@ -3721,22 +3760,29 @@ it would be neccessary to have separate typemaps for each exception type.
example interface that could be generated and built. -->
<div class="code">
<pre>
%module(directors="1") example
%{
#include &lt;string&gt;
#include &lt;stdexcept&gt;
%}
// 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) {}; };
struct NAME : public std::runtime_error { NAME(const std::string &amp;what) : runtime_error(what) {} };
}
%}
%enddef
// Expose c++ exceptions as java Exceptions with getMessage
// Expose C++ exceptions as Java Exceptions by changing the Java base class and providing a getMessage()
%define DECLARE_EXCEPTION(NAME)
%typemap(javabase) MyNS::NAME "java.lang.Exception";
%rename(getMessage,fullname=1) MyNS::NAME::what;
%rename(getMessage) MyNS::NAME::what;
namespace MyNS {
struct NAME {
NAME(const std::string& what);
NAME(const std::string&amp; what);
const char * what();
};
}
@ -3744,17 +3790,16 @@ namespace MyNS {
DEFINE_EXCEPTION(ExceptionA)
DEFINE_EXCEPTION(ExceptionB)
DEFINE_EXCEPTION(Unknown)
DEFINE_EXCEPTION(Unexpected)
// Mark three methods to map director-thrown exceptions.
// Standard rules for feature matching apply
// Mark three methods to map director thrown exceptions.
%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());
if (Swig::ExceptionMatches(jenv, $error, "$packagepath/$javaclassname"))
throw $1_type(Swig::JavaExceptionMessage(jenv, $error).message());
%}
DECLARE_EXCEPTION(ExceptionA)
@ -3769,68 +3814,54 @@ DECLARE_EXCEPTION(Unexpected)
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() {};
virtual ~MyClass() {}
};
}
</pre>
</div>
<p>
In this case the three different <code>directorthrows</code> typemaps will be used
In this case the three different "directorthrows" 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.
the exception specification or <code>%catches</code> feature.
</p>
<p>Note that the <code>directorthrows</code> typemaps are important
<p>Note that the "directorthrows" 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
SWIG generated Java wrapper and access is always through this wrapper,
the default <code>Swig::DirectorException</code> class provides enough information
to reconstruct the original exception. In this case removing the
$directorthrowshandlers replacement variable from the
<code>$directorthrowshandlers</code> special variable from the
default <code>director:except</code> feature and simply always
throwing a Swig::DirectorException will achieve the desired result.
throwing a <code>Swig::DirectorException</code> 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>
Java exceptions via the <code>Swig::DirectorException::raiseJavaException</code> method,
as demonstrated with <code>%javaexception</code> below:
</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);
%javaexception("Exception") MyClass::myMethod %{
try {
$action
} catch (Swig::DirectorException &amp;e) {
// raise/throw the Java exception that originally caused the DirectorException
e.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>
<p>
See the <a href="#Java_exception_handling">Exception handling with %exception and %javaexception</a>
section for more on converting C++ exceptions to Java exceptions.
</p>
<H2><a name="Java_allprotected"></a>24.6 Accessing protected members</H2>
@ -5643,7 +5674,7 @@ can be wrapped with the Java equivalent, that is, static inner proxy classes.
</p>
<p>
<b><tt>$jniinput, $javacall and $packagepath</tt></b><br>
<b><tt>$error, $jniinput, $javacall and $packagepath</tt></b><br>
These special variables are used in the directors typemaps. See <a href="#Java_directors_typemaps">Director specific typemaps</a> for details.
</p>
@ -5977,6 +6008,10 @@ is the package name passed from the SWIG command line and <code>$javaclassname</
If the <tt>-package</tt> commandline option is not used to specify the package, then '$packagepath/' will be removed from the resulting output JNI field descriptor.
<b>Do not forget the terminating ';' for JNI field descriptors starting with 'L'.</b>
If the ';' is left out, Java will generate a "method not found" runtime error.
Note that the <code>$packagepath</code> substitution always uses the path separator '/' when expanded.
The <code>$javaclassname</code> expansion can be confusing as it is normally expanded using the '.' separator.
However, <code>$javaclassname</code> is expanded using the path separator '/' in typemap's "descriptor" attribute
as well as in the "directorthrows" typemap.
</p>
</div>
@ -6072,6 +6107,40 @@ The target method is the method in the Java proxy class which overrides the virt
</div>
<p><tt>%typemap(directorthrows)</tt></p>
<div class="indent">
<p>
Conversion of Java exceptions to C++ exceptions in director method's exception handling.
This typemap is expected to test the <tt>$error</tt> special variable for a matching Java exception
and if successful convert and throw it into a C++ exception given by the typemap's type.
The <code>$error</code> special variable is of type <code>jthrowable</code> and is
substituted with a unique variable name in the generated code.
</p>
<p>
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>:
</p>
<div class="code">
<pre>
%typemap(directorthrows) std::out_of_range %{
if (Swig::ExceptionMatches(jenv, $error, "java/lang/IndexOutOfBoundsException")) {
throw std::out_of_range(Swig::JavaExceptionMessage(jenv, $error).message());
}
%}
</pre>
</div>
<p>
The utility function <code>Swig::ExceptionMatches</code>
and class <code>Swig::JavaExceptionMessage</code> are helpers available when using directors and are described
in the <a href="#Java_exceptions_from_directors">Java Exceptions from Directors</a> section.
</p>
</div>
<p><tt>%typemap(javapackage)</tt></p>
<div class="indent">