Merge branch 'perl5-directors-minimal' of https://github.com/talby-/swig into talby--perl5-directors-minimal
* 'perl5-directors-minimal' of https://github.com/talby-/swig: try adding travis ci to this branch ran "beautify-file" make target over perl5.cxx patch hunks and rewrote callback and extend examples in the style of existing examples prefer polymorphism on existing destructor over custom destructor method fix string mangled by tidy eliminate dead director code and convert remaining blocks mitigate ConvertPtr director cost when directors are not enabled steals python director docs and adapts to perl5 adds "callback" and "extend" examples don't forget the most important part steals python directors and adapts to perl5
This commit is contained in:
commit
d1bb451eab
40 changed files with 2740 additions and 5 deletions
|
|
@ -68,6 +68,15 @@
|
|||
<li><a href="#Perl5_nn46">Modifying the proxy methods</a>
|
||||
</ul>
|
||||
<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>
|
||||
</div>
|
||||
<!-- INDEX -->
|
||||
|
|
@ -2993,6 +3002,363 @@ set_transform($im, $a);
|
|||
</pre>
|
||||
</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>
|
||||
|
||||
<p>
|
||||
Also note that due to the proxy implementation, the <tt>DESTROY()</tt>
|
||||
method on directors can be called for several reasons, many of which
|
||||
have little to do with the teardown of an object instance. To help
|
||||
disambiguate this, a second argument is added to the <tt>DESTROY()</tt>
|
||||
call when a C++ director object is being released. So, to avoid running
|
||||
your clean-up code when an object is not really going away, or after it
|
||||
has already been reclaimed, it is suggested that custom destructors in
|
||||
Perl subclasses looks something like:
|
||||
</p>
|
||||
|
||||
<div class="targetlang">
|
||||
<pre>
|
||||
sub DESTROY {
|
||||
my($self, $final) = @_;
|
||||
if($final) {
|
||||
# real teardown code
|
||||
}
|
||||
shift->SUPER::DESTROY(@_);
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
|
||||
<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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue