mirror of https://github.com/swig/swig
git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@11434 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
parent
8ec7035eee
commit
0249eea389
|
@ -1,6 +1,9 @@
|
|||
Version 1.3.40 (in progress)
|
||||
============================
|
||||
|
||||
2009-07-21: vmiklos
|
||||
[PHP] Director support added.
|
||||
|
||||
2009-07-15: olly
|
||||
[Perl] Don't specify Perl prototype "()" for a constructor with a
|
||||
different name to the class, as such constructors can still take
|
||||
|
@ -17,6 +20,10 @@ Version 1.3.40 (in progress)
|
|||
of the manual. Based on patch from SF#2810380 by Christian
|
||||
Gollwitzer.
|
||||
|
||||
2009-07-02: vmiklos
|
||||
[PHP] Added factory.i for PHP, see the li_factory testcase
|
||||
for more info on how to use it.
|
||||
|
||||
2009-07-02: wsfulton
|
||||
Fix -Wallkw option as reported by Solomon Gibbs.
|
||||
|
||||
|
@ -24,6 +31,14 @@ Version 1.3.40 (in progress)
|
|||
Fix syntax error when a nested struct contains a comment containing a * followed eventually by a /.
|
||||
Regression from 1.3.37, reported by Solomon Gibbs.
|
||||
|
||||
2009-07-01: vmiklos
|
||||
[PHP] Unknown properties are no longer ignored in proxy
|
||||
classes.
|
||||
|
||||
2009-07-01: vmiklos
|
||||
[PHP] Fixed %newobject behaviour, previously any method
|
||||
marked with %newobject was handled as a constructor.
|
||||
|
||||
2009-06-30: olly
|
||||
[Ruby] Undefine close and connect macros defined by Ruby API
|
||||
headers as we don't need them and they can clash with C++ methods
|
||||
|
@ -66,6 +81,12 @@ Version 1.3.40 (in progress)
|
|||
|
||||
*** POTENTIAL INCOMPATIBILITY ***
|
||||
|
||||
2009-05-20: vmiklos
|
||||
[PHP] Add the 'thisown' member to classes. The usage of it
|
||||
is the same as the Python thisown one: it's 1 by default and
|
||||
you can set it to 0 if you want to prevent freeing it. (For
|
||||
example to prevent a double free.)
|
||||
|
||||
2009-05-14: bhy
|
||||
[Python] Fix the wrong pointer value returned by SwigPyObject_repr().
|
||||
|
||||
|
|
|
@ -32,6 +32,16 @@
|
|||
</ul>
|
||||
<li><a href="#Php_nn2_7">PHP Pragmas, Startup and Shutdown code</a>
|
||||
</ul>
|
||||
<li><a href="#Php_nn3">Cross language polymorphism</a>
|
||||
<ul>
|
||||
<li><a href="#Php_nn3_1">Enabling directors</a>
|
||||
<li><a href="#Php_nn3_2">Director classes</a>
|
||||
<li><a href="#Php_nn3_3">Ownership and object destruction</a>
|
||||
<li><a href="#Php_nn3_4">Exception unrolling</a>
|
||||
<li><a href="#Php_nn3_5">Overhead and code bloat</a>
|
||||
<li><a href="#Php_nn3_6">Typemaps</a>
|
||||
<li><a href="#Php_nn3_7">Miscellaneous</a>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- INDEX -->
|
||||
|
@ -866,5 +876,381 @@ The <tt>%rinit</tt> and <tt>%rshutdown</tt> statements insert code
|
|||
into the request init and shutdown code respectively.
|
||||
</p>
|
||||
|
||||
<H2><a name="Php_nn3"></a>29.3 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 PHP. In particular, if a C++ class has been extended in PHP
|
||||
(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 1.3.18 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. Support for generating PHP classes
|
||||
has been added in SWIG 1.3.40. The job of the directors is to route
|
||||
method calls correctly, either to C++ implementations higher in the
|
||||
inheritance chain or to PHP implementations lower in the inheritance
|
||||
chain. The upshot is that C++ classes can be extended in PHP and from
|
||||
C++ these extensions look exactly like native C++ classes. Neither C++
|
||||
code nor PHP 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="Php_nn3_1"></a>29.3.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;
|
||||
|
||||
// generate a director for just Foo::bar()
|
||||
%feature("director") Foo::bar;
|
||||
</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 PHP side you can define
|
||||
</p>
|
||||
|
||||
<div class="targetlang">
|
||||
<pre>
|
||||
require("mymodule.php");
|
||||
|
||||
class MyFoo extends Foo {
|
||||
function one() {
|
||||
print "one from php\n";
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
|
||||
<H3><a name="Php_nn3_2"></a>29.3.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 PHP
|
||||
proxy classes. The director classes store a pointer to their underlying
|
||||
PHP object. Indeed, this is quite similar to the "_cPtr" and "thisown"
|
||||
members of the PHP proxy classes.
|
||||
</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
|
||||
PHP 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 PHP 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 PHP. 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 PHP, the Zend API is used to call the method of the
|
||||
underlying PHP object (after which the usual virtual method resolution
|
||||
in PHP 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 PHP, unless there's a good
|
||||
reason not to. The reason for this is simple: PHP has the most
|
||||
"extended" implementation of the method. This assertion is guaranteed,
|
||||
since at a minimum the PHP 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 PHP 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
|
||||
called and the declaring class name of the given method. If these are
|
||||
not 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
|
||||
PHP, SWIG creates an instance of the original C++ class and assigns it
|
||||
to <tt>->_cPtr</tt>. 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 PHP.
|
||||
</p>
|
||||
|
||||
<H3><a name="Php_nn3_3"></a>29.3.3 Ownership and object destruction</H3>
|
||||
|
||||
|
||||
<p>
|
||||
Memory management issues are slightly more complicated with directors
|
||||
than for proxy classes alone. PHP instances hold a pointer to the
|
||||
associated C++ director object, and the director in turn holds a pointer
|
||||
back to the PHP 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>->thisown</tt> property of the proxy class. After setting this
|
||||
property to <tt>0</tt>, the director class no longer destroys the PHP
|
||||
object. Assuming no outstanding references to the PHP object remain,
|
||||
the PHP 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>
|
||||
Here is an example:
|
||||
</p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
class Foo {
|
||||
public:
|
||||
...
|
||||
};
|
||||
class FooContainer {
|
||||
public:
|
||||
void addFoo(Foo *);
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="targetlang">
|
||||
<pre>
|
||||
$c = new FooContainer();
|
||||
$a = new Foo();
|
||||
$a->thisown = 0;
|
||||
$c->addFoo($a);
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
In this example, we are assuming that FooContainer will take care of
|
||||
deleting all the Foo pointers it contains at some point.
|
||||
</p>
|
||||
|
||||
<H3><a name="Php_nn3_4"></a>29.3.4 Exception unrolling</H3>
|
||||
|
||||
|
||||
<p>
|
||||
With directors routing method calls to PHP, 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 PHP. 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 == FAILURE) {
|
||||
throw Swig::DirectorMethodException();
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
This code will check the PHP error state after each method call from a
|
||||
director into PHP, and throw a C++ exception if an error occurred. This
|
||||
exception can be caught in C++ to implement an error handler.
|
||||
Currently no information about the PHP error is stored in the
|
||||
Swig::DirectorMethodException object, but this will likely change in the
|
||||
future.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
It may be the case that a method call originates in PHP, travels up to
|
||||
C++ through a proxy class, and then back into PHP via a director method.
|
||||
If an exception occurs in PHP 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 PHP error state is still set when
|
||||
Swig::DirectorMethodException is thrown, PHP will register the exception
|
||||
as soon as the C wrapper function returns.
|
||||
</p>
|
||||
|
||||
<H3><a name="Php_nn3_5"></a>29.3.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 PHP 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
|
||||
PHP. Relative to the speed of PHP execution this is probably completely
|
||||
negligible. For worst case routing, a method call that ultimately
|
||||
resolves in C++ may take one extra detour through PHP in order to ensure
|
||||
that the method does not have an extended PHP implementation. This could
|
||||
result in a noticeable overhead in some cases.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Although directors make it natural to mix native C++ objects with PHP
|
||||
objects (as director objects) via a common base class pointer, one
|
||||
should be aware of the obvious fact that method calls to PHP 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 PHP.
|
||||
</p>
|
||||
|
||||
<H3><a name="Php_nn3_6"></a>29.3.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>
|
||||
|
||||
|
||||
<H3><a name="Php_nn3_7"></a>29.3.7 Miscellaneous</H3>
|
||||
|
||||
|
||||
<p> Director typemaps for STL classes are mostly in place, and hence you
|
||||
should be able to use std::string, etc., as you would any other type.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
TOP = ../..
|
||||
SWIG = $(TOP)/../preinst-swig
|
||||
CXXSRCS = example.cxx
|
||||
TARGET = example
|
||||
INTERFACE = example.i
|
||||
LIBS = -lm
|
||||
SWIGOPT =
|
||||
|
||||
all::
|
||||
$(MAKE) -f $(TOP)/Makefile $(SWIGLIB) CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \
|
||||
SWIGOPT='$(SWIGOPT)' TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' php_cpp
|
||||
|
||||
static::
|
||||
$(MAKE) -f $(TOP)/Makefile $(SWIGLIB) CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \
|
||||
SWIGOPT='$(SWIGOPT)' TARGET='myphp' INTERFACE='$(INTERFACE)' php_cpp_static
|
||||
|
||||
clean::
|
||||
$(MAKE) -f $(TOP)/Makefile php_clean
|
||||
rm -f $(TARGET).php
|
||||
|
||||
check: all
|
||||
$(MAKE) -f $(TOP)/Makefile php_run
|
|
@ -0,0 +1,4 @@
|
|||
/* File : example.cxx */
|
||||
|
||||
#include "example.h"
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/* File : example.h */
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class Callback {
|
||||
public:
|
||||
virtual ~Callback() { std::cout << "Callback::~Callback()" << std:: endl; }
|
||||
virtual void run() { std::cout << "Callback::run()" << std::endl; }
|
||||
};
|
||||
|
||||
|
||||
class Caller {
|
||||
private:
|
||||
Callback *_callback;
|
||||
public:
|
||||
Caller(): _callback(0) {}
|
||||
~Caller() { delCallback(); }
|
||||
void delCallback() { delete _callback; _callback = 0; }
|
||||
void setCallback(Callback *cb) { delCallback(); _callback = cb; }
|
||||
void call() { if (_callback) _callback->run(); }
|
||||
};
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
/* File : example.i */
|
||||
%module(directors="1") example
|
||||
%{
|
||||
#include "example.h"
|
||||
%}
|
||||
|
||||
%include "std_string.i"
|
||||
|
||||
/* turn on director wrapping Callback */
|
||||
%feature("director") Callback;
|
||||
|
||||
%include "example.h"
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>SWIG:Examples:php:callback</title>
|
||||
</head>
|
||||
|
||||
<body bgcolor="#ffffff">
|
||||
|
||||
|
||||
<tt>SWIG/Examples/php/callback/</tt>
|
||||
<hr>
|
||||
|
||||
<H2>Implementing C++ callbacks in PHP</H2>
|
||||
|
||||
<p>
|
||||
This example illustrates how to use directors to implement C++ callbacks in PHP.
|
||||
|
||||
<hr>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
# This file illustrates the cross language polymorphism using directors.
|
||||
|
||||
require("example.php");
|
||||
|
||||
# Class, which overwrites Callback::run().
|
||||
|
||||
class PhpCallback extends Callback {
|
||||
function run() {
|
||||
print "PhpCallback.run()\n";
|
||||
}
|
||||
};
|
||||
|
||||
# Create an Caller instance
|
||||
|
||||
$caller = new Caller();
|
||||
|
||||
# Add a simple C++ callback (caller owns the callback, so
|
||||
# we disown it first by clearing the .thisown flag).
|
||||
|
||||
print "Adding and calling a normal C++ callback\n";
|
||||
print "----------------------------------------\n";
|
||||
|
||||
$callback = new Callback();
|
||||
$callback->thisown = 0;
|
||||
$caller->setCallback($callback);
|
||||
$caller->call();
|
||||
$caller->delCallback();
|
||||
|
||||
print "\n";
|
||||
print "Adding and calling a PHP callback\n";
|
||||
print "------------------------------------\n";
|
||||
|
||||
# Add a PHP callback.
|
||||
|
||||
$callback = new PhpCallback();
|
||||
$callback->thisown = 0;
|
||||
$caller->setCallback($callback);
|
||||
$caller->call();
|
||||
$caller->delCallback();
|
||||
|
||||
# All done.
|
||||
|
||||
print "php exit\n";
|
||||
|
||||
?>
|
|
@ -0,0 +1,22 @@
|
|||
TOP = ../..
|
||||
SWIG = $(TOP)/../preinst-swig
|
||||
CXXSRCS = example.cxx
|
||||
TARGET = example
|
||||
INTERFACE = example.i
|
||||
LIBS = -lm
|
||||
SWIGOPT =
|
||||
|
||||
all::
|
||||
$(MAKE) -f $(TOP)/Makefile $(SWIGLIB) CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \
|
||||
SWIGOPT='$(SWIGOPT)' TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' php_cpp
|
||||
|
||||
static::
|
||||
$(MAKE) -f $(TOP)/Makefile $(SWIGLIB) CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \
|
||||
SWIGOPT='$(SWIGOPT)' TARGET='myphp' INTERFACE='$(INTERFACE)' php_cpp_static
|
||||
|
||||
clean::
|
||||
$(MAKE) -f $(TOP)/Makefile php_clean
|
||||
rm -f $(TARGET).php
|
||||
|
||||
check: all
|
||||
$(MAKE) -f $(TOP)/Makefile php_run
|
|
@ -0,0 +1,4 @@
|
|||
/* File : example.cxx */
|
||||
|
||||
#include "example.h"
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/* File : example.h */
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
|
||||
class Employee {
|
||||
private:
|
||||
std::string name;
|
||||
public:
|
||||
Employee(const char* n): name(n) {}
|
||||
virtual std::string getTitle() { return getPosition() + " " + getName(); }
|
||||
virtual std::string getName() { return name; }
|
||||
virtual std::string getPosition() const { return "Employee"; }
|
||||
virtual ~Employee() { printf("~Employee() @ %p\n", this); }
|
||||
};
|
||||
|
||||
|
||||
class Manager: public Employee {
|
||||
public:
|
||||
Manager(const char* n): Employee(n) {}
|
||||
virtual std::string getPosition() const { return "Manager"; }
|
||||
};
|
||||
|
||||
|
||||
class EmployeeList {
|
||||
std::vector<Employee*> list;
|
||||
public:
|
||||
EmployeeList() {
|
||||
list.push_back(new Employee("Bob"));
|
||||
list.push_back(new Employee("Jane"));
|
||||
list.push_back(new Manager("Ted"));
|
||||
}
|
||||
void addEmployee(Employee *p) {
|
||||
list.push_back(p);
|
||||
std::cout << "New employee added. Current employees are:" << std::endl;
|
||||
std::vector<Employee*>::iterator i;
|
||||
for (i=list.begin(); i!=list.end(); i++) {
|
||||
std::cout << " " << (*i)->getTitle() << std::endl;
|
||||
}
|
||||
}
|
||||
const Employee *get_item(int i) {
|
||||
return list[i];
|
||||
}
|
||||
~EmployeeList() {
|
||||
std::vector<Employee*>::iterator i;
|
||||
std::cout << "~EmployeeList, deleting " << list.size() << " employees." << std::endl;
|
||||
for (i=list.begin(); i!=list.end(); i++) {
|
||||
delete *i;
|
||||
}
|
||||
std::cout << "~EmployeeList empty." << std::endl;
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
/* File : example.i */
|
||||
%module(directors="1") example
|
||||
%{
|
||||
#include "example.h"
|
||||
%}
|
||||
|
||||
%include "std_vector.i"
|
||||
%include "std_string.i"
|
||||
|
||||
/* turn on director wrapping for Manager */
|
||||
%feature("director") Employee;
|
||||
%feature("director") Manager;
|
||||
|
||||
%include "example.h"
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>SWIG:Examples:php:extend</title>
|
||||
</head>
|
||||
|
||||
<body bgcolor="#ffffff">
|
||||
|
||||
|
||||
<tt>SWIG/Examples/php/extend/</tt>
|
||||
<hr>
|
||||
|
||||
<H2>Extending a simple C++ class in PHP</H2>
|
||||
|
||||
<p>
|
||||
This example illustrates the extending of a C++ class with cross language polymorphism.
|
||||
|
||||
<hr>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
# This file illustrates the cross language polymorphism using directors.
|
||||
|
||||
require("example.php");
|
||||
|
||||
# CEO class, which overrides Employee::getPosition().
|
||||
|
||||
class CEO extends Manager {
|
||||
function getPosition() {
|
||||
return "CEO";
|
||||
}
|
||||
}
|
||||
|
||||
# Create an instance of our employee extension class, CEO. The calls to
|
||||
# getName() and getPosition() are standard, the call to getTitle() uses
|
||||
# the director wrappers to call CEO.getPosition.
|
||||
|
||||
$e = new CEO("Alice");
|
||||
print $e->getName() . " is a " . $e->getPosition() . "\n";
|
||||
printf("Just call her \"%s\"\n", $e->getTitle());
|
||||
print "----------------------\n";
|
||||
|
||||
# Create a new EmployeeList instance. This class does not have a C++
|
||||
# director wrapper, but can be used freely with other classes that do.
|
||||
|
||||
$list = new EmployeeList();
|
||||
|
||||
# EmployeeList owns its items, so we must surrender ownership of objects
|
||||
# we add. This involves first clearing the ->disown member to tell the
|
||||
# C++ director to start reference counting.
|
||||
|
||||
$e->thisown = 0;
|
||||
$list->addEmployee($e);
|
||||
print "----------------------\n";
|
||||
|
||||
# Now we access the first four items in list (three are C++ objects that
|
||||
# EmployeeList's constructor adds, the last is our CEO). The virtual
|
||||
# methods of all these instances are treated the same. For items 0, 1, and
|
||||
# 2, both all methods resolve in C++. For item 3, our CEO, getTitle calls
|
||||
# getPosition which resolves in PHP. The call to getPosition is
|
||||
# slightly different, however, from the e.getPosition() call above, since
|
||||
# now the object reference has been "laundered" by passing through
|
||||
# EmployeeList as an Employee*. Previously, PHP resolved the call
|
||||
# immediately in CEO, but now PHP thinks the object is an instance of
|
||||
# class Employee (actually EmployeePtr). So the call passes through the
|
||||
# Employee proxy class and on to the C wrappers and C++ director,
|
||||
# eventually ending up back at the CEO implementation of getPosition().
|
||||
# The call to getTitle() for item 3 runs the C++ Employee::getTitle()
|
||||
# method, which in turn calls getPosition(). This virtual method call
|
||||
# passes down through the C++ director class to the PHP implementation
|
||||
# in CEO. All this routing takes place transparently.
|
||||
|
||||
print "(position, title) for items 0-3:\n";
|
||||
|
||||
printf(" %s, \"%s\"\n", $list->get_item(0)->getPosition(), $list->get_item(0)->getTitle());
|
||||
printf(" %s, \"%s\"\n", $list->get_item(1)->getPosition(), $list->get_item(1)->getTitle());
|
||||
printf(" %s, \"%s\"\n", $list->get_item(2)->getPosition(), $list->get_item(2)->getTitle());
|
||||
printf(" %s, \"%s\"\n", $list->get_item(3)->getPosition(), $list->get_item(3)->getTitle());
|
||||
print "----------------------\n";
|
||||
|
||||
# Time to delete the EmployeeList, which will delete all the Employee*
|
||||
# items it contains. The last item is our CEO, which gets destroyed as its
|
||||
# reference count goes to zero. The PHP destructor runs, and is still
|
||||
# able to call the getName() method since the underlying C++ object still
|
||||
# exists. After this destructor runs the remaining C++ destructors run as
|
||||
# usual to destroy the object.
|
||||
|
||||
unset($list);
|
||||
print "----------------------\n";
|
||||
|
||||
# All done.
|
||||
|
||||
print "php exit\n";
|
||||
|
||||
?>
|
|
@ -1,6 +1,5 @@
|
|||
/* File : example.h */
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
class Callback {
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
%feature("director") Foo;
|
||||
|
||||
%newobject Foo::cloner();
|
||||
%newobject Foo::get_class();
|
||||
%newobject Bar::cloner();
|
||||
%newobject Bar::get_class();
|
||||
|
||||
|
||||
%inline {
|
||||
|
|
|
@ -11,6 +11,13 @@
|
|||
|
||||
%newobject *::create();
|
||||
|
||||
#ifdef SWIGPHP
|
||||
// TODO: Currently we do not track the dynamic type of returned objects
|
||||
// in PHP, so we need the factory helper.
|
||||
%include factory.i
|
||||
%factory(Foo *Bar::create, Bar);
|
||||
#endif
|
||||
|
||||
%rename(a) Bar::hello;
|
||||
%rename(s) Foo::p;
|
||||
%rename(q) Foo::r;
|
||||
|
|
|
@ -17,7 +17,11 @@
|
|||
%feature("director") Foo;
|
||||
|
||||
%feature("director:except") {
|
||||
#ifndef SWIGPHP
|
||||
if ($error != NULL) {
|
||||
#else
|
||||
if ($error == FAILURE) {
|
||||
#endif
|
||||
throw Swig::DirectorMethodException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
%newobject Geometry::clone;
|
||||
%factory(Geometry *Geometry::create, Point, Circle);
|
||||
%factory(Geometry *Geometry::clone, Point, Circle);
|
||||
#ifdef SWIGPHP
|
||||
%rename(clone_) clone;
|
||||
#endif
|
||||
%factory(Geometry *Point::clone, Point, Circle);
|
||||
%factory(Geometry *Circle::clone, Point, Circle);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ include $(srcdir)/../common.mk
|
|||
TARGETPREFIX =# Should be php_ for Windows, empty otherwise
|
||||
|
||||
# Custom tests - tests with additional commandline options
|
||||
# none!
|
||||
prefix.cpptest: SWIGOPT += -prefix Project
|
||||
|
||||
# write out tests without a _runme.php
|
||||
missingcpptests:
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_abstract.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_ping,foo_pong,example0_getxsize,example0_color,example0_get_color,example1_getxsize,example1_color,example1_get_color,example2_getxsize,example2_color,example2_get_color,example4_getxsize,example4_color,example4_get_color,example3_i_color,example3_i_get_color,g,a_f));
|
||||
// No new classes
|
||||
check::classes(array(director_abstract,Foo,Example0,Example1,Example2,Example4,Example3_i,A));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
class MyFoo extends Foo {
|
||||
function ping() {
|
||||
return "MyFoo::ping()";
|
||||
}
|
||||
}
|
||||
|
||||
$a = new MyFoo();
|
||||
|
||||
check::equal($a->ping(), "MyFoo::ping()", "MyFoo::ping failed");
|
||||
|
||||
check::equal($a->pong(), "Foo::pong();MyFoo::ping()", "MyFoo::pong failed");
|
||||
|
||||
class MyExample1 extends Example1 {
|
||||
function Color($r, $g, $b) {
|
||||
return $r;
|
||||
}
|
||||
}
|
||||
|
||||
class MyExample2 extends Example1 {
|
||||
function Color($r, $g, $b) {
|
||||
return $g;
|
||||
}
|
||||
}
|
||||
|
||||
class MyExample3 extends Example1 {
|
||||
function Color($r, $g, $b) {
|
||||
return $b;
|
||||
}
|
||||
}
|
||||
|
||||
$me1 = new MyExample1();
|
||||
check::equal($me1->Color(1, 2, 3), 1, "Example1_get_color failed");
|
||||
|
||||
$me2 = new MyExample2(1, 2);
|
||||
check::equal($me2->Color(1, 2, 3), 2, "Example2_get_color failed");
|
||||
|
||||
$me3 = new MyExample3();
|
||||
check::equal($me3->Color(1, 2, 3), 3, "Example3_get_color failed");
|
||||
|
||||
$class = new ReflectionClass('Example1');
|
||||
check::equal($class->isAbstract(), true, "Example1 abstractness failed");
|
||||
|
||||
$class = new ReflectionClass('Example2');
|
||||
check::equal($class->isAbstract(), true, "Example2 abstractness failed");
|
||||
|
||||
$class = new ReflectionClass('Example3_i');
|
||||
check::equal($class->isAbstract(), true, "Example3_i abstractness failed");
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_basic.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_ping,foo_pong,foo_get_self,a_f,a_rg,a1_ff,myclass_method,myclass_vmethod,myclass_pmethod,myclass_cmethod,myclass_get_self,myclass_call_pmethod,myclasst_i_method));
|
||||
// No new classes
|
||||
check::classes(array(Foo,A,A1,Bar,MyClass,MyClassT_i));
|
||||
// now new vars
|
||||
check::globals(array(bar_x));
|
||||
|
||||
class PhpFoo extends Foo {
|
||||
function ping() {
|
||||
return "PhpFoo::ping()";
|
||||
}
|
||||
}
|
||||
|
||||
$a = new PhpFoo();
|
||||
|
||||
check::equal($a->ping(), "PhpFoo::ping()", "ping failed");
|
||||
|
||||
check::equal($a->pong(), "Foo::pong();PhpFoo::ping()", "pong failed");
|
||||
|
||||
$b = new Foo();
|
||||
|
||||
check::equal($b->ping(), "Foo::ping()", "ping failed");
|
||||
|
||||
check::equal($b->pong(), "Foo::pong();Foo::ping()", "pong failed");
|
||||
|
||||
$a = new A1(1);
|
||||
|
||||
check::equal($a->rg(2), 2, "rg failed");
|
||||
|
||||
class PhpClass extends MyClass {
|
||||
function vmethod($b) {
|
||||
$b->x = $b->x + 31;
|
||||
return $b;
|
||||
}
|
||||
}
|
||||
|
||||
$b = new Bar(3);
|
||||
$d = new MyClass();
|
||||
$c = new PhpClass();
|
||||
|
||||
$cc = MyClass::get_self($c);
|
||||
$dd = MyClass::get_self($d);
|
||||
|
||||
$bc = $cc->cmethod($b);
|
||||
$bd = $dd->cmethod($b);
|
||||
|
||||
$cc->method($b);
|
||||
|
||||
check::equal($bc->x, 34, "bc failed");
|
||||
check::equal($bd->x, 16, "bd failed");
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_classic.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(being_id,person_id,child_id,grandchild_id,caller_delcallback,caller_setcallback,caller_resetcallback,caller_call,caller_baseclass));
|
||||
// No new classes
|
||||
check::classes(array(Being,Person,Child,GrandChild,OrphanPerson,OrphanChild,Caller));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
class TargetLangPerson extends Person {
|
||||
function id() {
|
||||
$identifier = "TargetLangPerson";
|
||||
return $identifier;
|
||||
}
|
||||
}
|
||||
|
||||
class TargetLangChild extends Child {
|
||||
function id() {
|
||||
$identifier = "TargetLangChild";
|
||||
return $identifier;
|
||||
}
|
||||
}
|
||||
|
||||
class TargetLangGrandChild extends GrandChild {
|
||||
function id() {
|
||||
$identifier = "TargetLangGrandChild";
|
||||
return $identifier;
|
||||
}
|
||||
}
|
||||
|
||||
# Semis - don't override id() in target language
|
||||
class TargetLangSemiPerson extends Person {
|
||||
# No id() override
|
||||
}
|
||||
|
||||
class TargetLangSemiChild extends Child {
|
||||
# No id() override
|
||||
}
|
||||
|
||||
class TargetLangSemiGrandChild extends GrandChild {
|
||||
# No id() override
|
||||
}
|
||||
|
||||
# Orphans - don't override id() in C++
|
||||
class TargetLangOrphanPerson extends OrphanPerson {
|
||||
function id() {
|
||||
$identifier = "TargetLangOrphanPerson";
|
||||
return $identifier;
|
||||
}
|
||||
}
|
||||
|
||||
class TargetLangOrphanChild extends OrphanChild {
|
||||
function id() {
|
||||
$identifier = "TargetLangOrphanChild";
|
||||
return $identifier;
|
||||
}
|
||||
}
|
||||
|
||||
function mycheck($person, $expected) {
|
||||
$debug = 0;
|
||||
# Normal target language polymorphic call
|
||||
$ret = $person->id();
|
||||
if ($debug)
|
||||
print $ret . "\n";
|
||||
check::equal($ret, $expected, "#1 failed");
|
||||
|
||||
# Polymorphic call from C++
|
||||
$caller = new Caller();
|
||||
$caller->setCallback($person);
|
||||
$ret = $caller->call();
|
||||
if ($debug)
|
||||
print $ret . "\n";
|
||||
check::equal($ret, $expected, "#2 failed");
|
||||
|
||||
# Polymorphic call of object created in target language and passed to
|
||||
# C++ and back again
|
||||
$baseclass = $caller->baseClass();
|
||||
$ret = $baseclass->id();
|
||||
if ($debug)
|
||||
print $ret . "\n";
|
||||
# TODO: Currently we do not track the dynamic type of returned
|
||||
# objects, so in case it's possible that the dynamic type is not equal
|
||||
# to the static type, we skip this check.
|
||||
if (get_parent_class($person) === false)
|
||||
check::equal($ret, $expected, "#3 failed");
|
||||
|
||||
$caller->resetCallback();
|
||||
if ($debug)
|
||||
print "----------------------------------------\n";
|
||||
}
|
||||
|
||||
$person = new Person();
|
||||
mycheck($person, "Person");
|
||||
unset($person);
|
||||
|
||||
$person = new Child();
|
||||
mycheck($person, "Child");
|
||||
unset($person);
|
||||
|
||||
$person = new GrandChild();
|
||||
mycheck($person, "GrandChild");
|
||||
unset($person);
|
||||
|
||||
$person = new TargetLangPerson();
|
||||
mycheck($person, "TargetLangPerson");
|
||||
unset($person);
|
||||
|
||||
$person = new TargetLangChild();
|
||||
mycheck($person, "TargetLangChild");
|
||||
unset($person);
|
||||
|
||||
$person = new TargetLangGrandChild();
|
||||
mycheck($person, "TargetLangGrandChild");
|
||||
unset($person);
|
||||
|
||||
# Semis - don't override id() in target language
|
||||
$person = new TargetLangSemiPerson();
|
||||
mycheck($person, "Person");
|
||||
unset($person);
|
||||
|
||||
$person = new TargetLangSemiChild();
|
||||
mycheck($person, "Child");
|
||||
unset($person);
|
||||
|
||||
$person = new TargetLangSemiGrandChild();
|
||||
mycheck($person, "GrandChild");
|
||||
unset($person);
|
||||
|
||||
# Orphans - don't override id() in C++
|
||||
$person = new OrphanPerson();
|
||||
mycheck($person, "Person");
|
||||
unset($person);
|
||||
|
||||
$person = new OrphanChild();
|
||||
mycheck($person, "Child");
|
||||
unset($person);
|
||||
|
||||
$person = new TargetLangOrphanPerson();
|
||||
mycheck($person, "TargetLangOrphanPerson");
|
||||
unset($person);
|
||||
|
||||
$person = new TargetLangOrphanChild();
|
||||
mycheck($person, "TargetLangOrphanChild");
|
||||
unset($person);
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_default.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_msg,foo_getmsg,bar_msg,bar_getmsg,defaultsbase_defaultargs,defaultsderived_defaultargs));
|
||||
// No new classes
|
||||
check::classes(array(Foo,Bar,DefaultsBase,DefaultsDerived));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
$f = new Foo();
|
||||
$f = new Foo(1);
|
||||
|
||||
$f = new Bar();
|
||||
$f = new Bar(1);
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_detect.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_cloner,foo_get_value,foo_get_class,foo_just_do_it,bar_baseclass,bar_cloner,bar_get_value,bar_get_class,bar_just_do_it));
|
||||
// No new classes
|
||||
check::classes(array(A,Foo,Bar));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
class MyBar extends Bar {
|
||||
function __construct($val = 2) {
|
||||
parent::__construct();
|
||||
$this->val = $val;
|
||||
}
|
||||
|
||||
function get_value() {
|
||||
$this->val = $this->val + 1;
|
||||
return $this->val;
|
||||
}
|
||||
|
||||
function get_class() {
|
||||
$this->val = $this->val + 1;
|
||||
return new A();
|
||||
}
|
||||
|
||||
function just_do_it() {
|
||||
$this->val = $this->val + 1;
|
||||
}
|
||||
|
||||
/* clone is a reserved keyword */
|
||||
function clone_() {
|
||||
return new MyBar($this->val);
|
||||
}
|
||||
}
|
||||
|
||||
$b = new MyBar();
|
||||
|
||||
$f = $b->baseclass();
|
||||
|
||||
$v = $f->get_value();
|
||||
$a = $f->get_class();
|
||||
$f->just_do_it();
|
||||
|
||||
$c = $b->clone_();
|
||||
$vc = $c->get_value();
|
||||
|
||||
check::equal($v, 3, "f: Bad virtual detection");
|
||||
check::equal($b->val, 5, "b: Bad virtual detection");
|
||||
check::equal($vc, 6, "c: Bad virtual detection");
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_enum.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_say_hello,foo_say_hi,foo_say_bye,foo_say_hi_ref,foo_ping,foo_ping_ref,foo_ping_member_enum,a_f,a2_f));
|
||||
// No new classes
|
||||
check::classes(array(director_enum,Foo,A,B,A2,B2));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
class MyFoo extends Foo {
|
||||
function say_hi($val) {
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
|
||||
$b = new Foo();
|
||||
$a = new MyFoo();
|
||||
|
||||
check::equal($a->say_hi(director_enum::hello), $b->say_hello(director_enum::hi), "say failed");
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_exception.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_ping,foo_pong,launder,bar_ping,bar_pong,bar_pang));
|
||||
// No new classes
|
||||
check::classes(array(director_exception,Foo,Exception1,Exception2,Base,Bar));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
class MyException extends Exception {
|
||||
function __construct($a, $b) {
|
||||
$this->msg = $a . $b;
|
||||
}
|
||||
}
|
||||
|
||||
class MyFoo extends Foo {
|
||||
function ping() {
|
||||
throw new Exception("MyFoo::ping() EXCEPTION");
|
||||
}
|
||||
}
|
||||
|
||||
class MyFoo2 extends Foo {
|
||||
function ping() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class MyFoo3 extends Foo {
|
||||
function ping() {
|
||||
throw new MyException("foo", "bar");
|
||||
}
|
||||
}
|
||||
|
||||
# Check that the Exception raised by MyFoo.ping() is returned by
|
||||
# MyFoo.pong().
|
||||
$ok = 0;
|
||||
$a = new MyFoo();
|
||||
# TODO: Currently we do not track the dynamic type of returned
|
||||
# objects, so we skip the launder() call.
|
||||
#$b = director_exception::launder($a);
|
||||
$b = $a;
|
||||
try {
|
||||
$b->pong();
|
||||
} catch (Exception $e) {
|
||||
$ok = 1;
|
||||
check::equal($e->getMessage(), "MyFoo::ping() EXCEPTION", "Unexpected error message #1");
|
||||
}
|
||||
check::equal($ok, 1, "Got no exception while expected one #1");
|
||||
|
||||
# Check that the director can return an exception which requires two
|
||||
# arguments to the constructor, without mangling it.
|
||||
$ok = 0;
|
||||
$a = new MyFoo3();
|
||||
#$b = director_exception::launder($a);
|
||||
$b = $a;
|
||||
try {
|
||||
$b->pong();
|
||||
} catch (Exception $e) {
|
||||
$ok = 1;
|
||||
check::equal($e->msg, "foobar", "Unexpected error message #2");
|
||||
}
|
||||
check::equal($ok, 1, "Got no exception while expected one #2");
|
||||
|
||||
try {
|
||||
throw new Exception2();
|
||||
} catch (Exception2 $e2) {
|
||||
}
|
||||
|
||||
try {
|
||||
throw new Exception1();
|
||||
} catch (Exception1 $e1) {
|
||||
}
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_extend.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(spobject_getfoobar,spobject_dummy,spobject_exceptionmethod));
|
||||
// No new classes
|
||||
check::classes(array(SpObject));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
class MyObject extends SpObject{
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
return;
|
||||
}
|
||||
|
||||
function getFoo() {
|
||||
return 123;
|
||||
}
|
||||
}
|
||||
|
||||
$m = new MyObject();
|
||||
check::equal($m->dummy(), 666, "1st call");
|
||||
check::equal($m->dummy(), 666, "2st call"); // Locked system
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_finalizer.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_orstatus,deletefoo,getstatus,launder,resetstatus));
|
||||
// No new classes
|
||||
check::classes(array(director_finalizer,Foo));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
class MyFoo extends Foo {
|
||||
function __destruct() {
|
||||
$this->orStatus(2);
|
||||
if (method_exists(parent, "__destruct")) {
|
||||
parent::__destruct();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resetStatus();
|
||||
|
||||
$a = new MyFoo();
|
||||
unset($a);
|
||||
|
||||
check::equal(getStatus(), 3, "getStatus() failed #1");
|
||||
|
||||
resetStatus();
|
||||
|
||||
$a = new MyFoo();
|
||||
launder($a);
|
||||
|
||||
check::equal(getStatus(), 0, "getStatus() failed #2");
|
||||
|
||||
unset($a);
|
||||
|
||||
check::equal(getStatus(), 3, "getStatus() failed #3");
|
||||
|
||||
resetStatus();
|
||||
|
||||
$a = new MyFoo();
|
||||
$a->thisown = 0;
|
||||
deleteFoo($a);
|
||||
unset($a);
|
||||
|
||||
check::equal(getStatus(), 3, "getStatus() failed #4");
|
||||
|
||||
resetStatus();
|
||||
|
||||
$a = new MyFoo();
|
||||
$a->thisown = 0;
|
||||
deleteFoo(launder($a));
|
||||
unset($a);
|
||||
|
||||
check::equal(getStatus(), 3, "getStatus() failed #5");
|
||||
|
||||
resetStatus();
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_frob.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(alpha_abs_method,bravo_abs_method,charlie_abs_method,ops_opint,ops_opintstarstarconst,ops_opintamp,ops_opintstar,ops_opconstintintstar,prims_ull,prims_callull,corecallbacks_on3dengineredrawn,corecallbacks_on3dengineredrawn2));
|
||||
// No new classes
|
||||
check::classes(array(Alpha,Bravo,Charlie,Delta,Ops,Prims,corePoint3d,coreCallbacks_On3dEngineRedrawnData,coreCallbacksOn3dEngineRedrawnData,coreCallbacks));
|
||||
// now new vars
|
||||
check::globals(array(corecallbacks_on3dengineredrawndata__eye,corecallbacks_on3dengineredrawndata__at,corecallbackson3dengineredrawndata__eye,corecallbackson3dengineredrawndata__at));
|
||||
|
||||
$foo = new Bravo();
|
||||
$s = $foo->abs_method();
|
||||
|
||||
check::equal($s, "Bravo::abs_method()", "s failed");
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
// Sample test file
|
||||
|
||||
require "tests.php";
|
||||
require "director_nested.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_int_advance,foo_int_do_advance,bar_step,bar_do_advance,bar_do_step,foobar_int_get_value,foobar_int_get_name,foobar_int_name,foobar_int_get_self,foobar_int_do_advance,foobar_int_do_step));
|
||||
// No new classes
|
||||
check::classes(array(Foo_int,Bar,FooBar_int));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
class A extends FooBar_int {
|
||||
function do_step() {
|
||||
return "A::do_step;";
|
||||
}
|
||||
|
||||
function get_value() {
|
||||
return "A::get_value";
|
||||
}
|
||||
}
|
||||
|
||||
$a = new A();
|
||||
check::equal($a->step(), "Bar::step;Foo::advance;Bar::do_advance;A::do_step;", "Bad A virtual resolution");
|
||||
|
||||
class B extends FooBar_int {
|
||||
function do_advance() {
|
||||
return "B::do_advance;" . $this->do_step();
|
||||
}
|
||||
|
||||
function do_step() {
|
||||
return "B::do_step;";
|
||||
}
|
||||
|
||||
function get_value() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
$b = new B();
|
||||
|
||||
check::equal($b->step(), "Bar::step;Foo::advance;B::do_advance;B::do_step;", "Bad B virtual resolution");
|
||||
|
||||
class C extends FooBar_int {
|
||||
function do_advance() {
|
||||
return "C::do_advance;" . parent::do_advance();
|
||||
}
|
||||
|
||||
function do_step() {
|
||||
return "C::do_step;";
|
||||
}
|
||||
|
||||
function get_value() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
function get_name() {
|
||||
return parent::get_name() . " hello";
|
||||
}
|
||||
}
|
||||
|
||||
$cc = new C();
|
||||
# TODO: Currently we do not track the dynamic type of returned
|
||||
# objects, so we skip the get_self() call.
|
||||
#$c = Foobar_int::get_self($cc);
|
||||
$c = $cc;
|
||||
$c->advance();
|
||||
|
||||
check::equal($c->get_name(), "FooBar::get_name hello", "get_name failed");
|
||||
|
||||
check::equal($c->name(), "FooBar::get_name hello", "name failed");
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_profile.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(b_fn,b_vfi,b_fi,b_fj,b_fk,b_fl,b_get_self,b_vfs,b_fs));
|
||||
// No new classes
|
||||
check::classes(array(A,B));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
class MyB extends B {
|
||||
function vfi($a) {
|
||||
return $a+3;
|
||||
}
|
||||
}
|
||||
|
||||
$a = new A();
|
||||
$myb = new MyB();
|
||||
$b = B::get_self($myb);
|
||||
|
||||
$i = 50000;
|
||||
$a = 1;
|
||||
|
||||
while ($i) {
|
||||
$a = $b->fi($a); #1
|
||||
$a = $b->fi($a); #2
|
||||
$a = $b->fi($a); #3
|
||||
$a = $b->fi($a); #4
|
||||
$a = $b->fi($a); #5
|
||||
$a = $b->fi($a); #6
|
||||
$a = $b->fi($a); #7
|
||||
$a = $b->fi($a); #8
|
||||
$a = $b->fi($a); #9
|
||||
$a = $b->fi($a); #10
|
||||
$a = $b->fi($a); #1
|
||||
$a = $b->fi($a); #2
|
||||
$a = $b->fi($a); #3
|
||||
$a = $b->fi($a); #4
|
||||
$a = $b->fi($a); #5
|
||||
$a = $b->fi($a); #6
|
||||
$a = $b->fi($a); #7
|
||||
$a = $b->fi($a); #8
|
||||
$a = $b->fi($a); #9
|
||||
$a = $b->fi($a); #20
|
||||
$i -= 1;
|
||||
}
|
||||
|
||||
print $a . "\n";
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_protected.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_pong,foo_s,foo_q,foo_ping,foo_pang,foo_used,bar_create,bar_pong,bar_used,bar_ping,bar_pang,a_draw,b_draw));
|
||||
// No new classes
|
||||
check::classes(array(Foo,Bar,PrivateFoo,A,B,AA,BB));
|
||||
// now new vars
|
||||
check::globals(array(bar_a));
|
||||
|
||||
class FooBar extends Bar {
|
||||
protected function ping() {
|
||||
return "FooBar::ping();";
|
||||
}
|
||||
}
|
||||
|
||||
class FooBar2 extends Bar {
|
||||
function ping() {
|
||||
return "FooBar2::ping();";
|
||||
}
|
||||
|
||||
function pang() {
|
||||
return "FooBar2::pang();";
|
||||
}
|
||||
}
|
||||
|
||||
$b = new Bar();
|
||||
$f = $b->create();
|
||||
$fb = new FooBar();
|
||||
$fb2 = new FooBar2();
|
||||
|
||||
check::equal($fb->used(), "Foo::pang();Bar::pong();Foo::pong();FooBar::ping();", "bad FooBar::used");
|
||||
|
||||
check::equal($fb2->used(), "FooBar2::pang();Bar::pong();Foo::pong();FooBar2::ping();", "bad FooBar2::used");
|
||||
|
||||
check::equal($b->pong(), "Bar::pong();Foo::pong();Bar::ping();", "bad Bar::pong");
|
||||
|
||||
check::equal($f->pong(), "Bar::pong();Foo::pong();Bar::ping();", "bad Foo::pong");
|
||||
|
||||
check::equal($fb->pong(), "Bar::pong();Foo::pong();FooBar::ping();", "bad FooBar::pong");
|
||||
|
||||
$method = new ReflectionMethod('Bar', 'ping');
|
||||
check::equal($method->isProtected(), true, "Boo::ping should be protected");
|
||||
|
||||
$method = new ReflectionMethod('Foo', 'ping');
|
||||
check::equal($method->isProtected(), true, "Foo::ping should be protected");
|
||||
|
||||
$method = new ReflectionMethod('FooBar', 'pang');
|
||||
check::equal($method->isProtected(), true, "FooBar::pang should be protected");
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_stl.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_bar,foo_ping,foo_pong,foo_tping,foo_tpong,foo_pident,foo_vident,foo_vsecond,foo_tpident,foo_tvident,foo_tvsecond,foo_vidents,foo_tvidents));
|
||||
// No new classes
|
||||
check::classes(array(Foo));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
class MyFoo extends Foo {
|
||||
function ping($s) {
|
||||
return "MyFoo::ping():" . $s;
|
||||
}
|
||||
|
||||
function pident($arg) {
|
||||
return $arg;
|
||||
}
|
||||
|
||||
function vident($v) {
|
||||
return $v;
|
||||
}
|
||||
|
||||
function vidents($v) {
|
||||
return $v;
|
||||
}
|
||||
|
||||
function vsecond($v1, $v2) {
|
||||
return $v2;
|
||||
}
|
||||
}
|
||||
|
||||
$a = new MyFoo();
|
||||
|
||||
$a->tping("hello");
|
||||
$a->tpong("hello");
|
||||
|
||||
# TODO: automatic conversion between PHP arrays and std::pair or
|
||||
# std::vector is not yet implemented.
|
||||
/*$p = array(1, 2);
|
||||
$a->pident($p);
|
||||
$v = array(3, 4);
|
||||
$a->vident($v);
|
||||
|
||||
$a->tpident($p);
|
||||
$a->tvident($v);
|
||||
|
||||
$v1 = array(3, 4);
|
||||
$v2 = array(5, 6);
|
||||
|
||||
$a->tvsecond($v1, $v2);
|
||||
|
||||
$vs = array("hi", "hello");
|
||||
$vs;
|
||||
$a->tvidents($vs);*/
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_string.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(a_get_first,a_call_get_first,a_string_length,a_process_text,a_call_process_func,stringvector_size,stringvector_is_empty,stringvector_clear,stringvector_push,stringvector_pop));
|
||||
// No new classes
|
||||
check::classes(array(A,StringVector));
|
||||
// now new vars
|
||||
check::globals(array(a,a_call,a_m_strings,stringvector));
|
||||
|
||||
class B extends A {
|
||||
function get_first() {
|
||||
return parent::get_first() . " world!";
|
||||
}
|
||||
|
||||
function process_text($string) {
|
||||
parent::process_text($string);
|
||||
$this->smem = "hello";
|
||||
}
|
||||
}
|
||||
|
||||
$b = new B("hello");
|
||||
|
||||
$b->get(0);
|
||||
check::equal($b->get_first(),"hello world!", "get_first failed");
|
||||
|
||||
$b->call_process_func();
|
||||
|
||||
check::equal($b->smem, "hello", "smem failed");
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_thread.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(millisecondsleep,foo_stop,foo_run,foo_do_foo));
|
||||
// No new classes
|
||||
check::classes(array(director_thread,Foo));
|
||||
// now new vars
|
||||
check::globals(array(foo_val));
|
||||
|
||||
class Derived extends Foo {
|
||||
function do_foo() {
|
||||
$this->val = $this->val - 1;
|
||||
}
|
||||
}
|
||||
|
||||
$d = new Derived();
|
||||
$d->run();
|
||||
|
||||
if ($d->val >= 0) {
|
||||
check::fail($d->val);
|
||||
}
|
||||
|
||||
$d->stop();
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "director_unroll.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_ping,foo_pong));
|
||||
// No new classes
|
||||
check::classes(array(Foo,Bar));
|
||||
// now new vars
|
||||
check::globals(array(bar));
|
||||
|
||||
class MyFoo extends Foo {
|
||||
function ping() {
|
||||
return "MyFoo::ping()";
|
||||
}
|
||||
}
|
||||
|
||||
$a = new MyFoo();
|
||||
|
||||
$b = new Bar();
|
||||
|
||||
$b->set($a);
|
||||
$c = $b->get();
|
||||
|
||||
check::equal($a->this, $c->this, "this failed");
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -31,7 +31,8 @@ $spam=new spam();
|
|||
check::is_a($spam,"spam");
|
||||
check::equal(1,$spam->_foo,"1==spam->_foo");
|
||||
check::equal(2,$spam->_bar,"2==spam->_bar");
|
||||
check::equal(3,$spam->_baz,"3==spam->_baz");
|
||||
// multiple inheritance not supported in PHP
|
||||
check::equal(null,$spam->_baz,"null==spam->_baz");
|
||||
check::equal(4,$spam->_spam,"4==spam->_spam");
|
||||
|
||||
check::done();
|
||||
|
|
|
@ -11,5 +11,11 @@ check::classes(array(doubleArray));
|
|||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
$d = new doubleArray(10);
|
||||
|
||||
$d->setitem(0, 7);
|
||||
$d->setitem(5, $d->getitem(0) + 3);
|
||||
check::equal($d->getitem(0) + $d->getitem(5), 17., "7+10==17");
|
||||
|
||||
check::done();
|
||||
?>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
require "tests.php";
|
||||
require "li_factory.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(geometry_draw,geometry_create,geometry_clone_,point_draw,point_width,point_clone_,circle_draw,circle_radius,circle_clone_));
|
||||
// No new classes
|
||||
check::classes(array(Geometry,Point,Circle));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
$circle = Geometry::create(Geometry::CIRCLE);
|
||||
$r = $circle->radius();
|
||||
check::equal($r, 1.5, "r failed");
|
||||
|
||||
$point = Geometry::create(Geometry::POINT);
|
||||
$w = $point->width();
|
||||
check::equal($w, 1.0, "w failed");
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
// Sample test file
|
||||
|
||||
require "tests.php";
|
||||
require "newobject1.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_makefoo,foo_makemore,foo_foocount));
|
||||
// No new classes
|
||||
check::classes(array(Foo));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
$foo = Foo::makeFoo();
|
||||
check::equal(get_class($foo), "Foo", "static failed");
|
||||
$bar = $foo->makeMore();
|
||||
check::equal(get_class($bar), "Foo", "regular failed");
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
// Sample test file
|
||||
|
||||
require "tests.php";
|
||||
require "prefix.php";
|
||||
|
||||
// No new functions
|
||||
check::functions(array(foo_get_self));
|
||||
// No new classes
|
||||
check::classes(array(ProjectFoo));
|
||||
// now new vars
|
||||
check::globals(array());
|
||||
|
||||
$f = new ProjectFoo();
|
||||
// This resulted in "Fatal error: Class 'Foo' not found"
|
||||
$f->get_self();
|
||||
|
||||
check::done();
|
||||
?>
|
|
@ -34,8 +34,11 @@ class check {
|
|||
$df=array_flip($df[internal]);
|
||||
foreach($_original_functions[internal] as $func) unset($df[$func]);
|
||||
// Now chop out any get/set accessors
|
||||
foreach(array_keys($df) as $func) if (GETSET && ereg('_[gs]et$',$func)) $extrags[]=$func;
|
||||
else $extra[]=$func;
|
||||
foreach(array_keys($df) as $func)
|
||||
if ((GETSET && ereg('_[gs]et$',$func)) || ereg('^new_', $func)
|
||||
|| ereg('_(alter|get)_newobject$', $func))
|
||||
$extrags[]=$func;
|
||||
else $extra[]=$func;
|
||||
// $extra=array_keys($df);
|
||||
}
|
||||
if ($gs) return $extrags;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// Test that was failing for PHP - the value of the -prefix option was
|
||||
// ignored
|
||||
%module prefix
|
||||
|
||||
%inline %{
|
||||
|
||||
class Foo {
|
||||
public:
|
||||
Foo *get_self() {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
%}
|
|
@ -0,0 +1,198 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* See the LICENSE file for information on copyright, usage and redistribution
|
||||
* of SWIG, and the README file for authors - http://www.swig.org/release.html.
|
||||
*
|
||||
* director.swg
|
||||
*
|
||||
* This file contains support for director classes that proxy
|
||||
* method calls from C++ to PHP extensions.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
#ifndef SWIG_DIRECTOR_PHP_HEADER_
|
||||
#define SWIG_DIRECTOR_PHP_HEADER_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
/*
|
||||
Use -DSWIG_DIRECTOR_STATIC if you prefer to avoid the use of the
|
||||
'Swig' namespace. This could be useful for multi-modules projects.
|
||||
*/
|
||||
#ifdef SWIG_DIRECTOR_STATIC
|
||||
/* Force anonymous (static) namespace */
|
||||
#define Swig
|
||||
#endif
|
||||
|
||||
namespace Swig {
|
||||
/* memory handler */
|
||||
struct GCItem
|
||||
{
|
||||
virtual ~GCItem() {}
|
||||
|
||||
virtual int get_own() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct GCItem_var
|
||||
{
|
||||
GCItem_var(GCItem *item = 0) : _item(item)
|
||||
{
|
||||
}
|
||||
|
||||
GCItem_var& operator=(GCItem *item)
|
||||
{
|
||||
GCItem *tmp = _item;
|
||||
_item = item;
|
||||
delete tmp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~GCItem_var()
|
||||
{
|
||||
delete _item;
|
||||
}
|
||||
|
||||
GCItem * operator->() const
|
||||
{
|
||||
return _item;
|
||||
}
|
||||
|
||||
private:
|
||||
GCItem *_item;
|
||||
};
|
||||
|
||||
struct GCItem_Object : GCItem
|
||||
{
|
||||
GCItem_Object(int own) : _own(own)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~GCItem_Object()
|
||||
{
|
||||
}
|
||||
|
||||
int get_own() const
|
||||
{
|
||||
return _own;
|
||||
}
|
||||
|
||||
private:
|
||||
int _own;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct GCItem_T : GCItem
|
||||
{
|
||||
GCItem_T(Type *ptr) : _ptr(ptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~GCItem_T()
|
||||
{
|
||||
delete _ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
Type *_ptr;
|
||||
};
|
||||
|
||||
class Director {
|
||||
protected:
|
||||
zval *swig_self;
|
||||
typedef std::map<void*, GCItem_var> ownership_map;
|
||||
mutable ownership_map owner;
|
||||
public:
|
||||
Director(zval* self) : swig_self(self) {
|
||||
}
|
||||
|
||||
~Director() {
|
||||
for (ownership_map::iterator i = owner.begin(); i != owner.end(); i++) {
|
||||
owner.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_overriden_method(char *cname, char *lc_fname) {
|
||||
zval classname;
|
||||
zend_class_entry **ce;
|
||||
zend_function *mptr;
|
||||
int name_len = strlen(lc_fname);
|
||||
|
||||
ZVAL_STRING(&classname, cname, 0);
|
||||
if (zend_lookup_class(Z_STRVAL_P(&classname), Z_STRLEN_P(&classname), &ce TSRMLS_CC) != SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
if (zend_hash_find(&(*ce)->function_table, lc_fname, name_len + 1, (void**) &mptr) != SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
// common.scope points to the declaring class
|
||||
return strcmp(mptr->common.scope->name, cname);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void swig_acquire_ownership(Type *vptr) const
|
||||
{
|
||||
if (vptr) {
|
||||
owner[vptr] = new GCItem_T<Type>(vptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* base class for director exceptions */
|
||||
class DirectorException {
|
||||
protected:
|
||||
std::string swig_msg;
|
||||
public:
|
||||
DirectorException(int code, const char *hdr, const char* msg)
|
||||
: swig_msg(hdr)
|
||||
{
|
||||
if (strlen(msg)) {
|
||||
swig_msg += " ";
|
||||
swig_msg += msg;
|
||||
}
|
||||
SWIG_ErrorCode() = code;
|
||||
SWIG_ErrorMsg() = swig_msg.c_str();
|
||||
}
|
||||
|
||||
static void raise(int code, const char *hdr, const char* msg)
|
||||
{
|
||||
throw DirectorException(code, hdr, msg);
|
||||
}
|
||||
};
|
||||
|
||||
/* attempt to call a pure virtual method via a director method */
|
||||
class DirectorPureVirtualException : public Swig::DirectorException
|
||||
{
|
||||
public:
|
||||
DirectorPureVirtualException(const char* msg)
|
||||
: DirectorException(E_ERROR, "Swig director pure virtual method called", msg)
|
||||
{
|
||||
}
|
||||
|
||||
static void raise(const char *msg)
|
||||
{
|
||||
throw DirectorPureVirtualException(msg);
|
||||
}
|
||||
};
|
||||
/* any php exception that occurs during a director method call */
|
||||
class DirectorMethodException : public Swig::DirectorException
|
||||
{
|
||||
public:
|
||||
DirectorMethodException(const char* msg = "")
|
||||
: DirectorException(E_ERROR, "Swig director method error", msg)
|
||||
{
|
||||
}
|
||||
|
||||
static void raise(const char *msg)
|
||||
{
|
||||
throw DirectorMethodException(msg);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Implement a more natural wrap for factory methods, for example, if
|
||||
you have:
|
||||
|
||||
---- geometry.h --------
|
||||
struct Geometry {
|
||||
enum GeomType{
|
||||
POINT,
|
||||
CIRCLE
|
||||
};
|
||||
|
||||
virtual ~Geometry() {}
|
||||
virtual int draw() = 0;
|
||||
|
||||
//
|
||||
// Factory method for all the Geometry objects
|
||||
//
|
||||
static Geometry *create(GeomType i);
|
||||
};
|
||||
|
||||
struct Point : Geometry {
|
||||
int draw() { return 1; }
|
||||
double width() { return 1.0; }
|
||||
};
|
||||
|
||||
struct Circle : Geometry {
|
||||
int draw() { return 2; }
|
||||
double radius() { return 1.5; }
|
||||
};
|
||||
|
||||
//
|
||||
// Factory method for all the Geometry objects
|
||||
//
|
||||
Geometry *Geometry::create(GeomType type) {
|
||||
switch (type) {
|
||||
case POINT: return new Point();
|
||||
case CIRCLE: return new Circle();
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
---- geometry.h --------
|
||||
|
||||
|
||||
You can use the %factory with the Geometry::create method as follows:
|
||||
|
||||
%newobject Geometry::create;
|
||||
%factory(Geometry *Geometry::create, Point, Circle);
|
||||
%include "geometry.h"
|
||||
|
||||
and Geometry::create will return a 'Point' or 'Circle' instance
|
||||
instead of the plain 'Geometry' type. For example, in python:
|
||||
|
||||
circle = Geometry.create(Geometry.CIRCLE)
|
||||
r = circle.radius()
|
||||
|
||||
where circle is a Circle proxy instance.
|
||||
|
||||
NOTES: remember to fully qualify all the type names and don't
|
||||
use %factory inside a namespace declaration, ie, instead of
|
||||
|
||||
namespace Foo {
|
||||
%factory(Geometry *Geometry::create, Point, Circle);
|
||||
}
|
||||
|
||||
use
|
||||
|
||||
%factory(Foo::Geometry *Foo::Geometry::create, Foo::Point, Foo::Circle);
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/* for loop for macro with one argument */
|
||||
%define %_formacro_1(macro, arg1,...)macro(arg1)
|
||||
#if #__VA_ARGS__ != "__fordone__"
|
||||
%_formacro_1(macro, __VA_ARGS__)
|
||||
#endif
|
||||
%enddef
|
||||
|
||||
/* for loop for macro with one argument */
|
||||
%define %formacro_1(macro,...)%_formacro_1(macro,__VA_ARGS__,__fordone__)%enddef
|
||||
%define %formacro(macro,...)%_formacro_1(macro,__VA_ARGS__,__fordone__)%enddef
|
||||
|
||||
/* for loop for macro with two arguments */
|
||||
%define %_formacro_2(macro, arg1, arg2, ...)macro(arg1, arg2)
|
||||
#if #__VA_ARGS__ != "__fordone__"
|
||||
%_formacro_2(macro, __VA_ARGS__)
|
||||
#endif
|
||||
%enddef
|
||||
|
||||
/* for loop for macro with two arguments */
|
||||
%define %formacro_2(macro,...)%_formacro_2(macro, __VA_ARGS__, __fordone__)%enddef
|
||||
|
||||
%define %_factory_dispatch(Type)
|
||||
if (!dcast) {
|
||||
Type *dobj = dynamic_cast<Type *>($1);
|
||||
if (dobj) {
|
||||
dcast = 1;
|
||||
SWIG_SetPointerZval(return_value, SWIG_as_voidptr(dobj),$descriptor(Type *), $owner);
|
||||
}
|
||||
}%enddef
|
||||
|
||||
%define %factory(Method,Types...)
|
||||
%typemap(out) Method {
|
||||
int dcast = 0;
|
||||
%formacro(%_factory_dispatch, Types)
|
||||
if (!dcast) {
|
||||
SWIG_SetPointerZval(return_value, SWIG_as_voidptr($1),$descriptor, $owner);
|
||||
}
|
||||
}%enddef
|
|
@ -89,6 +89,14 @@
|
|||
$1 = *tmp;
|
||||
}
|
||||
|
||||
%typemap(directorout) SWIGTYPE ($&1_ltype tmp)
|
||||
{
|
||||
if(SWIG_ConvertPtr(*$input, (void **) &tmp, $&1_descriptor, 0) < 0 || tmp == NULL) {
|
||||
SWIG_PHP_Error(E_ERROR, "Type error in argument $argnum of $symname. Expected $&1_descriptor");
|
||||
}
|
||||
$result = *tmp;
|
||||
}
|
||||
|
||||
%typemap(in) SWIGTYPE *,
|
||||
SWIGTYPE []
|
||||
{
|
||||
|
@ -176,17 +184,42 @@
|
|||
ZVAL_LONG(return_value,$1);
|
||||
}
|
||||
|
||||
%typemap(directorin) int,
|
||||
unsigned int,
|
||||
short,
|
||||
unsigned short,
|
||||
long,
|
||||
unsigned long,
|
||||
signed char,
|
||||
unsigned char,
|
||||
size_t,
|
||||
enum SWIGTYPE
|
||||
{
|
||||
ZVAL_LONG($input,$1_name);
|
||||
}
|
||||
|
||||
%typemap(out) bool
|
||||
{
|
||||
ZVAL_BOOL(return_value,($1)?1:0);
|
||||
}
|
||||
|
||||
%typemap(directorin) bool
|
||||
{
|
||||
ZVAL_BOOL($input,($1_name)?1:0);
|
||||
}
|
||||
|
||||
%typemap(out) float,
|
||||
double
|
||||
{
|
||||
ZVAL_DOUBLE(return_value,$1);
|
||||
}
|
||||
|
||||
%typemap(directorin) float,
|
||||
double
|
||||
{
|
||||
ZVAL_DOUBLE($input,$1_name);
|
||||
}
|
||||
|
||||
%typemap(out) char
|
||||
{
|
||||
ZVAL_STRINGL(return_value,&$1, 1, 1);
|
||||
|
@ -209,6 +242,13 @@
|
|||
SWIG_SetPointerZval(return_value, (void *)$1, $1_descriptor, $owner);
|
||||
%}
|
||||
|
||||
%typemap(directorin) SWIGTYPE *,
|
||||
SWIGTYPE [],
|
||||
SWIGTYPE &
|
||||
%{
|
||||
SWIG_SetPointerZval($input, (void *)&$1_name, $1_descriptor, $owner);
|
||||
%}
|
||||
|
||||
%typemap(out) SWIGTYPE *DYNAMIC,
|
||||
SWIGTYPE &DYNAMIC
|
||||
{
|
||||
|
@ -230,6 +270,19 @@
|
|||
}
|
||||
#endif
|
||||
|
||||
%typemap(directorin) SWIGTYPE
|
||||
#ifdef __cplusplus
|
||||
{
|
||||
SWIG_SetPointerZval($input, SWIG_as_voidptr(&$1_name), $&1_descriptor, 2);
|
||||
}
|
||||
#else
|
||||
{
|
||||
$&1_ltype resultobj = ($&1_ltype) emalloc(sizeof($1_type));
|
||||
memcpy(resultobj, &$1, sizeof($1_type));
|
||||
SWIG_SetPointerZval($input, (void *)resultobj, $&1_descriptor, 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
%typemap(out) void "";
|
||||
|
||||
%typemap(out) char [ANY]
|
||||
|
|
|
@ -13,6 +13,7 @@ extern "C" {
|
|||
#include "zend.h"
|
||||
#include "zend_API.h"
|
||||
#include "php.h"
|
||||
#include "ext/standard/php_string.h"
|
||||
|
||||
#ifdef ZEND_RAW_FENTRY
|
||||
/* ZEND_RAW_FENTRY was added somewhere between 5.2.0 and 5.2.3 */
|
||||
|
@ -84,6 +85,7 @@ typedef struct {
|
|||
static ZEND_RSRC_DTOR_FUNC(SWIG_landfill) { (void)rsrc; }
|
||||
|
||||
#define SWIG_SetPointerZval(a,b,c,d) SWIG_ZTS_SetPointerZval(a,b,c,d TSRMLS_CC)
|
||||
#define SWIG_as_voidptr(a) const_cast< void * >(static_cast< const void * >(a))
|
||||
|
||||
static void
|
||||
SWIG_ZTS_SetPointerZval(zval *z, void *ptr, swig_type_info *type, int newobject TSRMLS_DC) {
|
||||
|
@ -101,7 +103,31 @@ SWIG_ZTS_SetPointerZval(zval *z, void *ptr, swig_type_info *type, int newobject
|
|||
value=(swig_object_wrapper *)emalloc(sizeof(swig_object_wrapper));
|
||||
value->ptr=ptr;
|
||||
value->newobject=newobject;
|
||||
ZEND_REGISTER_RESOURCE(z, value, *(int *)(type->clientdata));
|
||||
if (newobject <= 1) {
|
||||
ZEND_REGISTER_RESOURCE(z, value, *(int *)(type->clientdata));
|
||||
} else {
|
||||
value->newobject = 0;
|
||||
zval *resource;
|
||||
MAKE_STD_ZVAL(resource);
|
||||
ZEND_REGISTER_RESOURCE(resource, value, *(int *)(type->clientdata));
|
||||
zend_class_entry **ce = NULL;
|
||||
zval *classname;
|
||||
MAKE_STD_ZVAL(classname);
|
||||
/* _p_Foo -> Foo */
|
||||
ZVAL_STRING(classname, (char*)type->name+3, 1);
|
||||
/* class names are stored in lowercase */
|
||||
php_strtolower(Z_STRVAL_PP(&classname), Z_STRLEN_PP(&classname));
|
||||
if (zend_lookup_class(Z_STRVAL_P(classname), Z_STRLEN_P(classname), &ce TSRMLS_CC) != SUCCESS) {
|
||||
/* class does not exists */
|
||||
object_init(z);
|
||||
} else {
|
||||
object_init_ex(z, *ce);
|
||||
}
|
||||
z->refcount = 1;
|
||||
z->is_ref = 1;
|
||||
zend_hash_update(HASH_OF(z), (char*)"_cPtr", sizeof("_cPtr"), (void*)&resource, sizeof(zval), NULL);
|
||||
FREE_ZVAL(classname);
|
||||
}
|
||||
return;
|
||||
}
|
||||
zend_error(E_ERROR, "Type: %s not registered with zend",type->name);
|
||||
|
@ -156,7 +182,7 @@ SWIG_ZTS_ConvertResourcePtr(zval *z, swig_type_info *ty, int flags TSRMLS_DC) {
|
|||
char *type_name;
|
||||
|
||||
value = (swig_object_wrapper *) zend_list_find(z->value.lval, &type);
|
||||
if ( flags && SWIG_POINTER_DISOWN ) {
|
||||
if ( flags & SWIG_POINTER_DISOWN ) {
|
||||
value->newobject = 0;
|
||||
}
|
||||
p = value->ptr;
|
||||
|
|
|
@ -35,6 +35,11 @@ namespace std {
|
|||
$1.assign(Z_STRVAL_PP($input), Z_STRLEN_PP($input));
|
||||
%}
|
||||
|
||||
%typemap(directorout) string %{
|
||||
convert_to_string_ex($input);
|
||||
$result.assign(Z_STRVAL_PP($input), Z_STRLEN_PP($input));
|
||||
%}
|
||||
|
||||
%typemap(typecheck,precedence=SWIG_TYPECHECK_STRING) const string& %{
|
||||
$1 = ( Z_TYPE_PP($input) == IS_STRING ) ? 1 : 0;
|
||||
%}
|
||||
|
@ -43,6 +48,10 @@ namespace std {
|
|||
ZVAL_STRINGL($result, const_cast<char*>($1.data()), $1.size(), 1);
|
||||
%}
|
||||
|
||||
%typemap(directorin) string %{
|
||||
ZVAL_STRINGL($input, const_cast<char*>($1_name.data()), $1_name.size(), 1);
|
||||
%}
|
||||
|
||||
%typemap(out) const string & %{
|
||||
ZVAL_STRINGL($result, const_cast<char*>($1->data()), $1->size(), 1);
|
||||
%}
|
||||
|
@ -63,6 +72,14 @@ namespace std {
|
|||
$1 = &temp;
|
||||
%}
|
||||
|
||||
%typemap(directorout) string & (std::string *temp) %{
|
||||
convert_to_string_ex($input);
|
||||
temp = new std::string;
|
||||
temp->assign(Z_STRVAL_PP($input), Z_STRLEN_PP($input));
|
||||
swig_acquire_ownership(temp);
|
||||
$result = temp;
|
||||
%}
|
||||
|
||||
%typemap(argout) string & %{
|
||||
ZVAL_STRINGL(*($input), const_cast<char*>($1->data()), $1->size(), 1);
|
||||
%}
|
||||
|
|
|
@ -37,6 +37,10 @@
|
|||
%{
|
||||
CONVERT_IN($1,$1_ltype,$input);
|
||||
%}
|
||||
%typemap(directorout) TYPE, const TYPE &
|
||||
%{
|
||||
CONVERT_IN($result,$1_ltype,$input);
|
||||
%}
|
||||
%enddef
|
||||
|
||||
%fragment("t_output_helper","header") %{
|
||||
|
|
1
README
1
README
|
@ -59,6 +59,7 @@ Major contributors include:
|
|||
Martin Froehlich <MartinFroehlich@ACM.org> (Guile)
|
||||
Marcio Luis Teixeira <marciot@holly.colostate.edu> (Guile)
|
||||
Duncan Temple Lang (R)
|
||||
Miklos Vajna <vmiklos@frugalware.org> (PHP directors)
|
||||
|
||||
Past contributors include:
|
||||
James Michael DuPont, Clark McGrew, Dustin Mitchell, Ian Cooke, Catalin Dumitrescu, Baran
|
||||
|
|
|
@ -718,6 +718,10 @@ String *Swig_overload_dispatch(Node *n, const_String_or_char_ptr fmt, int *maxar
|
|||
Parm *pi = Getattr(ni, "wrap:parms");
|
||||
int num_required = emit_num_required(pi);
|
||||
int num_arguments = emit_num_arguments(pi);
|
||||
if (GetFlag(n, "wrap:this")) {
|
||||
num_required++;
|
||||
num_arguments++;
|
||||
}
|
||||
if (num_arguments > *maxargs)
|
||||
*maxargs = num_arguments;
|
||||
int varargs = emit_isvarargs(pi);
|
||||
|
@ -751,7 +755,7 @@ String *Swig_overload_dispatch(Node *n, const_String_or_char_ptr fmt, int *maxar
|
|||
Printf(f, "}\n");
|
||||
Delete(lfmt);
|
||||
}
|
||||
if (print_typecheck(f, j, pj)) {
|
||||
if (print_typecheck(f, (GetFlag(n, "wrap:this") ? j + 1 : j), pj)) {
|
||||
Printf(f, "if (_v) {\n");
|
||||
num_braces++;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue