From d73f04e925e9210f2c688ef3656e6059eb540565 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Thu, 7 Nov 2013 22:48:17 +0000 Subject: Documentation edit for the director:except feature and directorthrows typemap --- Doc/Manual/Java.html | 329 ++++++++++++++++++++++++++++------------------- Doc/Manual/Warnings.html | 2 +- 2 files changed, 200 insertions(+), 131 deletions(-) (limited to 'Doc/Manual') diff --git a/Doc/Manual/Java.html b/Doc/Manual/Java.html index 0e9ba75de..08c80c83a 100644 --- a/Doc/Manual/Java.html +++ b/Doc/Manual/Java.html @@ -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.

-

24.5.7 Java Exceptions from Directors

+

24.5.7 Java exceptions from directors

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 DirectorException 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 %feature("director:except") 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 +DirectorException C++ exception. +SWIG 2.0 and earlier versions didn't provide any mechanism to handle the Java director method exceptions in C++. +

+ +

+Converting Java exceptions into C++ exceptions can be done in two different ways using +the director:except feature. +In the simplest approach, a code block is attached to each director method to +handle the mapping of Java exceptions into C++ exceptions.

-// 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->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->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);
 }
 
+

-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 Swig::ExceptionMatches +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++ exception specifications on the C++ method. The +utility function Swig::ExceptionMatches and class Swig::JavaExceptionMessage are provided to simplify -writing code for wrappers that use director:exceptfeature. These simplify -testing the type of the java exception and constructing C++ exceptions. The +writing code for wrappers that use the director:except feature. The function Swig::ExceptionMatches matches the type of the jthrowable thrown against a fully qualified JNI style class -name, like "java/lang/IOError". 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 "java/lang/IOError". If the throwable class is the same +type, or derives from the given type, Swig::ExceptionMatches 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 nspace -feature. 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 Swig::JavaExceptionMessage is a holder -object giving access to the message from the thrown java exception. -The message() method returns the exception message as a const char *, +the '-package' argument and use of the nspace + feature. The special variable $error is expanded by SWIG into a unique variable name and +should be used for the +assignment of the exception that occurred. The special variable $packagepath is +replaced by the outer package provided for SWIG generation by the -package +option. The utility class Swig::JavaExceptionMessage is a holder +providing access to the message from the thrown Java exception. +The message() method returns the exception message as a const char *, 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.

-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 director:except 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 director:except feature code. +To mitigate this, an alternative approach is provided via typemaps in a fashion analagous to the "throws" typemap. The "throws" typemap provides an approach to automatically map all the C++ exceptions listed in a method's defined exceptions (either from -an exception specification or a %catches -feature) into Java exceptions, for the generated proxy classes. To -provide the inverse mapping, the directorthrows typemap -is provided. +a C++ exception specification or a %catches +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 java.lang.IndexOutOfBoundsException exception +to the typemap's type, that is std::out_of_range: -

Using directorthrows typemaps allows a -generic director:except feature to be combined with -method-specific handling to achieve the desired result. The -default director:except feature, in combination -with directorthrows typemaps generate exception mapping -to C++ exceptions for all the exceptions defined for each method. The -default definition is shown below.

+
+
+%typemap(directorthrows) std::out_of_range %{
+  if (Swig::ExceptionMatches(jenv, $error, "java/lang/IndexOutOfBoundsException")) {
+    throw std::out_of_range(Swig::JavaExceptionMessage(jenv, $error).message());
+  }
+%}
+
+
+ +

+The "directorthrows" typemap is then used in conjunction with the +director:except feature if the $directorthrowshandlers special variable +is used in the feature code. Consider the following, which also happens to be the default: +

 %feature("director:except") %{
-   jthrowable $error = jenv->ExceptionOccurred();
+   jthrowable $error = jenv->ExceptionOccurred();
    if ($error) {
-     jenv->ExceptionClear();
+     jenv->ExceptionClear();
      $directorthrowshandlers
      throw Swig::DirectorException(jenv, $error);
    }
@@ -3657,34 +3666,66 @@ default definition is shown below.

The code generated using the director:except feature -replaces the $directorthrowshandlers with code that throws -appropriate C++ exceptions from directorthrows typemaps -for each exception defined for the method. Just as with -the "throws" typemap, the -possible exceptions may be defined either with an exception -specification ( throw(MyException,std::runtime_error) ) or -using the %catches feature applied to the method.

+replaces the $directorthrowshandlers 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 %catches as described for the +"throws" typemap. +

+ +

+Consider the following director method: +

+ +
+
+  ...
+  virtual void doSomething(int index) throw (std::out_of_range);
+  ...
+
+
-

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.

+

+When combined with the default director:except feature and the "directorthrows" typemap above, +the resulting code generated in the director method after calling up to Java will be: +

-

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: +

+
+jthrowable swigerror = jenv->ExceptionOccurred();
+if (swigerror) {
+  jenv->ExceptionClear();
+  if (Swig::ExceptionMatches(jenv, swigerror, "java/lang/IndexOutOfBoundsException")) {
+    throw std::out_of_range(Swig::JavaExceptionMessage(jenv, swigerror).message());
+  }
+  
+  throw Swig::DirectorException(jenv, swigerror);
+}
+
+
+ +

+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. +

+ +

Because the default code generation maps any unhandled Java exceptions to +Swig::DirectorException, any director methods that have exception +specifications may cause program termination. To simply ignore +unexpected exceptions, the default handling can be changed with:

 %feature("director:except") %{
-   jthrowable $error = jenv->ExceptionOccurred();
+   jthrowable $error = jenv->ExceptionOccurred();
    if ($error) {
-     jenv->ExceptionClear();
+     jenv->ExceptionClear();
      $directorthrowshandlers
-     return $null;
+     return $null; // exception is ignored
    }
 %}
 
@@ -3692,27 +3733,25 @@ unexpected exceptions, the default can be changed to:

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 return $null; could be changed to: +

-   throw std::runtime_error(Swig::JavaExceptionMessage(jenv,$error).message());
+   throw std::runtime_error(Swig::JavaExceptionMessage(jenv, $error).message());
 
-

In more complex situations, a separate director:except feature may need to be attached to specific methods.

Below is a complete example demonstrating the use -of directorthrows typemaps. The directorthrows 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. -->

+%module(directors="1") example
+
+%{
+  #include <string>
+  #include <stdexcept>
+%}
+
 // Define exceptions in header section using std::runtime_error
 %define DEFINE_EXCEPTION(NAME)
 %{
-  #include <exception>
   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 &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& 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() {}
   };
 }
 

-In this case the three different directorthrows typemaps will be used +In this case the three different "directorthrows" typemaps will be used to generate the three different exception handlers for meth1, meth2 and meth3. The generated handlers will have "if" blocks for each exception type specified, in -the exception specification or %catches feature. The code block -in the directorthrows typemap should always throw a c++ exception. +the exception specification or %catches feature.

-

Note that the directorthrows typemaps are important +

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 Swig::DirectorException class provides enough information to reconstruct the original exception. In this case removing the -$directorthrowshandlers replacement variable from the +$directorthrowshandlers special variable from the default director:except feature and simply always -throwing a Swig::DirectorException will achieve the desired result. +throwing a Swig::DirectorException will achieve the desired result. Along with this a generic exception feature is added to convert any caught Swig::DirectorExceptions back into the underlying -java exceptions, for each method which may get a generic -DirectorException from a wrapped director method.

+Java exceptions via the Swig::DirectorException::raiseJavaException method, +as demonstrated with %javaexception below: +

-%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 &e) {
+    // raise/throw the Java exception that originally caused the DirectorException
+    e.raiseJavaException(jenv);
     return $null;
   }
 %}
 
-

The throws="Exception" 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.

- -
-
-%feature ("except",throws="Exception")  MyDirectorClass::myDirMeth %{ %}
-
-
- - +

+See the Exception handling with %exception and %javaexception +section for more on converting C++ exceptions to Java exceptions. +

24.6 Accessing protected members

@@ -5643,7 +5674,7 @@ can be wrapped with the Java equivalent, that is, static inner proxy classes.

-$jniinput, $javacall and $packagepath
+$error, $jniinput, $javacall and $packagepath
These special variables are used in the directors typemaps. See Director specific typemaps for details.

@@ -5977,6 +6008,10 @@ is the package name passed from the SWIG command line and $javaclassname-package commandline option is not used to specify the package, then '$packagepath/' will be removed from the resulting output JNI field descriptor. Do not forget the terminating ';' for JNI field descriptors starting with 'L'. If the ';' is left out, Java will generate a "method not found" runtime error. +Note that the $packagepath substitution always uses the path separator '/' when expanded. +The $javaclassname expansion can be confusing as it is normally expanded using the '.' separator. +However, $javaclassname is expanded using the path separator '/' in typemap's "descriptor" attribute +as well as in the "directorthrows" typemap.

@@ -6072,6 +6107,40 @@ The target method is the method in the Java proxy class which overrides the virt +

%typemap(directorthrows)

+
+ +

+Conversion of Java exceptions to C++ exceptions in director method's exception handling. +This typemap is expected to test the $error 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 $error special variable is of type jthrowable and is +substituted with a unique variable name in the generated code. +

+ +

+The example below converts a Java java.lang.IndexOutOfBoundsException exception +to the typemap's type, that is std::out_of_range: +

+ +
+
+%typemap(directorthrows) std::out_of_range %{
+  if (Swig::ExceptionMatches(jenv, $error, "java/lang/IndexOutOfBoundsException")) {
+    throw std::out_of_range(Swig::JavaExceptionMessage(jenv, $error).message());
+  }
+%}
+
+
+ +

+The utility function Swig::ExceptionMatches +and class Swig::JavaExceptionMessage are helpers available when using directors and are described +in the Java Exceptions from Directors section. +

+ +
+

%typemap(javapackage)

diff --git a/Doc/Manual/Warnings.html b/Doc/Manual/Warnings.html index 5b507c123..e0debe41c 100644 --- a/Doc/Manual/Warnings.html +++ b/Doc/Manual/Warnings.html @@ -499,7 +499,7 @@ example.i(4) : Syntax error in input.
  • 474. Method method usage of the optimal attribute ignored in the out typemap as the following cannot be used to generate optimal code: code
  • 475. Multiple calls to method might be generated due to optimal attribute usage in the out typemap.
  • 476. Initialization using std::initializer_list. -
  • 477. Feature director:except on Class::method with $directorthrowshandlers requires directorthrows typemap for exception Exception +
  • 477. No directorthrows typemap defined for type -- cgit v1.2.3