steals python director docs and adapts to perl5
This commit is contained in:
parent
73e2de7538
commit
35dc86f064
2 changed files with 343 additions and 1 deletions
|
|
@ -68,6 +68,15 @@
|
||||||
<li><a href="#Perl5_nn46">Modifying the proxy methods</a>
|
<li><a href="#Perl5_nn46">Modifying the proxy methods</a>
|
||||||
</ul>
|
</ul>
|
||||||
<li><a href="#Perl5_nn47">Adding additional Perl code</a>
|
<li><a href="#Perl5_nn47">Adding additional Perl code</a>
|
||||||
|
<li><a href="#Perl5_directors">Cross language polymorphism</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#Perl5_nn48">Enabling directors</a>
|
||||||
|
<li><a href="#Perl5_nn49">Director classes</a>
|
||||||
|
<li><a href="#Perl5_nn50">Ownership and object destruction</a>
|
||||||
|
<li><a href="#Perl5_nn51">Exception unrolling</a>
|
||||||
|
<li><a href="#Perl5_nn52">Overhead and code bloat</a>
|
||||||
|
<li><a href="#Perl5_nn53">Typemaps</a>
|
||||||
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<!-- INDEX -->
|
<!-- INDEX -->
|
||||||
|
|
@ -2993,6 +3002,339 @@ set_transform($im, $a);
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<H2><a name="Perl5_directors"></a>31.11 Cross language polymorphism</H2>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Proxy classes provide a more natural, object-oriented way to access
|
||||||
|
extension classes. As described above, each proxy instance has an
|
||||||
|
associated C++ instance, and method calls to the proxy are passed to the
|
||||||
|
C++ instance transparently via C wrapper functions.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This arrangement is asymmetric in the sense that no corresponding
|
||||||
|
mechanism exists to pass method calls down the inheritance chain from
|
||||||
|
C++ to Perl. In particular, if a C++ class has been extended in Perl
|
||||||
|
(by extending the proxy class), these extensions will not be visible
|
||||||
|
from C++ code. Virtual method calls from C++ are thus not able access
|
||||||
|
the lowest implementation in the inheritance chain.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Changes have been made to SWIG to address this problem and
|
||||||
|
make the relationship between C++ classes and proxy classes more
|
||||||
|
symmetric. To achieve this goal, new classes called directors are
|
||||||
|
introduced at the bottom of the C++ inheritance chain. The job of the
|
||||||
|
directors is to route method calls correctly, either to C++
|
||||||
|
implementations higher in the inheritance chain or to Perl
|
||||||
|
implementations lower in the inheritance chain. The upshot is that C++
|
||||||
|
classes can be extended in Perl and from C++ these extensions look
|
||||||
|
exactly like native C++ classes. Neither C++ code nor Perl code needs
|
||||||
|
to know where a particular method is implemented: the combination of
|
||||||
|
proxy classes, director classes, and C wrapper functions takes care of
|
||||||
|
all the cross-language method routing transparently.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<H3><a name="Perl5_nn48"></a>31.11.1 Enabling directors</H3>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The director feature is disabled by default. To use directors you
|
||||||
|
must make two changes to the interface file. First, add the "directors"
|
||||||
|
option to the %module directive, like this:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="code">
|
||||||
|
<pre>
|
||||||
|
%module(directors="1") modulename
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Without this option no director code will be generated. Second, you
|
||||||
|
must use the %feature("director") directive to tell SWIG which classes
|
||||||
|
and methods should get directors. The %feature directive can be applied
|
||||||
|
globally, to specific classes, and to specific methods, like this:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="code">
|
||||||
|
<pre>
|
||||||
|
// generate directors for all classes that have virtual methods
|
||||||
|
%feature("director");
|
||||||
|
|
||||||
|
// generate directors for all virtual methods in class Foo
|
||||||
|
%feature("director") Foo;
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You can use the %feature("nodirector") directive to turn off
|
||||||
|
directors for specific classes or methods. So for example,
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="code">
|
||||||
|
<pre>
|
||||||
|
%feature("director") Foo;
|
||||||
|
%feature("nodirector") Foo::bar;
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
will generate directors for all virtual methods of class Foo except
|
||||||
|
bar().
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Directors can also be generated implicitly through inheritance.
|
||||||
|
In the following, class Bar will get a director class that handles
|
||||||
|
the methods one() and two() (but not three()):
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="code">
|
||||||
|
<pre>
|
||||||
|
%feature("director") Foo;
|
||||||
|
class Foo {
|
||||||
|
public:
|
||||||
|
Foo(int foo);
|
||||||
|
virtual void one();
|
||||||
|
virtual void two();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Bar: public Foo {
|
||||||
|
public:
|
||||||
|
virtual void three();
|
||||||
|
};
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
then at the Perl side you can define
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="targetlang">
|
||||||
|
<pre>
|
||||||
|
use mymodule;
|
||||||
|
|
||||||
|
package MyFoo;
|
||||||
|
use base 'mymodule::Foo';
|
||||||
|
|
||||||
|
sub one {
|
||||||
|
print "one from Perl\n";
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<H3><a name="Perl5_nn49"></a>31.11.2 Director classes</H3>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
For each class that has directors enabled, SWIG generates a new class
|
||||||
|
that derives from both the class in question and a special
|
||||||
|
<tt>Swig::Director</tt> class. These new classes, referred to as director
|
||||||
|
classes, can be loosely thought of as the C++ equivalent of the Perl
|
||||||
|
proxy classes. The director classes store a pointer to their underlying
|
||||||
|
Perl object and handle various issues related to object ownership.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
For simplicity let's ignore the <tt>Swig::Director</tt> class and refer to the
|
||||||
|
original C++ class as the director's base class. By default, a director
|
||||||
|
class extends all virtual methods in the inheritance chain of its base
|
||||||
|
class (see the preceding section for how to modify this behavior).
|
||||||
|
Thus all virtual method calls, whether they originate in C++ or in
|
||||||
|
Perl via proxy classes, eventually end up in at the implementation in
|
||||||
|
the director class. The job of the director methods is to route these
|
||||||
|
method calls to the appropriate place in the inheritance chain. By
|
||||||
|
"appropriate place" we mean the method that would have been called if
|
||||||
|
the C++ base class and its extensions in Perl were seamlessly
|
||||||
|
integrated. That seamless integration is exactly what the director
|
||||||
|
classes provide, transparently skipping over all the messy extension API
|
||||||
|
glue that binds the two languages together.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
In reality, the "appropriate place" is one of only two possibilities:
|
||||||
|
C++ or Perl. Once this decision is made, the rest is fairly easy. If
|
||||||
|
the correct implementation is in C++, then the lowest implementation of
|
||||||
|
the method in the C++ inheritance chain is called explicitly. If the
|
||||||
|
correct implementation is in Perl, the Perl API is used to call the
|
||||||
|
method of the underlying Perl object (after which the usual virtual
|
||||||
|
method resolution in Perl automatically finds the right
|
||||||
|
implementation).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Now how does the director decide which language should handle the method call?
|
||||||
|
The basic rule is to handle the method in Perl, unless there's a good
|
||||||
|
reason not to. The reason for this is simple: Perl has the most
|
||||||
|
"extended" implementation of the method. This assertion is guaranteed,
|
||||||
|
since at a minimum the Perl proxy class implements the method. If the
|
||||||
|
method in question has been extended by a class derived from the proxy
|
||||||
|
class, that extended implementation will execute exactly as it should.
|
||||||
|
If not, the proxy class will route the method call into a C wrapper
|
||||||
|
function, expecting that the method will be resolved in C++. The wrapper
|
||||||
|
will call the virtual method of the C++ instance, and since the director
|
||||||
|
extends this the call will end up right back in the director method. Now
|
||||||
|
comes the "good reason not to" part. If the director method were to blindly
|
||||||
|
call the Perl method again, it would get stuck in an infinite loop. We avoid this
|
||||||
|
situation by adding special code to the C wrapper function that tells
|
||||||
|
the director method to not do this. The C wrapper function compares the
|
||||||
|
pointer to the Perl object that called the wrapper function to the
|
||||||
|
pointer stored by the director. If these are the same, then the C
|
||||||
|
wrapper function tells the director to resolve the method by calling up
|
||||||
|
the C++ inheritance chain, preventing an infinite loop.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
One more point needs to be made about the relationship between director
|
||||||
|
classes and proxy classes. When a proxy class instance is created in
|
||||||
|
Perl, SWIG creates an instance of the original C++ class.
|
||||||
|
This is exactly what happens without directors and
|
||||||
|
is true even if directors are enabled for the particular class in
|
||||||
|
question. When a class <i>derived</i> from a proxy class is created,
|
||||||
|
however, SWIG then creates an instance of the corresponding C++ director
|
||||||
|
class. The reason for this difference is that user-defined subclasses
|
||||||
|
may override or extend methods of the original class, so the director
|
||||||
|
class is needed to route calls to these methods correctly. For
|
||||||
|
unmodified proxy classes, all methods are ultimately implemented in C++
|
||||||
|
so there is no need for the extra overhead involved with routing the
|
||||||
|
calls through Perl.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<H3><a name="Perl5_nn50"></a>31.11.3 Ownership and object destruction</H3>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Memory management issues are slightly more complicated with directors
|
||||||
|
than for proxy classes alone. Perl instances hold a pointer to the
|
||||||
|
associated C++ director object, and the director in turn holds a pointer
|
||||||
|
back to a Perl object. By default, proxy classes own their C++
|
||||||
|
director object and take care of deleting it when they are garbage
|
||||||
|
collected.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This relationship can be reversed by calling the special
|
||||||
|
<tt>DISOWN()</tt> method of the proxy class. After calling this
|
||||||
|
method the director
|
||||||
|
class increments the reference count of the Perl object. When the
|
||||||
|
director class is deleted it decrements the reference count. Assuming no
|
||||||
|
outstanding references to the Perl object remain, the Perl object
|
||||||
|
will be destroyed at the same time. This is a good thing, since
|
||||||
|
directors and proxies refer to each other and so must be created and
|
||||||
|
destroyed together. Destroying one without destroying the other will
|
||||||
|
likely cause your program to segfault.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<H3><a name="Perl5_nn51"></a>31.11.4 Exception unrolling</H3>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
With directors routing method calls to Perl, and proxies routing them
|
||||||
|
to C++, the handling of exceptions is an important concern. By default, the
|
||||||
|
directors ignore exceptions that occur during method calls that are
|
||||||
|
resolved in Perl. To handle such exceptions correctly, it is necessary
|
||||||
|
to temporarily translate them into C++ exceptions. This can be done with
|
||||||
|
the %feature("director:except") directive. The following code should
|
||||||
|
suffice in most cases:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="code">
|
||||||
|
<pre>
|
||||||
|
%feature("director:except") {
|
||||||
|
if ($error != NULL) {
|
||||||
|
throw Swig::DirectorMethodException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This code will check the Perl error state after each method call from
|
||||||
|
a director into Perl, and throw a C++ exception if an error occurred.
|
||||||
|
This exception can be caught in C++ to implement an error handler.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
It may be the case that a method call originates in Perl, travels up
|
||||||
|
to C++ through a proxy class, and then back into Perl via a director
|
||||||
|
method. If an exception occurs in Perl at this point, it would be nice
|
||||||
|
for that exception to find its way back to the original caller. This can
|
||||||
|
be done by combining a normal %exception directive with the
|
||||||
|
<tt>director:except</tt> handler shown above. Here is an example of a
|
||||||
|
suitable exception handler:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="code">
|
||||||
|
<pre>
|
||||||
|
%exception {
|
||||||
|
try { $action }
|
||||||
|
catch (Swig::DirectorException &e) { SWIG_fail; }
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The class Swig::DirectorException used in this example is actually a
|
||||||
|
base class of Swig::DirectorMethodException, so it will trap this
|
||||||
|
exception. Because the Perl error state is still set when
|
||||||
|
Swig::DirectorMethodException is thrown, Perl will register the
|
||||||
|
exception as soon as the C wrapper function returns.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<H3><a name="Perl5_nn52"></a>31.11.5 Overhead and code bloat</H3>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Enabling directors for a class will generate a new director method for
|
||||||
|
every virtual method in the class' inheritance chain. This alone can
|
||||||
|
generate a lot of code bloat for large hierarchies. Method arguments
|
||||||
|
that require complex conversions to and from target language types can
|
||||||
|
result in large director methods. For this reason it is recommended that
|
||||||
|
you selectively enable directors only for specific classes that are
|
||||||
|
likely to be extended in Perl and used in C++.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Compared to classes that do not use directors, the call routing in the
|
||||||
|
director methods does add some overhead. In particular, at least one
|
||||||
|
dynamic cast and one extra function call occurs per method call from
|
||||||
|
Perl. Relative to the speed of Perl execution this is probably
|
||||||
|
completely negligible. For worst case routing, a method call that
|
||||||
|
ultimately resolves in C++ may take one extra detour through Perl in
|
||||||
|
order to ensure that the method does not have an extended Perl
|
||||||
|
implementation. This could result in a noticeable overhead in some cases.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Although directors make it natural to mix native C++ objects with Perl
|
||||||
|
objects (as director objects) via a common base class pointer, one
|
||||||
|
should be aware of the obvious fact that method calls to Perl objects
|
||||||
|
will be much slower than calls to C++ objects. This situation can be
|
||||||
|
optimized by selectively enabling director methods (using the %feature
|
||||||
|
directive) for only those methods that are likely to be extended in
|
||||||
|
Perl.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<H3><a name="Perl5_nn53"></a>31.11.6 Typemaps</H3>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Typemaps for input and output of most of the basic types from director
|
||||||
|
classes have been written. These are roughly the reverse of the usual
|
||||||
|
input and output typemaps used by the wrapper code. The typemap
|
||||||
|
operation names are 'directorin', 'directorout', and 'directorargout'.
|
||||||
|
The director code does not currently use any of the other kinds of typemaps.
|
||||||
|
It is not clear at this point which kinds are appropriate and
|
||||||
|
need to be supported.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ namespace Swig {
|
||||||
protected:
|
protected:
|
||||||
SV *err;
|
SV *err;
|
||||||
public:
|
public:
|
||||||
DirectorMethodException(SV *sv)
|
DirectorMethodException(SV *sv = sv_mortalcopy(ERRSV))
|
||||||
: err(sv)
|
: err(sv)
|
||||||
{
|
{
|
||||||
SvREFCNT_inc(err);
|
SvREFCNT_inc(err);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue