Add feature director:except for improved director exception handling in Java

Closes #91
This commit is contained in:
Marvin Greenberg 2013-10-22 20:31:14 +01:00 • committed by William S Fulton
commit 6736e74127
12 changed files with 1469 additions and 51 deletions

View file

@ -41,6 +41,29 @@ class DirectorMethodException: public Swig::DirectorException {};
#endif
#ifdef SWIGJAVA
// Default for director exception warns about unmapped exceptions now in java
// Suppress warnings for this older test
// %warnfilter(476) Bar;
// Default for java is to throw Swig::DirectorException if no
// direct:except feature. Since methods below have exception specification
// cannot throw director exception.
// Change back to old 2.0 default behavior
%feature("director:except") {
jthrowable $error = jenv->ExceptionOccurred();
if ($error) {
// Dont clear exception, still be active when return to java execution
// Essentially ignore exception occurred -- old behavior.
return $null;
}
}
#endif
#ifdef SWIGRUBY
%feature("director:except") {

View file

@ -24,6 +24,8 @@ CPP_TEST_CASES = \
java_constants \
java_director \
java_director_assumeoverride \
java_director_exception_feature \
java_director_exception_feature_nspace \
java_enums \
java_jnitypes \
java_lib_arrays_dimensionless \
@ -44,8 +46,18 @@ JAVA_PACKAGE = $*
JAVA_PACKAGEOPT = -package $(JAVA_PACKAGE)
SWIGOPT += $(JAVA_PACKAGEOPT)
RUNME_CLASSPATH = -classpath .
ifeq (Cygwin,$(shell uname -o 2>/dev/null))
SEP=;
else
SEP=:
endif
# Custom tests - tests with additional commandline options
java_nspacewithoutpackage.%: JAVA_PACKAGEOPT =
java_director_exception_feature_nspace.%: JAVA_PACKAGEOPT =
java_director_exception_feature_nspace.%: RUNME_CLASSPATH = -classpath .$(SEP)./java_director_exception_feature_nspace
nspace.%: JAVA_PACKAGE = $*Package
nspace_extend.%: JAVA_PACKAGE = $*Package
director_nspace.%: JAVA_PACKAGE = $*Package
@ -84,8 +96,8 @@ setup = \
run_testcase = \
cd $(JAVA_PACKAGE) && $(COMPILETOOL) $(JAVAC) -classpath . `find . -name "*.java"` && cd .. && \
if [ -f $(srcdir)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) ]; then \
$(COMPILETOOL) $(JAVAC) -classpath . -d . $(srcdir)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) && \
env LD_LIBRARY_PATH="$(JAVA_PACKAGE):$$LD_LIBRARY_PATH" PATH="$(JAVA_PACKAGE):$$PATH" SHLIB_PATH="$(JAVA_PACKAGE):$$SHLIB_PATH" DYLD_LIBRARY_PATH="$(JAVA_PACKAGE):$$DYLD_LIBRARY_PATH" $(RUNTOOL) $(JAVA) $(JAVAFLAGS) -classpath . $*_runme; \
$(COMPILETOOL) $(JAVAC) $(RUNME_CLASSPATH) -d . $(srcdir)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) && \
env LD_LIBRARY_PATH="$(JAVA_PACKAGE):$$LD_LIBRARY_PATH" PATH="$(JAVA_PACKAGE):$$PATH" SHLIB_PATH="$(JAVA_PACKAGE):$$SHLIB_PATH" DYLD_LIBRARY_PATH="$(JAVA_PACKAGE):$$DYLD_LIBRARY_PATH" $(RUNTOOL) $(JAVA) $(JAVAFLAGS) $(RUNME_CLASSPATH) $*_runme; \
fi
# Clean: remove testcase directories

View file

@ -0,0 +1,150 @@
import MyNS.*;
import MyNS_JNI.*;
class java_director_exception_feature_nspace_Consts {
public static final String PINGEXCP1 = "Ping MyJavaException1"; // should get translated through an int on ping
public static final String PINGEXCP2 = "Ping MyJavaException2";
public static final String PONGEXCP1 = "Pong MyJavaException1";
public static final String PONGEXCP2 = "Pong MyJavaException2";
public static final String PONGUNEXPECTED = "Pong MyJavaUnexpected";
public static final String TRANSLATED_NPE = "Pong Translated NPE";
public static final String GENERICPONGEXCP1 = "GenericPong Wrapped MyJavaException1";
public static final String GENERICPONGEXCP2 = "GenericPong New Checked Exception";
public static final String GENERICPONGEXCP3 = "GenericPong New Unchecked Exception";
public static final String GENERICPONGEXCP4 = "GenericPong New Exception Without String ctor";
}
// an exception not mentioned or wrapped by the swig interface,
// to reconstruct using generic DirectorException handling
class NewCheckedException extends Exception {
public NewCheckedException(String s) {
super(s);
}
}
// an exception not mentioned or wrapped by the swig interface,
// to reconstruct using generic DirectorException handling
class NewUncheckedException extends RuntimeException {
public NewUncheckedException(String s) {
super(s);
}
}
// an exception not constructable from a string,
// to test DirectorException fallback reconstruction
class UnconstructableException extends Exception {
private int extrastate;
public UnconstructableException(int a, String s) {
super(s);
extrastate = a;
}
}
class java_director_exception_feature_nspace_MyFooDirectorImpl extends Foo {
public java_director_exception_feature_nspace_MyFooDirectorImpl() { };
@Override
public String ping(int excp) throws MyJavaException1, MyJavaException2 {
if (excp == 1) throw new MyJavaException1(java_director_exception_feature_nspace_Consts.PINGEXCP1);
if (excp == 2) throw new MyJavaException2(java_director_exception_feature_nspace_Consts.PINGEXCP2);
return "Ping director returned";
}
@Override
public String pong(int excp) throws MyJavaException1, MyJavaException2, MyJavaUnexpected {
if (excp == 1) throw new MyJavaException1(java_director_exception_feature_nspace_Consts.PONGEXCP1);
if (excp == 2) throw new MyJavaException2(java_director_exception_feature_nspace_Consts.PONGEXCP2);
if (excp == 3) throw new MyJavaUnexpected(java_director_exception_feature_nspace_Consts.PONGUNEXPECTED);
if (excp == 4) throw new java.lang.NullPointerException(java_director_exception_feature_nspace_Consts.TRANSLATED_NPE); // should be translated to ::Unexpected
return "Pong director returned";
}
@Override
public String genericpong(int excp) throws MyJavaException1, NewCheckedException, UnconstructableException {
if (excp == 1)
throw new MyJavaException1(java_director_exception_feature_nspace_Consts.GENERICPONGEXCP1);
if (excp == 2)
throw new NewCheckedException(java_director_exception_feature_nspace_Consts.GENERICPONGEXCP2);
if (excp == 3)
throw new NewUncheckedException(java_director_exception_feature_nspace_Consts.GENERICPONGEXCP3);
if (excp == 4)
throw new UnconstructableException(1, java_director_exception_feature_nspace_Consts.GENERICPONGEXCP4);
return "GenericPong director returned";
}
}
public class java_director_exception_feature_nspace_runme {
static {
try {
System.loadLibrary("java_director_exception_feature_nspace");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
System.exit(1);
}
}
public static void fail(String msg) {
System.err.println(msg); System.exit(1);
}
public static void failif(boolean cond, String msg) {
if (cond) fail(msg);
}
public static void main(String argv[]) {
Bar b = new Bar(new java_director_exception_feature_nspace_MyFooDirectorImpl());
try {
try { b.ping(0); } catch (Exception e)
{ fail("Exception should not have been thrown: " + e + " from ping(0)"); }
try { b.ping(1); fail("No exception thrown in ping(1)"); } catch (MyJavaException1 e)
// Should say "Threw some integer", see java_director_exception_feature.i Foo::ping throws a "1"
{ failif( ! "Threw some integer".equals(e.getMessage()), "Ping exception not translated through int: '" + e.getMessage() + "'"); }
try { b.ping(2); fail("No exception thrown in ping(2)"); } catch (MyJavaException2 e)
{ failif( ! java_director_exception_feature_nspace_Consts.PINGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); }
try { b.pong(0); } catch (Exception e)
{ fail("Exception should not have been thrown: " + e + " from pong(0)"); }
try { b.pong(1); fail("No exception thrown in pong(1)"); } catch (MyJavaException1 e)
{ failif( ! java_director_exception_feature_nspace_Consts.PONGEXCP1.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); }
try { b.pong(2); fail("No exception thrown in pong(2)");} catch (MyJavaException2 e)
{ failif( ! java_director_exception_feature_nspace_Consts.PONGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); }
try { b.pong(3); fail("No exception thrown in pong(3)");} catch (MyJavaUnexpected e)
{ failif( ! java_director_exception_feature_nspace_Consts.PONGUNEXPECTED.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); }
try { b.pong(4); fail("No exception thrown in pong(4)"); } catch (MyJavaUnexpected e)
{ failif( ! java_director_exception_feature_nspace_Consts.TRANSLATED_NPE.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); }
try { b.genericpong(0); }
catch (Exception e) {
fail("Exception should not have been thrown: " + e + " from genericpong(0)");
}
try { b.genericpong(1); fail("No exception thrown in genericpong(1)"); }
catch (MyJavaException1 e) {
failif( ! java_director_exception_feature_nspace_Consts.GENERICPONGEXCP1.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'");
}
try { b.genericpong(2); fail("No exception thrown in genericpong(2)");}
catch (NewCheckedException e) {
failif( ! java_director_exception_feature_nspace_Consts.GENERICPONGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'");
}
try { b.genericpong(3); fail("No exception thrown in genericpong(3)");}
catch (NewUncheckedException e) {
failif( ! java_director_exception_feature_nspace_Consts.GENERICPONGEXCP3.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'");
}
try { b.genericpong(4); fail("No exception thrown in genericpong(4)");}
catch (RuntimeException e) {
failif ( e.getClass() != RuntimeException.class, "Exception " + e + " is not exactly RumtimeException");
failif( ! java_director_exception_feature_nspace_Consts.GENERICPONGEXCP4.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'");
}
}
catch (Exception e) {
e.printStackTrace();
fail("Unexpected exception thrown or exception not mapped properly");
}
}
}

View file

@ -0,0 +1,152 @@
import java_director_exception_feature.*;
class java_director_exception_feature_Consts {
public static final String PINGEXCP1 = "Ping MyJavaException1"; // should get translated through an int on ping
public static final String PINGEXCP2 = "Ping MyJavaException2";
public static final String PONGEXCP1 = "Pong MyJavaException1";
public static final String PONGEXCP2 = "Pong MyJavaException2";
public static final String PONGUNEXPECTED = "Pong MyJavaUnexpected";
public static final String TRANSLATED_NPE = "Pong Translated NPE";
public static final String GENERICPONGEXCP1 = "GenericPong Wrapped MyJavaException1";
public static final String GENERICPONGEXCP2 = "GenericPong New Checked Exception";
public static final String GENERICPONGEXCP3 = "GenericPong New Unchecked Exception";
public static final String GENERICPONGEXCP4 = "GenericPong New Exception Without String ctor";
}
// an exception not mentioned or wrapped by the swig interface,
// to reconstruct using generic DirectorException handling
class NewCheckedException extends Exception {
public NewCheckedException(String s) {
super(s);
}
}
// an exception not mentioned or wrapped by the swig interface,
// to reconstruct using generic DirectorException handling
class NewUncheckedException extends RuntimeException {
public NewUncheckedException(String s) {
super(s);
}
}
// an exception not constructable from a string,
// to test DirectorException fallback reconstruction
class UnconstructableException extends Exception {
private int extrastate;
public UnconstructableException(int a, String s) {
super(s);
extrastate = a;
}
}
class java_director_exception_feature_MyFooDirectorImpl extends Foo {
public java_director_exception_feature_MyFooDirectorImpl() { };
@Override
public String ping(int excp) throws MyJavaException1, MyJavaException2 {
if (excp == 1) throw new MyJavaException1(java_director_exception_feature_Consts.PINGEXCP1);
if (excp == 2) throw new MyJavaException2(java_director_exception_feature_Consts.PINGEXCP2);
return "Ping director returned";
}
@Override
public String pong(int excp) throws MyJavaException1, MyJavaException2, MyJavaUnexpected {
if (excp == 1) throw new MyJavaException1(java_director_exception_feature_Consts.PONGEXCP1);
if (excp == 2) throw new MyJavaException2(java_director_exception_feature_Consts.PONGEXCP2);
if (excp == 3) throw new MyJavaUnexpected(java_director_exception_feature_Consts.PONGUNEXPECTED);
if (excp == 4) throw new java.lang.NullPointerException(java_director_exception_feature_Consts.TRANSLATED_NPE); // should be translated to ::Unexpected
return "Pong director returned";
}
@Override
public String genericpong(int excp) throws MyJavaException1, NewCheckedException, UnconstructableException {
if (excp == 1)
throw new MyJavaException1(java_director_exception_feature_Consts.GENERICPONGEXCP1);
if (excp == 2)
throw new NewCheckedException(java_director_exception_feature_Consts.GENERICPONGEXCP2);
if (excp == 3)
throw new NewUncheckedException(java_director_exception_feature_Consts.GENERICPONGEXCP3);
if (excp == 4)
throw new UnconstructableException(1, java_director_exception_feature_Consts.GENERICPONGEXCP4);
return "GenericPong director returned";
}
}
public class java_director_exception_feature_runme {
static {
try {
System.loadLibrary("java_director_exception_feature");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
System.exit(1);
}
}
public static void fail(String msg) {
System.err.println(msg); System.exit(1);
}
public static void failif(boolean cond, String msg) {
if (cond) fail(msg);
}
public static void main(String argv[]) {
Bar b = new Bar(new java_director_exception_feature_MyFooDirectorImpl());
try {
try { b.ping(0); } catch (Exception e)
{ fail("Exception should not have been thrown: " + e + " from ping(0)"); }
try { b.ping(1); fail("No exception thrown in ping(1)"); } catch (MyJavaException1 e)
// Should say "Threw some integer", see java_director_exception_feature.i Foo::ping throws a "1"
{ failif( ! "Threw some integer".equals(e.getMessage()), "Ping exception not translated through int: '" + e.getMessage() + "'"); }
try { b.ping(2); fail("No exception thrown in ping(2)"); } catch (MyJavaException2 e)
{ failif( ! java_director_exception_feature_Consts.PINGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); }
try { b.pong(0); } catch (Exception e)
{ fail("Exception should not have been thrown: " + e + " from pong(0)"); }
try { b.pong(1); fail("No exception thrown in pong(1)"); } catch (MyJavaException1 e)
{ failif( ! java_director_exception_feature_Consts.PONGEXCP1.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); }
try { b.pong(2); fail("No exception thrown in pong(2)");} catch (MyJavaException2 e)
{ failif( ! java_director_exception_feature_Consts.PONGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); }
try { b.pong(3); fail("No exception thrown in pong(3)");} catch (MyJavaUnexpected e)
{ failif( ! java_director_exception_feature_Consts.PONGUNEXPECTED.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); }
try { b.pong(4); fail("No exception thrown in pong(4)"); } catch (MyJavaUnexpected e)
{ failif( ! java_director_exception_feature_Consts.TRANSLATED_NPE.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'"); }
try { b.genericpong(0); }
catch (Exception e) {
fail("Exception should not have been thrown: " + e + " from genericpong(0)");
}
try { b.genericpong(1); fail("No exception thrown in genericpong(1)"); }
catch (MyJavaException1 e) {
failif( ! java_director_exception_feature_Consts.GENERICPONGEXCP1.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'");
}
try { b.genericpong(2); fail("No exception thrown in genericpong(2)");}
catch (NewCheckedException e) {
failif( ! java_director_exception_feature_Consts.GENERICPONGEXCP2.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'");
}
try { b.genericpong(3); fail("No exception thrown in genericpong(3)");}
catch (NewUncheckedException e) {
failif( ! java_director_exception_feature_Consts.GENERICPONGEXCP3.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'");
}
try { b.genericpong(4); fail("No exception thrown in genericpong(4)");}
catch (RuntimeException e) {
failif ( e.getClass() != RuntimeException.class, "Exception " + e + " is not exactly RumtimeException");
failif( ! java_director_exception_feature_Consts.GENERICPONGEXCP4.equals(e.getMessage()), "Expected exception has unexpected message: '" + e.getMessage() + "'");
}
}
catch (Exception e) {
e.printStackTrace();
fail("Unexpected exception thrown or exception not mapped properly");
}
}
}

View file

@ -0,0 +1,209 @@
%module(directors="1") java_director_exception_feature
%include <std_except.i>
%{
#if defined(_MSC_VER)
#pragma warning(disable: 4290) // C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
#endif
#include <string>
%}
%include <std_string.i>
// DEFINE exceptions in header section using std::runtime_error
%{
#include <exception>
#include <iostream>
namespace MyNS {
struct Exception1 : public std::runtime_error {
Exception1(const std::string& what):runtime_error(what) {}
};
struct Exception2 : public std::runtime_error {
Exception2(const std::string& what):runtime_error(what) {}
};
struct Unexpected : public std::runtime_error {
Unexpected(const std::string& what):runtime_error(what) {}
};
}
%}
// Add an explicit handler for Foo::ping, mapping one java exception back to an 'int'
%feature("director:except") MyNS::Foo::ping {
jthrowable $error = jenv->ExceptionOccurred();
if ($error) {
jenv->ExceptionClear(); // clear java exception since mapping to c++ exception
if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyJavaException1")) {
throw 1;
} else if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyJavaException2")) {
std::string msg(Swig::JavaExceptionMessage(jenv,$error).message());
throw MyNS::Exception2(msg);
} else {
std::cerr << "Test failed, unexpected exception thrown: " <<
Swig::JavaExceptionMessage(jenv,$error).message() << std::endl;
throw std::runtime_error("unexpected exception in Foo::ping");
}
}
}
// Use default handler on Foo::pong, with directorthrows typemaps
// directorthrows typemaps for java->c++ conversions
%typemap(directorthrows) MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected %{
if (Swig::ExceptionMatches(jenv, $error, "$packagepath/$javaclassname")) {
std::string msg(Swig::JavaExceptionMessage(jenv,$error).message());
throw $1_type(msg);
}
%}
// Override the director:except feature so exception specification is not violated
// (Cannot use built-in default of throw DirectorException)
%feature("director:except") MyNS::Foo::pong %{
jthrowable $error = jenv->ExceptionOccurred();
if ($error) {
jenv->ExceptionClear();
$directorthrowshandlers
throw ::MyNS::Unexpected(Swig::JavaExceptionMessage(jenv,$error).message());
}
%}
// TODO 'throws' typemap emitted by emit_action (emit.cxx) has no way
// to get access to language specific special variables like
// $javaclassname or $packagepath ("java_director_exception_feature" here)
// throws typemaps for c++->java exception conversions
%typemap(throws,throws="MyJavaException1") MyNS::Exception1 %{
jclass excpcls = jenv->FindClass("java_director_exception_feature/MyJavaException1");
if (excpcls) {
jenv->ThrowNew(excpcls, $1.what());
}
return $null;
%}
%typemap(throws,throws="MyJavaException1") int %{
jclass excpcls = jenv->FindClass("java_director_exception_feature/MyJavaException1");
if (excpcls) {
jenv->ThrowNew(excpcls, "Threw some integer");
}
return $null;
%}
%typemap(throws,throws="MyJavaException2") MyNS::Exception2 %{
jclass excpcls = jenv->FindClass("java_director_exception_feature/MyJavaException2");
if (excpcls) {
jenv->ThrowNew(excpcls, $1.what());
}
return $null;
%}
%typemap(throws,throws="MyJavaUnexpected") MyNS::Unexpected %{
jclass excpcls = jenv->FindClass("java_director_exception_feature/MyJavaUnexpected");
if (excpcls) {
jenv->ThrowNew(excpcls, $1.what());
}
return $null;
%}
// Use generic exception translation approach like python, ruby
%feature("director:except") MyNS::Foo::genericpong {
jthrowable $error = jenv->ExceptionOccurred();
if ($error) {
jenv->ExceptionClear();
throw Swig::DirectorException(jenv,$error);
}
}
// %exception with throws attribute. Need throws attribute for checked exceptions
%feature ("except",throws="Exception") MyNS::Foo::genericpong %{
%}
%feature ("except",throws="Exception") MyNS::Bar::genericpong %{
try { $action }
catch (Swig::DirectorException & direxcp) {
direxcp.raiseJavaException(jenv); // jenv always available in JNI code
return $null;
}
%}
%feature("director") Foo;
// Rename exceptions on java side to make translation of exceptions more clear
%rename(MyJavaException1,fullname=1) MyNS::Exception1;
%rename(MyJavaException2,fullname=1) MyNS::Exception2;
%rename(MyJavaUnexpected,fullname=1) MyNS::Unexpected;
%typemap(javabase) ::MyNS::Exception1,::MyNS::Exception2,::MyNS::Unexpected "java.lang.Exception";
%rename(getMessage) what(); // Rename all what() methods
namespace MyNS {
struct Exception1 {
Exception1(const std::string& what);
const char * what();
};
struct Exception2 {
Exception2(const std::string& what);
const char * what();
};
struct Unexpected {
Unexpected(const std::string& what);
const char * what();
};
}
// In general it is better to use %catches instead of an exception specification on the method
// since violating an exception specification calls terminate() preventing catch-all behavior
// like throwing std::runtime_error. But an exception specification must be used if the
// actual interface being wrapped does use them.
%catches(MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected) MyNS::Foo::pong;
%catches(MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected) MyNS::Bar::pong;
%inline %{
namespace MyNS {
class Foo {
public:
virtual ~Foo() {}
// ping java implementation throws a java Exception1 or an Exception2 if excp is 1 or 2.
// pong java implementation throws Exception1,Exception2,Unexpected,NullPointerException for 1,2,3,4
virtual std::string ping(int excp) throw(int,MyNS::Exception2) = 0;
virtual std::string pong(int excp) /* throws MyNS::Exception1 MyNS::Exception2 MyNS::Unexpected) */ = 0;
virtual std::string genericpong(int excp) /* unspecified throws - exception is always DirectorException in C++, translated back to whatever thrown in java */ = 0;
};
// Make a bar from a foo, so a call to Java Bar
// goes Java Bar -> C++ Bar -> C++ Foo -> Java Foo Director
class Bar {
public:
Bar(Foo* d) { delegate=d; }
virtual std::string ping(int excp) throw(int,MyNS::Exception2)
{
return delegate->ping(excp);
}
virtual std::string pong(int excp) /* throws MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected */
{
return delegate->pong(excp);
}
virtual std::string genericpong(int excp)
{
return delegate->genericpong(excp);
}
private:
Foo * delegate;
};
}
%}

View file

@ -0,0 +1,216 @@
%module(directors="1") java_director_exception_feature_nspace
%include <std_except.i>
%nspace; // turn namespace feature on for everything.
// When using namespaces with no -package, must put JNI classes into a namespace
%pragma(java) jniclasspackage=%{MyNS_JNI%}
%warnfilter(826);
%{
#if defined(_MSC_VER)
#pragma warning(disable: 4290) // C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
#endif
#include <string>
%}
%include <std_string.i>
// DEFINE exceptions in header section using std::runtime_error
%{
#include <exception>
#include <iostream>
namespace MyNS {
struct Exception1 : public std::runtime_error {
Exception1(const std::string& what):runtime_error(what) {}
};
struct Exception2 : public std::runtime_error {
Exception2(const std::string& what):runtime_error(what) {}
};
struct Unexpected : public std::runtime_error {
Unexpected(const std::string& what):runtime_error(what) {}
};
}
%}
// Add an explicit handler for Foo::ping, mapping one java exception back to an 'int'
%feature("director:except") MyNS::Foo::ping {
jthrowable $error = jenv->ExceptionOccurred();
if ($error) {
jenv->ExceptionClear(); // clear java exception since mapping to c++ exception
if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyNS/MyJavaException1")) {
throw 1;
} else if (Swig::ExceptionMatches(jenv,$error,"$packagepath/MyNS/MyJavaException2")) {
std::string msg(Swig::JavaExceptionMessage(jenv,$error).message());
throw MyNS::Exception2(msg);
} else {
std::cerr << "Test failed, unexpected exception thrown: " <<
Swig::JavaExceptionMessage(jenv,$error).message() << std::endl;
throw std::runtime_error("unexpected exception in Foo::ping");
}
}
}
// Use default handler on Foo::pong, with directorthrows typemaps
// directorthrows typemaps for java->c++ conversions
%typemap(directorthrows) MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected %{
if (Swig::ExceptionMatches(jenv, $error, "$packagepath/$javaclassname")) {
std::string msg(Swig::JavaExceptionMessage(jenv,$error).message());
throw $1_type(msg);
}
%}
// Override the director:except feature so exception specification is not violated
// (Cannot use built-in default of throw DirectorException)
%feature("director:except") MyNS::Foo::pong %{
jthrowable $error = jenv->ExceptionOccurred();
if ($error) {
jenv->ExceptionClear();
$directorthrowshandlers
throw ::MyNS::Unexpected(Swig::JavaExceptionMessage(jenv,$error).message());
}
%}
// TODO 'throws' typemap emitted by emit_action (emit.cxx) has no way
// to get access to language specific special variables like
// $javaclassname or $packagepath ("java_director_exception_feature" here)
// throws typemaps for c++->java exception conversions
%typemap(throws,throws="MyNS.MyJavaException1") MyNS::Exception1 %{
jclass excpcls = jenv->FindClass("MyNS/MyJavaException1");
if (excpcls) {
jenv->ThrowNew(excpcls, $1.what());
}
return $null;
%}
%typemap(throws,throws="MyNS.MyJavaException1") int %{
jclass excpcls = jenv->FindClass("MyNS/MyJavaException1");
if (excpcls) {
jenv->ThrowNew(excpcls, "Threw some integer");
}
return $null;
%}
%typemap(throws,throws="MyNS.MyJavaException2") MyNS::Exception2 %{
jclass excpcls = jenv->FindClass("MyNS/MyJavaException2");
if (excpcls) {
jenv->ThrowNew(excpcls, $1.what());
}
return $null;
%}
%typemap(throws,throws="MyNS.MyJavaUnexpected") MyNS::Unexpected %{
jclass excpcls = jenv->FindClass("MyNS/MyJavaUnexpected");
if (excpcls) {
jenv->ThrowNew(excpcls, $1.what());
}
return $null;
%}
// Use generic exception translation approach like python, ruby
%feature("director:except") MyNS::Foo::genericpong {
jthrowable $error = jenv->ExceptionOccurred();
if ($error) {
jenv->ExceptionClear();
throw Swig::DirectorException(jenv,$error);
}
}
// %exception with throws attribute. Need throws attribute for checked exceptions
%feature ("except",throws="Exception") MyNS::Foo::genericpong %{
%}
%feature ("except",throws="Exception") MyNS::Bar::genericpong %{
try { $action }
catch (Swig::DirectorException & direxcp) {
direxcp.raiseJavaException(jenv); // jenv always available in JNI code
return $null;
}
%}
%feature("director") Foo;
// Rename exceptions on java side to make translation of exceptions more clear
%rename(MyJavaException1,fullname=1) MyNS::Exception1;
%rename(MyJavaException2,fullname=1) MyNS::Exception2;
%rename(MyJavaUnexpected,fullname=1) MyNS::Unexpected;
%typemap(javabase) ::MyNS::Exception1,::MyNS::Exception2,::MyNS::Unexpected "java.lang.Exception";
%rename(getMessage) what(); // Rename all what() methods
namespace MyNS {
struct Exception1 {
Exception1(const std::string& what);
const char * what();
};
struct Exception2 {
Exception2(const std::string& what);
const char * what();
};
struct Unexpected {
Unexpected(const std::string& what);
const char * what();
};
}
// In general it is better to use %catches instead of an exception specification on the method
// since violating an exception specification calls terminate() preventing catch-all behavior
// like throwing std::runtime_error. But an exception specification must be used if the
// actual interface being wrapped does use them.
%catches(MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected) MyNS::Foo::pong;
%catches(MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected) MyNS::Bar::pong;
%inline %{
namespace MyNS {
class Foo {
public:
virtual ~Foo() {}
// ping java implementation throws a java Exception1 or an Exception2 if excp is 1 or 2.
// pong java implementation throws Exception1,Exception2,Unexpected,NullPointerException for 1,2,3,4
virtual std::string ping(int excp) throw(int,MyNS::Exception2) = 0;
virtual std::string pong(int excp) /* throws MyNS::Exception1 MyNS::Exception2 MyNS::Unexpected) */ = 0;
virtual std::string genericpong(int excp) /* unspecified throws - exception is always DirectorException in C++, translated back to whatever thrown in java */ = 0;
};
// Make a bar from a foo, so a call to Java Bar
// goes Java Bar -> C++ Bar -> C++ Foo -> Java Foo Director
class Bar {
public:
Bar(Foo* d) { delegate=d; }
virtual std::string ping(int excp) throw(int,MyNS::Exception2)
{
return delegate->ping(excp);
}
virtual std::string pong(int excp) /* throws MyNS::Exception1,MyNS::Exception2,MyNS::Unexpected */
{
return delegate->pong(excp);
}
virtual std::string genericpong(int excp)
{
return delegate->genericpong(excp);
}
private:
Foo * delegate;
};
}
%}