Further director documentation contributed by Scott Michel
git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@5239 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
parent
7567736299
commit
dd8b2cfb0d
1 changed files with 177 additions and 84 deletions
|
|
@ -4105,15 +4105,12 @@ This example contains some useful functionality which you may want in your code.
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Like the Python module, Java supports the <i>directors</i> feature. The
|
Like the Python module, Java supports the <i>directors</i> feature.
|
||||||
<i>directors</i> feature creates a shadow class in the SWIG-generated C++ code
|
The <i>directors</i> feature creates a hidden C++ director class that overrides the wrapped C++ class's virtual methods.
|
||||||
that redirects a C++ class's virtual methods to a Java subclass, when the Java
|
The hidden C++ class upcalls from C++ to Java when a Java object is derived from Java proxy class and the called method is
|
||||||
subclass overrides the method. The shadow class upcalls from C++ to Java when a
|
overridden by the derived subclass.
|
||||||
Java object is derived from Java proxy class and the called method is
|
The director class calls the wrapped C++ class's method if the called method isn't overridden by the proxy-derived Java class.
|
||||||
overridden by the derived subclass. If the called method isn't overridden by
|
If the wrapped C++ class's method is pure virtual, the director class throws a Java RuntimeException as a reminder.
|
||||||
the proxy-derived class, the shadow director class will call the original C++
|
|
||||||
method or throw a Java Runtime exception if the C++ class is an abstract base
|
|
||||||
class.
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Consider the following SWIG interface file:
|
Consider the following SWIG interface file:
|
||||||
|
|
@ -4145,14 +4142,10 @@ Notes:
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The following <code>director_derived</code> Java class overrides the C++ and
|
The following <code>director_derived</code> Java class overrides the C++ and Java proxy class <code>director_example</code>'s upcall_method() method.
|
||||||
Java proxy class <code>director_example</code>'s upcall_method() method. When
|
When C++ code invokes <code>director_example::upcall_method()</code>, the SWIG-generated C++ code redirects the call via JNI to the Java <code>director_derived</code> subclass.
|
||||||
C++ code invokes <code>director_example::upcall_method()</code>, the
|
The following code would output "director_derived::upcall_method() invoked".
|
||||||
SWIG-generated C++ code redirects the call via JNI to the Java
|
Naturally, the SWIG generated C++ code and the generated Java intermediate class marshall and convert arguments between C++ and Java when needed.
|
||||||
<code>director_derived</code> subclass. The following code would output
|
|
||||||
"director_derived::upcall_method() invoked". Naturally, the SWIG generated C++
|
|
||||||
code and the generated Java intermediate class marshall and convert arguments
|
|
||||||
between C++ and Java when needed.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<pre><code>
|
<pre><code>
|
||||||
|
|
@ -4187,16 +4180,11 @@ individual C++/SWIG-generated proxy classes.
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The <code>%typemap(directorin)</code> type map is used for converting arguments
|
The <code>%typemap(directorin)</code> type map is used for converting arguments in the C++ director class to the appropriate JNI type before the upcall to Java.
|
||||||
in the C++ director class to the appropriate JNI type before the upcall
|
This typemap also specifies the JNI field descriptor for the type.
|
||||||
to Java. This typemap also specifies the JNI field descriptor for the
|
For example, integers are converted as follows: <pre>%typemap(directorin,parse="I") int "$input = (jint) $1;"</pre> <code>$input</code> is the SWIG name of the JNI temporary variable passed to Java in the upcall.
|
||||||
type. For example, integers are converted as follows:
|
The <code>parse="I"</code> will put an <code>I</code> into the JNI field descriptor that identifies the Java method that will be upcalled.
|
||||||
<pre>%typemap(directorin,parse="I") int "$input = (jint) $1;"</pre>
|
For more about JNI field descriptors and their importance, refer to Sun's JNI documentation.
|
||||||
<code>$input</code> is the SWIG name of the JNI temporary variable passed to
|
|
||||||
Java in the upcall. The <code>parse="I"</code> will put an <code>I</code>
|
|
||||||
into the JNI field descriptor that identifies the Java method that will be
|
|
||||||
upcalled. For more about JNI field descriptors and their importance, refer to
|
|
||||||
Sun's JNI documentation.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
A typemap for C character strings is:
|
A typemap for C character strings is:
|
||||||
|
|
@ -4205,14 +4193,9 @@ individual C++/SWIG-generated proxy classes.
|
||||||
%{ $input = jenv->NewStringUTF($1); %}</pre>
|
%{ $input = jenv->NewStringUTF($1); %}</pre>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
User-defined types have the default jnidesc typemap
|
User-defined types have the default jnidesc typemap "<code>L$packagepath/$javaclassname;</code>" where <code>$packagepath</code> is the package name passed from the SWIG command line and <code>$javaclassname</code> is the Java proxy class' name.
|
||||||
"<code>L$packagepath/$javaclassname;</code>" where <code>$packagepath</code>
|
If the Java package name is not set, '$packagepath/' will be removed from the resulting output JNI field descriptor.
|
||||||
is the package name passed from the SWIG command line and
|
<b>DO NOT FORGET THE ';' terminating JNI field descriptors starting with 'L' (aka Java objects).</b> If the ';' is left out, Java will generate a "method not found" warning.
|
||||||
<code>$javaclassname</code> is the Java proxy class' name. If the Java
|
|
||||||
package name is not set, '$packagepath/' will be removed from the resulting
|
|
||||||
output JNI field descriptor. <b>DO NOT FORGET THE ';' terminating JNI field
|
|
||||||
descriptors starting with 'L' (aka Java objects).</b> If the ';' is left out,
|
|
||||||
Java will generate a "method not found" warning.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -4222,13 +4205,9 @@ individual C++/SWIG-generated proxy classes.
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This typemap specifies argument conversion in the generated Java upcall code.
|
This typemap specifies argument conversion in the generated Java upcall code.
|
||||||
Normally, this typemap should be specified as: <pre>%typemap(javadirectorin)
|
Normally, this typemap should be specified as: <pre>%typemap(javadirectorin) user_def_class_here "$jniinput"</pre> <code>$jniinput</code> is the variable name passed from JNI to the intermediate class method's upcall code.
|
||||||
user_def_class_here "$jniinput"</pre> <code>$jniinput</code> is the variable
|
For the default typemaps provided by the Java module, nothing special is done.
|
||||||
name passed from JNI to the intermediate class method's upcall code. For the
|
The default handling for pointers and references, such as the <code>SWIGTYPE &</code> javadirectorin typemap, a temporary object is created to encapsulate the corresponding C++ object.
|
||||||
default typemaps provided by the Java module, nothing special is done. The
|
|
||||||
default handling for pointers and references, such as the <code>SWIGTYPE
|
|
||||||
&</code> javadirectorin typemap, a temporary object is created to encapsulate
|
|
||||||
the corresponding C++ object.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a name="java_director_javadirectorout"></a>
|
<a name="java_director_javadirectorout"></a>
|
||||||
|
|
@ -4236,17 +4215,14 @@ individual C++/SWIG-generated proxy classes.
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This typemap specifies argument conversion from the generated Java
|
This typemap specifies argument conversion from the generated Java upcall code.
|
||||||
upcall code. Normally, this typemap should be specified as:
|
Normally, this typemap should be specified as:
|
||||||
<br>
|
<br>
|
||||||
<pre>%typemap(javadirectorout) user_def_class_here "$javacall"</pre>
|
<pre>%typemap(javadirectorout) user_def_class_here "$javacall"</pre>
|
||||||
<br>
|
<br>
|
||||||
$javacall is the SWIG-generated call to the derived class's
|
$javacall is the SWIG-generated call to the derived class's method, which is about to be upcalled.
|
||||||
method, which is about to be upcalled. For the default typemaps
|
For the default typemaps provided by the Java module, nothing special is done.
|
||||||
provided by the Java module, nothing special is done. The default
|
The default handling for pointers and references, such as <code>SWIGTYPE &</code> javadirectorout typemap, the pointer is extracted from the object returned by the derived class's method call and returned back to the C++ code.
|
||||||
handling for pointers and references, such as <code>SWIGTYPE &</code>
|
|
||||||
javadirectorout typemap, the pointer is extracted from the object returned
|
|
||||||
by the derived class's method call and returned back to the C++ code.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a name="java_director_javapackage"></a>
|
<a name="java_director_javapackage"></a>
|
||||||
|
|
@ -4254,9 +4230,8 @@ individual C++/SWIG-generated proxy classes.
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The <code>javapackage</code> typemap is optional; it serves to identify
|
The <code>javapackage</code> typemap is optional; it serves to identify a class's Java package.
|
||||||
a class's Java package. This typemap should be used in conjunction with
|
This typemap should be used in conjunction with classes that are defined outside of the current SWIG interface file.
|
||||||
classes that are defined outside of the current SWIG interface file.
|
|
||||||
For example:
|
For example:
|
||||||
<br>
|
<br>
|
||||||
<code><pre>
|
<code><pre>
|
||||||
|
|
@ -4276,11 +4251,9 @@ individual C++/SWIG-generated proxy classes.
|
||||||
}
|
}
|
||||||
</pre></code>
|
</pre></code>
|
||||||
<br>
|
<br>
|
||||||
Assume that the Foo class is part of the Java package <i>wombat.foo</i> but
|
Assume that the Foo class is part of the Java package <i>wombat.foo</i> but the above interface file is part of the Java package <i>wombat.example</i>.
|
||||||
the above interface file is part of the Java package <i>wombat.example</i>.
|
Without the <code>javapackage</code> typemap, SWIG will assume that the Foo class belongs to <i>wombat.example</i> class.
|
||||||
Without the <code>javapackage</code> typemap, SWIG will assume that the Foo
|
The corrected interface file looks like:
|
||||||
class belongs to <i>wombat.example</i> class. The corrected interface file
|
|
||||||
looks like:
|
|
||||||
<br>
|
<br>
|
||||||
<code><pre>
|
<code><pre>
|
||||||
// class Foo is handled in a different interface file:
|
// class Foo is handled in a different interface file:
|
||||||
|
|
@ -4300,8 +4273,7 @@ individual C++/SWIG-generated proxy classes.
|
||||||
</pre></code>
|
</pre></code>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Practically speaking, you should create a separate SWIG interface file, which
|
Practically speaking, you should create a separate SWIG interface file, which is %import-ed into each SWIG interface file, when you have multiple Java packages:
|
||||||
is %import-ed into each SWIG interface file, when you have multiple Java packages:
|
|
||||||
<br>
|
<br>
|
||||||
<code><pre>
|
<code><pre>
|
||||||
%typemap("javapackage") SWIGTYPE, SWIGTYPE *, SWIGTYPE & "package.for.most.classes";
|
%typemap("javapackage") SWIGTYPE, SWIGTYPE *, SWIGTYPE & "package.for.most.classes";
|
||||||
|
|
@ -4311,9 +4283,7 @@ individual C++/SWIG-generated proxy classes.
|
||||||
/* etc */
|
/* etc */
|
||||||
</pre></code>
|
</pre></code>
|
||||||
<br>
|
<br>
|
||||||
The basic strategy is to provide a default package typemap for the majority
|
The basic strategy here is to provide a default package typemap for the majority of the classes, only providing <code>javapackage</code> typemaps for the exceptions.
|
||||||
of the classes, only providing <code>javapackage</code> typemaps for the
|
|
||||||
exceptions.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a name="java_director_recursive"></a>
|
<a name="java_director_recursive"></a>
|
||||||
|
|
@ -4321,11 +4291,8 @@ individual C++/SWIG-generated proxy classes.
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The Java module's director code attempts to prevent recursion from the C++
|
The Java module's director code attempts to prevent recursion from the C++ shadow class's code to Java and back through the C++ shadow class.
|
||||||
shadow class's code to Java and back through the C++ shadow class. The most
|
The most common reason for this loop is a typo in the Java derived class, e.g., <i>ucpcall_method</i> instead of <i>upcall_method</i> in the following Java code:
|
||||||
common reason for this loop is a typo in the Java derived class, e.g.,
|
|
||||||
<i>ucpcall_method</i> instead of <i>upcall_method</i> in the following
|
|
||||||
Java code:
|
|
||||||
<pre>
|
<pre>
|
||||||
public class director_derived extends director_example
|
public class director_derived extends director_example
|
||||||
{
|
{
|
||||||
|
|
@ -4338,15 +4305,12 @@ public class director_derived extends director_example
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
Another common typo is a mismatch between the arguments specified in the C++
|
Another common typo is a mismatch between the arguments specified in the C++ method declaration and the Java subclass's method declaration.
|
||||||
method declaration and the Java subclass's method declaration. A
|
A <code>SWIG_JavaDirectorRicochet</code> exception is raised in Java when this particular problem is detected at runtime.
|
||||||
<code>SWIG_JavaDirectorRicochet</code> exception is raised in Java when this
|
|
||||||
particular problem is detected at runtime.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This feature can be turned off in the SWIG interface file can be turned
|
This feature can be turned off in the SWIG interface file can be turned off for the entire class,
|
||||||
off for the entire class,
|
|
||||||
<pre>
|
<pre>
|
||||||
%feature("director:recursive") director_example
|
%feature("director:recursive") director_example
|
||||||
</pre>
|
</pre>
|
||||||
|
|
@ -4361,21 +4325,150 @@ or selectively for individual methods:
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This section is intended to address frequently asked questions and frequently
|
This section is intended to address frequently asked questions and frequently encountered problems when using Java directors.
|
||||||
encountered problems when using Java directors.
|
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ol>
|
||||||
<li><i>When my program starts up, it complains that </i>method_foo<i> cannot
|
<li><i>When my program starts up, it complains that </i>method_foo<i> cannot
|
||||||
be found in a Java method called </i>swig_module_init<i>. How do I fix
|
be found in a Java method called </i>swig_module_init<i>. How do I fix
|
||||||
this?</i></li>
|
this?</i></li>
|
||||||
|
|
||||||
<p> Open up the C++ wrapper source code file and look for
|
<p>
|
||||||
<code>"method_foo"</code> (include the double quotes, they are important!)
|
Open up the C++ wrapper source code file and look for <code>"method_foo"</code> (include the double quotes, they are important!)
|
||||||
Look at the JNI field descriptor and make sure that each class that occurs in
|
Look at the JNI field descriptor and make sure that each class that occurs in the descriptor has the correct package name in front of it.
|
||||||
the descriptor has the correct package name in front of it. If the package
|
If the package name is incorrect, put a <code>javapackage</code> typemap in your SWIG interface file.
|
||||||
name is incorrect, put a <code>javapackage</code> typemap in your SWIG
|
</p>
|
||||||
interface file. </p>
|
|
||||||
</ul>
|
<li><i>I'm compiling my code and I'm using templates. I provided a
|
||||||
|
javapackage typemap, but SWIG doesn't generate the right JNI field
|
||||||
|
descriptor.</i></li>
|
||||||
|
<p>
|
||||||
|
Use the template's renamed name as the argument to the <code>javapackage</code> typemap:
|
||||||
|
<br>
|
||||||
|
<code><pre>
|
||||||
|
%template(VectorOfInt) std::vector<int>;
|
||||||
|
%typemap(javapackage) VectorOfInt "your.package.here";
|
||||||
|
</pre></code>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<li><i>When I pass class pointers or references through a C++ upcall and I
|
||||||
|
try to type cast them, Java complains with a ClassCastException. What am I
|
||||||
|
doing wrong?</i></li>
|
||||||
|
<p>
|
||||||
|
Normally, a non-director generated Java proxy class creates temporary Java objects as follows:
|
||||||
|
<code><pre>
|
||||||
|
public static void MyClass_method_upcall(MyClass self, long jarg1)
|
||||||
|
{
|
||||||
|
Foo darg1 = new Foo(jarg1, false);
|
||||||
|
|
||||||
|
self.method_upcall(darg1);
|
||||||
|
}
|
||||||
|
</pre></code>
|
||||||
|
<br>
|
||||||
|
Unfortunately, this loses the Java type information that is part of the underlying Foo director shadow class's java object pointer causing the type cast to fail.
|
||||||
|
The SWIG Java module's director code attempts to correct the problem, <b>but only for director-enabled classes</b>, since the director class retains a global reference to its Java object.
|
||||||
|
Thus, for director-enabled classes <b>and only for director-enabled classes</b>, the generated proxy Java code looks somthing like:
|
||||||
|
<code><pre>
|
||||||
|
public static void MyClass_method_upcall(MyClass self, long jarg1, Foo jarg1_object)
|
||||||
|
{
|
||||||
|
Foo darg1 = (jarg1_object != null ? jarg1_object : new Foo(jarg1, false));
|
||||||
|
|
||||||
|
self.method_upcall(darg1);
|
||||||
|
}
|
||||||
|
</pre></code>
|
||||||
|
<br>
|
||||||
|
When you import a SWIG interface file containing class definitions, the classes you want to be director-enabled must be have the <code>feature("director")</code> enabled for type symmetry to work.
|
||||||
|
This applies even when the class being wrapped isn't a director-enabled class but takes parameters that are director-enabled classes.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The current "type symmetry" design will work for simple C++ inheritance, but will most likely fail for anything more compicated such as tree or diamond C++ inheritance hierarchies.
|
||||||
|
Those who are interested in challenging problems are more than welcome to hack the <code>Java::Java_director_declaration</code> method in <code>Source/Modules/java.cxx</code>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If all else fails, you can use the downcastXXXXX() method to attempt to recover the director class's Java object pointer.
|
||||||
|
For the Java Foo proxy class, the Foo director class's java object pointer can be accessed through the javaObjectFoo() method.
|
||||||
|
The generated method's signature is:
|
||||||
|
<br>
|
||||||
|
<code><pre>
|
||||||
|
public static Foo javaObjectFoo(Foo obj);
|
||||||
|
</pre></code>
|
||||||
|
<br>
|
||||||
|
From your code, this method is invoked as follows:
|
||||||
|
<br>
|
||||||
|
<code><pre>
|
||||||
|
public class MyClassDerived {
|
||||||
|
public void method_upcall(Foo foo_object)
|
||||||
|
{
|
||||||
|
FooDerived derived = (foo_object != null ? (FooDerived) Foo.downcastFoo(foo_object) : null);
|
||||||
|
/* rest of your code here */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre></code>
|
||||||
|
<br>
|
||||||
|
An good approach for managing downcasting is placing a static method in each derived class that performs the downcast from the superclass, e.g.,
|
||||||
|
<br>
|
||||||
|
<code><pre>
|
||||||
|
public class FooDerived extends Foo {
|
||||||
|
/* ... */
|
||||||
|
public static FooDerived downcastFooDerived(Foo foo_object)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return (foo_object != null ? (FooDerived) Foo.downcastFoo(foo_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (ClassCastException exc) {
|
||||||
|
// Wasn't a FooDerived object, some other sublcass of Foo
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre></code>
|
||||||
|
<br>
|
||||||
|
Then change the code in MyClassDerived as follows:
|
||||||
|
<br>
|
||||||
|
<code><pre>
|
||||||
|
public class MyClassDerived extends MyClass {
|
||||||
|
/* ... */
|
||||||
|
public void method_upcall(Foo foo_object)
|
||||||
|
{
|
||||||
|
FooDerived derived = FooDerived.downcastFooDerived(foo_object) : null);
|
||||||
|
/* rest of your code here */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre></code>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<li><i>Why isn't the proxy class declared abstract? Why aren't the director
|
||||||
|
upcall methods in the proxy class declared abstract?</i></li>
|
||||||
|
<p>
|
||||||
|
Declaring the proxy class and its methods abstract would break the JNI argument marshalling and SWIG's downcall functionality (going from Java to C++.)
|
||||||
|
Create an abstract Java subclass that inherits from the director-enabled class instead.
|
||||||
|
Using the previous Foo class example:
|
||||||
|
<br>
|
||||||
|
<code><pre>
|
||||||
|
public abstract class UserVisibleFoo extends Foo {
|
||||||
|
/** Make sure user overrides this method, it's where the upcall
|
||||||
|
* happens.
|
||||||
|
*/
|
||||||
|
public abstract void method_upcall(Foo foo_object);
|
||||||
|
|
||||||
|
/// Downcast from Foo to UserVisibleFoo
|
||||||
|
public static UserVisibleFoo downcastUserVisibleFoo(Foo foo_object)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return (foo_object != null ? (FooDerived) Foo.downcastFoo(foo_object) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (ClassCastException exc) {
|
||||||
|
// Wasn't a FooDerived object, some other sublcass of Foo
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre></code>
|
||||||
|
<br>
|
||||||
|
This doesn't prevent the user from creating subclasses derived from Foo, however, UserVisibleFoo provides the safety net that reminds the user to override the <code>method_upcall()</code> method.
|
||||||
|
</p>
|
||||||
|
</ol>
|
||||||
|
|
||||||
<a name="odds_ends"></a>
|
<a name="odds_ends"></a>
|
||||||
<a name="n74"></a><H2>15.10 Odds and ends</H2>
|
<a name="n74"></a><H2>15.10 Odds and ends</H2>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue