mirror of
https://github.com/boostorg/python.git
synced 2026-01-26 18:52:26 +00:00
New Wrapper Facility
[SVN r26209]
This commit is contained in:
@@ -30,7 +30,7 @@
|
||||
</h3></div></div>
|
||||
<div><p class="copyright">Copyright © 2002-2004 Joel de Guzman, David Abrahams</p></div>
|
||||
<div><div class="legalnotice">
|
||||
<a name="id446797"></a><p>
|
||||
<a name="id376569"></a><p>
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
<a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">
|
||||
@@ -52,7 +52,6 @@
|
||||
<dt><span class="section"><a href="python/exposing.html#python.class_properties">Class Properties</a></span></dt>
|
||||
<dt><span class="section"><a href="python/exposing.html#python.inheritance">Inheritance</a></span></dt>
|
||||
<dt><span class="section"><a href="python/exposing.html#python.class_virtual_functions">Class Virtual Functions</a></span></dt>
|
||||
<dt><span class="section"><a href="python/exposing.html#python.deriving_a_python_class">Deriving a Python Class</a></span></dt>
|
||||
<dt><span class="section"><a href="python/exposing.html#python.virtual_functions_with_default_implementations">Virtual Functions with Default Implementations</a></span></dt>
|
||||
<dt><span class="section"><a href="python/exposing.html#python.class_operators_special_functions">Class Operators/Special Functions</a></span></dt>
|
||||
</dl></dd>
|
||||
@@ -97,7 +96,7 @@ metaprogramming techniques simplifies its syntax for users, so that
|
||||
wrapping code takes on the look of a kind of declarative interface
|
||||
definition language (IDL).</p>
|
||||
<a name="quickstart.hello_world"></a><h2>
|
||||
<a name="id376455"></a>Hello World</h2>
|
||||
<a name="id376600"></a>Hello World</h2>
|
||||
<p>
|
||||
Following C/C++ tradition, let's start with the "hello, world". A C++
|
||||
Function:</p>
|
||||
@@ -124,7 +123,7 @@ hello</span><span class="special">,</span><span class="identifier"> world</span>
|
||||
</div>
|
||||
</div>
|
||||
<table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr>
|
||||
<td align="left"><small><p>Last revised: October 30, 2004 at 10:39:27 GMT</p></small></td>
|
||||
<td align="left"><small><p>Last revised: October 12, 2004 at 03:11:11 GMT</p></small></td>
|
||||
<td align="right"><small></small></td>
|
||||
</tr></table>
|
||||
<hr>
|
||||
|
||||
@@ -42,7 +42,7 @@ As mentioned, one of the goals of Boost.Python is to provide a
|
||||
bidirectional mapping between C++ and Python while maintaining the Python
|
||||
feel. Boost.Python C++ <tt class="literal">object</tt>s are as close as possible to Python. This
|
||||
should minimize the learning curve significantly.</p>
|
||||
<p><span class="inlinemediaobject"><img src="../../images/python.png"></span></p>
|
||||
<p><span class="inlinemediaobject"><img src="../images/python.png"></span></p>
|
||||
<div class="section" lang="en">
|
||||
<div class="titlepage"><div><div><h3 class="title">
|
||||
<a name="python.basic_interface"></a>Basic Interface</h3></div></div></div>
|
||||
|
||||
@@ -111,7 +111,7 @@ minimalist ['bjam] script that builds the DLLs for us.
|
||||
Before anything else, you should have the bjam executable in your boost
|
||||
directory or somewhere in your path such that [^bjam] can be executed in
|
||||
the command line. Pre-built Boost.Jam executables are available for most
|
||||
platforms. The complete list of Bjam executables can be found
|
||||
platforms. The complete list of Bjam executables can be found
|
||||
[@http://sourceforge.net/project/showfiles.php?group_id=7586 here].
|
||||
|
||||
[h2 Let's Jam!]
|
||||
@@ -175,7 +175,7 @@ Python modules. Example:
|
||||
|
||||
The above assumes that the Python installation is in [^c:/dev/tools/python]
|
||||
and that we are using Python version 2.2. You'll have to tweak this path
|
||||
appropriately.
|
||||
appropriately.
|
||||
|
||||
[blurb __tip__ Be sure not to include a third number, e.g. [*not] "2.2.1",
|
||||
even if that's the version you have.]
|
||||
@@ -454,8 +454,11 @@ Now we can inform Boost.Python of the inheritance relationship between
|
||||
|
||||
Doing so, we get some things for free:
|
||||
|
||||
# Derived automatically inherits all of Base's Python methods (wrapped C++ member functions)
|
||||
# [*If] Base is polymorphic, [^Derived] objects which have been passed to Python via a pointer or reference to [^Base] can be passed where a pointer or reference to [^Derived] is expected.
|
||||
# Derived automatically inherits all of Base's Python methods
|
||||
(wrapped C++ member functions)
|
||||
# [*If] Base is polymorphic, [^Derived] objects which have been passed to
|
||||
Python via a pointer or reference to [^Base] can be passed where a pointer
|
||||
or reference to [^Derived] is expected.
|
||||
|
||||
Now, we shall expose the C++ free functions [^b] and [^d] and [^factory]:
|
||||
|
||||
@@ -475,148 +478,73 @@ Boost.Python [link python.call_policies call policies] later.
|
||||
return_value_policy<manage_new_object>());
|
||||
|
||||
[endsect]
|
||||
|
||||
[section Class Virtual Functions]
|
||||
|
||||
In this section, we shall learn how to make functions behave
|
||||
polymorphically through virtual functions. Continuing our example, let us
|
||||
add a virtual function to our [^Base] class:
|
||||
In this section, we shall learn how to make functions behave polymorphically
|
||||
through virtual functions. Continuing our example, let us add a virtual function
|
||||
to our [^Base] class:
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual ~Base() {}
|
||||
virtual int f() = 0;
|
||||
};
|
||||
|
||||
Since [^f] is a pure virtual function, [^Base] is now an abstract
|
||||
class. Given an instance of our class, the free function [^call_f]
|
||||
calls some implementation of this virtual function in a concrete
|
||||
derived class:
|
||||
One of the goals of Boost.Python is to be minimally intrusive on an existing C++
|
||||
design. In principle, it should be possible to expose the interface for a 3rd
|
||||
party library without changing it. It is not ideal to add anything to our class
|
||||
`Base`. Yet, when you have a virtual function that's going to be overridden in
|
||||
Python and called polymorphically *from C++*, we'll need to add some
|
||||
scaffoldings to make things work properly. What we'll do is write a class
|
||||
wrapper that derives from `Base` that will unintrusively hook into the virtual
|
||||
functions so that a Python override may be called:
|
||||
|
||||
int call_f(Base& b) { return b.f(); }
|
||||
|
||||
To allow this function to be implemented in a Python derived class, we
|
||||
need to create a class wrapper:
|
||||
|
||||
struct BaseWrap : Base
|
||||
struct BaseWrap : Base, wrapper<Base>
|
||||
{
|
||||
BaseWrap(PyObject* self_)
|
||||
: self(self_) {}
|
||||
int f() { return call_method<int>(self, "f"); }
|
||||
PyObject* self;
|
||||
int f()
|
||||
{
|
||||
return this->get_override("f")();
|
||||
}
|
||||
};
|
||||
|
||||
Notice too that in addition to inheriting from `Base`, we also multiply-
|
||||
inherited `wrapper<Base>` (See [@../../../v2//wrapper.html Wrapper]). The
|
||||
`wrapper` template makes the job of wrapping classes that are meant to
|
||||
overridden in Python, easier.
|
||||
|
||||
struct BaseWrap : Base
|
||||
{
|
||||
BaseWrap(PyObject* self_)
|
||||
: self(self_) {}
|
||||
BaseWrap(PyObject* self_, Base const& copy)
|
||||
: Base(copy), self(self_) {}
|
||||
int f() { return call_method<int>(self, "f"); }
|
||||
int default_f() { return Base::f(); } // <<=== ***ADDED***
|
||||
PyObject* self;
|
||||
};
|
||||
[blurb __alert__ MSVC6/7 Workaround\n\n
|
||||
If you are using Microsoft Visual C++ 6 or 7, you have to write `f` as:\n\n
|
||||
`return call<int>(this->get_override("f").ptr());`.]
|
||||
|
||||
BaseWrap's overridden virtual member function `f` in effect calls the
|
||||
corresponding method of the Python object through `get_override`.
|
||||
|
||||
Finally, exposing `Base`:
|
||||
|
||||
class_<BaseWrap, boost::noncopyable>("Base")
|
||||
.def("f", pure_virtual(&Base::f))
|
||||
;
|
||||
|
||||
`pure_virtual` signals Boost.Python that the function `f` is a pure virtual
|
||||
function.
|
||||
|
||||
[blurb __note__ [*member function and methods]\n\n Python, like
|
||||
many object oriented languages uses the term [*methods]. Methods
|
||||
correspond roughly to C++'s [*member functions]]
|
||||
|
||||
Our class wrapper [^BaseWrap] is derived from [^Base]. Its overridden
|
||||
virtual member function [^f] in effect calls the corresponding method
|
||||
of the Python object [^self], which is a pointer back to the Python
|
||||
[^Base] object holding our [^BaseWrap] instance.
|
||||
|
||||
[blurb __note__ [*Why do we need BaseWrap?]\n\n]
|
||||
|
||||
['You may ask], "Why do we need the [^BaseWrap] derived class? This could
|
||||
have been designed so that everything gets done right inside of
|
||||
Base."\n\n
|
||||
|
||||
One of the goals of Boost.Python is to be minimally intrusive on an
|
||||
existing C++ design. In principle, it should be possible to expose the
|
||||
interface for a 3rd party library without changing it. To unintrusively
|
||||
hook into the virtual functions so that a Python override may be called, we
|
||||
must use a derived class.\n\n
|
||||
|
||||
Note however that you don't need to do this to get methods overridden
|
||||
in Python to behave virtually when called ['from] [*Python]. The only
|
||||
time you need to do the [^BaseWrap] dance is when you have a virtual
|
||||
function that's going to be overridden in Python and called
|
||||
polymorphically ['from] [*C++].]
|
||||
|
||||
Wrapping [^Base] and the free function [^call_f]:
|
||||
|
||||
class_<Base, BaseWrap, boost::noncopyable>("Base", no_init)
|
||||
;
|
||||
def("call_f", call_f);
|
||||
|
||||
Notice that we parameterized the [^class_] template with [^BaseWrap] as the
|
||||
second parameter. What is [^noncopyable]? Without it, the library will try
|
||||
to create code for converting Base return values of wrapped functions to
|
||||
Python. To do that, it needs Base's copy constructor... which isn't
|
||||
available, since Base is an abstract class.
|
||||
|
||||
In Python, let us try to instantiate our [^Base] class:
|
||||
|
||||
>>> base = Base()
|
||||
RuntimeError: This class cannot be instantiated from Python
|
||||
|
||||
Why is it an error? [^Base] is an abstract class. As such it is advisable
|
||||
to define the Python wrapper with [^no_init] as we have done above. Doing
|
||||
so will disallow abstract base classes such as [^Base] to be instantiated.
|
||||
|
||||
[endsect]
|
||||
[section Deriving a Python Class]
|
||||
|
||||
Continuing, we can derive from our base class Base in Python and override
|
||||
the virtual function in Python. Before we can do that, we have to set up
|
||||
our [^class_] wrapper as:
|
||||
|
||||
class_<Base, BaseWrap, boost::noncopyable>("Base")
|
||||
;
|
||||
|
||||
Otherwise, we have to suppress the Base class' [^no_init] by adding an
|
||||
[^__init__()] method to all our derived classes. [^no_init] actually adds
|
||||
an [^__init__] method that raises a Python RuntimeError exception.
|
||||
|
||||
>>> class Derived(Base):
|
||||
... def f(self):
|
||||
... return 42
|
||||
...
|
||||
|
||||
Cool eh? A Python class deriving from a C++ class!
|
||||
|
||||
Let's now make an instance of our Python class [^Derived]:
|
||||
|
||||
>>> derived = Derived()
|
||||
|
||||
Calling [^derived.f()]:
|
||||
|
||||
>>> derived.f()
|
||||
42
|
||||
|
||||
Will yield the expected result. Finally, calling calling the free function
|
||||
[^call_f] with [^derived] as argument:
|
||||
|
||||
>>> call_f(derived)
|
||||
42
|
||||
|
||||
Will also yield the expected result.
|
||||
|
||||
Here's what's happening:
|
||||
|
||||
# [^call_f(derived)] is called in Python
|
||||
# This corresponds to [^def("call_f", call_f);]. Boost.Python dispatches this call.
|
||||
# [^int call_f(Base& b) { return b.f(); }] accepts the call.
|
||||
# The overridden virtual function [^f] of [^BaseWrap] is called.
|
||||
# [^call_method<int>(self, "f");] dispatches the call back to Python.
|
||||
# [^def f(self): return 42] is finally called.
|
||||
|
||||
[endsect]
|
||||
[section Virtual Functions with Default Implementations]
|
||||
|
||||
Recall that in the [link python.class_virtual_functions previous section], we
|
||||
wrapped a class with a pure virtual function that we then implemented in
|
||||
C++ or Python classes derived from it. Our base class:
|
||||
We've seen in the previous section how classes with pure virtual functions are
|
||||
wrapped using Boost.Python's [@../../../v2//wrapper.html class wrapper]
|
||||
facilities. If we wish to wrap [*non]-pure-virtual functions instead, the
|
||||
mechanism is a bit different.
|
||||
|
||||
Recall that in the [link python.class_virtual_functions previous section], we
|
||||
wrapped a class with a pure virtual function that we then implemented in C++, or
|
||||
Python classes derived from it. Our base class:
|
||||
|
||||
struct Base
|
||||
{
|
||||
@@ -628,33 +556,42 @@ not declared as pure virtual:
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual ~Base() {}
|
||||
virtual int f() { return 0; }
|
||||
};
|
||||
|
||||
and instead had a default implementation that returns [^0], as shown above,
|
||||
we need to add a forwarding function that calls the [^Base] default virtual
|
||||
function [^f] implementation:
|
||||
|
||||
struct BaseWrap : Base
|
||||
We wrap it this way:
|
||||
|
||||
struct BaseWrap : Base, wrapper<Base>
|
||||
{
|
||||
BaseWrap(PyObject* self_)
|
||||
: self(self_) {}
|
||||
int f() { return call_method<int>(self, "f"); }
|
||||
int default_f() { return Base::f(); } // <<=== ***ADDED***
|
||||
PyObject* self;
|
||||
int f()
|
||||
{
|
||||
if (override f = this->get_override("f"))
|
||||
return f(); // *note*
|
||||
return Base::f();
|
||||
}
|
||||
|
||||
int default_f() { return this->Base::f(); }
|
||||
};
|
||||
|
||||
Notice how we implemented `BaseWrap::f`. Now, we have to check if there is an
|
||||
override for `f`. If none, then we call `Base::f()`.
|
||||
|
||||
Then, Boost.Python needs to keep track of 1) the dispatch function [^f] and
|
||||
2) the forwarding function to its default implementation [^default_f].
|
||||
There's a special [^def] function for this purpose. Here's how it is
|
||||
applied to our example above:
|
||||
[blurb __alert__ MSVC6/7 Workaround\n\n
|
||||
If you are using Microsoft Visual C++ 6 or 7, you have to rewrite the line
|
||||
with the `*note*` as:\n\n
|
||||
`return call<char const*>(f.ptr());`.]
|
||||
|
||||
class_<Base, BaseWrap, BaseWrap, boost::noncopyable>("Base")
|
||||
Finally, exposing:
|
||||
|
||||
class_<BaseWrap, boost::noncopyable>("Base")
|
||||
.def("f", &Base::f, &BaseWrap::default_f)
|
||||
;
|
||||
|
||||
Note that we are allowing [^Base] objects to be instantiated this time,
|
||||
unlike before where we specifically defined the [^class_<Base>] with
|
||||
[^no_init].
|
||||
Take note that we expose both `&Base::f` and `&BaseWrap::default_f`.
|
||||
Boost.Python needs to keep track of 1) the dispatch function [^f] and 2) the
|
||||
forwarding function to its default implementation [^default_f]. There's a
|
||||
special [^def] function for this purpose.
|
||||
|
||||
In Python, the results would be as expected:
|
||||
|
||||
@@ -675,16 +612,6 @@ Calling [^derived.f()]:
|
||||
>>> derived.f()
|
||||
42
|
||||
|
||||
Calling [^call_f], passing in a [^base] object:
|
||||
|
||||
>>> call_f(base)
|
||||
0
|
||||
|
||||
Calling [^call_f], passing in a [^derived] object:
|
||||
|
||||
>>> call_f(derived)
|
||||
42
|
||||
|
||||
[endsect]
|
||||
[section Class Operators/Special Functions]
|
||||
|
||||
@@ -1179,7 +1106,7 @@ bidirectional mapping between C++ and Python while maintaining the Python
|
||||
feel. Boost.Python C++ [^object]s are as close as possible to Python. This
|
||||
should minimize the learning curve significantly.
|
||||
|
||||
[$../images/python.png]
|
||||
[$images/python.png]
|
||||
|
||||
[section Basic Interface]
|
||||
|
||||
@@ -1422,7 +1349,7 @@ Boost.Python's static link library comes in two variants. Both are located
|
||||
in Boost's [^/libs/python/build/bin-stage] subdirectory. On Windows, the
|
||||
variants are called [^boost_python.lib] (for release builds) and
|
||||
[^boost_python_debug.lib] (for debugging). If you can't find the libraries,
|
||||
you probably haven't built Boost.Python yet. See
|
||||
you probably haven't built Boost.Python yet. See
|
||||
[@../../../building.html Building and Testing] on how to do this.
|
||||
|
||||
Python's static link library can be found in the [^/libs] subdirectory of
|
||||
@@ -1622,10 +1549,10 @@ perform.
|
||||
|
||||
[h2 Exception handling]
|
||||
|
||||
If an exception occurs in the execution of some Python code, the PyRun_String
|
||||
function returns a null pointer. Constructing a [^handle] out of this null
|
||||
pointer throws [@../../../v2/errors.html#error_already_set-spec error_already_set],
|
||||
so basically, the Python exception is automatically translated into a
|
||||
If an exception occurs in the execution of some Python code, the PyRun_String
|
||||
function returns a null pointer. Constructing a [^handle] out of this null
|
||||
pointer throws [@../../../v2/errors.html#error_already_set-spec error_already_set],
|
||||
so basically, the Python exception is automatically translated into a
|
||||
C++ exception when using [^handle]:
|
||||
|
||||
try
|
||||
@@ -1645,13 +1572,13 @@ C++ exception when using [^handle]:
|
||||
// handle the exception in some way
|
||||
}
|
||||
|
||||
The [^error_already_set] exception class doesn't carry any information in itself.
|
||||
To find out more about the Python exception that occurred, you need to use the
|
||||
[@http://www.python.org/doc/api/exceptionHandling.html exception handling functions]
|
||||
of the Python/C API in your catch-statement. This can be as simple as calling
|
||||
[@http://www.python.org/doc/api/exceptionHandling.html#l2h-70 PyErr_Print()] to
|
||||
print the exception's traceback to the console, or comparing the type of the
|
||||
exception with those of the [@http://www.python.org/doc/api/standardExceptions.html
|
||||
The [^error_already_set] exception class doesn't carry any information in itself.
|
||||
To find out more about the Python exception that occurred, you need to use the
|
||||
[@http://www.python.org/doc/api/exceptionHandling.html exception handling functions]
|
||||
of the Python/C API in your catch-statement. This can be as simple as calling
|
||||
[@http://www.python.org/doc/api/exceptionHandling.html#l2h-70 PyErr_Print()] to
|
||||
print the exception's traceback to the console, or comparing the type of the
|
||||
exception with those of the [@http://www.python.org/doc/api/standardExceptions.html
|
||||
standard exceptions]:
|
||||
|
||||
catch(error_already_set)
|
||||
@@ -1667,11 +1594,11 @@ standard exceptions]:
|
||||
}
|
||||
}
|
||||
|
||||
(To retrieve even more information from the exception you can use some of the other
|
||||
(To retrieve even more information from the exception you can use some of the other
|
||||
exception handling functions listed [@http://www.python.org/doc/api/exceptionHandling.html here].)
|
||||
|
||||
If you'd rather not have [^handle] throw a C++ exception when it is constructed, you
|
||||
can use the [@../../../v2/handle.html#allow_null-spec allow_null] function in the same
|
||||
If you'd rather not have [^handle] throw a C++ exception when it is constructed, you
|
||||
can use the [@../../../v2/handle.html#allow_null-spec allow_null] function in the same
|
||||
way you'd use borrowed:
|
||||
|
||||
handle<> result((allow_null(PyRun_String(
|
||||
@@ -1859,7 +1786,7 @@ actually a Python package. It can be a empty file, but can also perform some
|
||||
magic, that will be shown later.
|
||||
|
||||
Now our package is ready. All the user has to do is put [^sounds] into his
|
||||
[@http://www.python.org/doc/current/tut/node8.html#SECTION008110000000000000000 PYTHONPATH]
|
||||
[@http://www.python.org/doc/current/tut/node8.html#SECTION008110000000000000000 PYTHONPATH]
|
||||
and fire up the interpreter:
|
||||
|
||||
>>> import sounds.io
|
||||
@@ -1980,7 +1907,7 @@ we have a class [^point] in C++:
|
||||
}
|
||||
|
||||
If we are using the technique from the previous session,
|
||||
[link python.creating_packages Creating Packages], we can code directly
|
||||
[link python.creating_packages Creating Packages], we can code directly
|
||||
into [^geom/__init__.py]:
|
||||
|
||||
from _geom import *
|
||||
|
||||
@@ -162,10 +162,10 @@ minimalist <emphasis>bjam</emphasis> script that builds the DLLs for us.</para>
|
||||
Before anything else, you should have the bjam executable in your boost
|
||||
directory or somewhere in your path such that <literal>bjam</literal> can be executed in
|
||||
the command line. Pre-built Boost.Jam executables are available for most
|
||||
platforms. The complete list of Bjam executables can be found
|
||||
platforms. The complete list of Bjam executables can be found
|
||||
<ulink url="http://sourceforge.net/project/showfiles.php?group_id=7586">here</ulink>.</para>
|
||||
<anchor id="hello.let_s_jam_" /><bridgehead renderas="sect2">Let's Jam!</bridgehead><para>
|
||||
<inlinemediaobject><imageobject><imagedata fileref="images/jam.png"></imagedata></imageobject></inlinemediaobject></para>
|
||||
<inlinemediaobject><imageobject><imagedata fileref="../images/jam.png"></imagedata></imageobject></inlinemediaobject></para>
|
||||
<para>
|
||||
Here is our minimalist Jamfile:</para>
|
||||
<programlisting><literal> subproject libs/python/example/tutorial ;
|
||||
@@ -563,9 +563,12 @@ Now we can inform Boost.Python of the inheritance relationship between
|
||||
Doing so, we get some things for free:</para>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
Derived automatically inherits all of Base's Python methods (wrapped C++ member functions)
|
||||
Derived automatically inherits all of Base's Python methods
|
||||
(wrapped C++ member functions)
|
||||
</listitem><listitem>
|
||||
<emphasis role="bold">If</emphasis> Base is polymorphic, <literal>Derived</literal> objects which have been passed to Python via a pointer or reference to <literal>Base</literal> can be passed where a pointer or reference to <literal>Derived</literal> is expected.
|
||||
<emphasis role="bold">If</emphasis> Base is polymorphic, <literal>Derived</literal> objects which have been passed to
|
||||
Python via a pointer or reference to <literal>Base</literal> can be passed where a pointer
|
||||
or reference to <literal>Derived</literal> is expected.
|
||||
</listitem>
|
||||
</orderedlist><para>
|
||||
Now, we shall expose the C++ free functions <literal>b</literal> and <literal>d</literal> and <literal>factory</literal>:</para>
|
||||
@@ -594,53 +597,75 @@ Boost.Python <link linkend="python.call_policies">call policies</link> later.</p
|
||||
<section id="python.class_virtual_functions">
|
||||
<title>Class Virtual Functions</title>
|
||||
<para>
|
||||
In this section, we shall learn how to make functions behave
|
||||
polymorphically through virtual functions. Continuing our example, let us
|
||||
add a virtual function to our <literal>Base</literal> class:</para>
|
||||
In this section, we shall learn how to make functions behave polymorphically
|
||||
through virtual functions. Continuing our example, let us add a virtual function
|
||||
to our <literal>Base</literal> class:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="keyword">struct</phrase><phrase role="identifier"> Base</phrase><phrase role="special">
|
||||
{</phrase><phrase role="keyword">
|
||||
virtual</phrase><phrase role="special"> ~</phrase><phrase role="identifier">Base</phrase><phrase role="special">()</phrase><phrase role="special"> {}</phrase><phrase role="keyword">
|
||||
virtual</phrase><phrase role="keyword"> int</phrase><phrase role="identifier"> f</phrase><phrase role="special">()</phrase><phrase role="special"> =</phrase><phrase role="number"> 0</phrase><phrase role="special">;</phrase><phrase role="special">
|
||||
};</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Since <literal>f</literal> is a pure virtual function, <literal>Base</literal> is now an abstract
|
||||
class. Given an instance of our class, the free function <literal>call_f</literal>
|
||||
calls some implementation of this virtual function in a concrete
|
||||
derived class:</para>
|
||||
One of the goals of Boost.Python is to be minimally intrusive on an existing C++
|
||||
design. In principle, it should be possible to expose the interface for a 3rd
|
||||
party library without changing it. It is not ideal to add anything to our class
|
||||
<code><phrase role="identifier">Base</phrase></code>. Yet, when you have a virtual function that's going to be overridden in
|
||||
Python and called polymorphically <emphasis role="bold">from C++</emphasis>, we'll need to add some
|
||||
scaffoldings to make things work properly. What we'll do is write a class
|
||||
wrapper that derives from <code><phrase role="identifier">Base</phrase></code> that will unintrusively hook into the virtual
|
||||
functions so that a Python override may be called:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="keyword">int</phrase><phrase role="identifier"> call_f</phrase><phrase role="special">(</phrase><phrase role="identifier">Base</phrase><phrase role="special">&</phrase><phrase role="identifier"> b</phrase><phrase role="special">)</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="identifier"> b</phrase><phrase role="special">.</phrase><phrase role="identifier">f</phrase><phrase role="special">();</phrase><phrase role="special"> }</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
To allow this function to be implemented in a Python derived class, we
|
||||
need to create a class wrapper:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="keyword">struct</phrase><phrase role="identifier"> BaseWrap</phrase><phrase role="special"> :</phrase><phrase role="identifier"> Base</phrase><phrase role="special">
|
||||
{</phrase><phrase role="identifier">
|
||||
BaseWrap</phrase><phrase role="special">(</phrase><phrase role="identifier">PyObject</phrase><phrase role="special">*</phrase><phrase role="identifier"> self_</phrase><phrase role="special">)</phrase><phrase role="special">
|
||||
:</phrase><phrase role="identifier"> self</phrase><phrase role="special">(</phrase><phrase role="identifier">self_</phrase><phrase role="special">)</phrase><phrase role="special"> {}</phrase><phrase role="keyword">
|
||||
int</phrase><phrase role="identifier"> f</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="identifier"> call_method</phrase><phrase role="special"><</phrase><phrase role="keyword">int</phrase><phrase role="special">>(</phrase><phrase role="identifier">self</phrase><phrase role="special">,</phrase><phrase role="string"> "f"</phrase><phrase role="special">);</phrase><phrase role="special"> }</phrase><phrase role="identifier">
|
||||
PyObject</phrase><phrase role="special">*</phrase><phrase role="identifier"> self</phrase><phrase role="special">;</phrase><phrase role="special">
|
||||
};</phrase><phrase role="keyword">
|
||||
|
||||
|
||||
struct</phrase><phrase role="identifier"> BaseWrap</phrase><phrase role="special"> :</phrase><phrase role="identifier"> Base</phrase><phrase role="special">
|
||||
{</phrase><phrase role="identifier">
|
||||
BaseWrap</phrase><phrase role="special">(</phrase><phrase role="identifier">PyObject</phrase><phrase role="special">*</phrase><phrase role="identifier"> self_</phrase><phrase role="special">)</phrase><phrase role="special">
|
||||
:</phrase><phrase role="identifier"> self</phrase><phrase role="special">(</phrase><phrase role="identifier">self_</phrase><phrase role="special">)</phrase><phrase role="special"> {}</phrase><phrase role="identifier">
|
||||
BaseWrap</phrase><phrase role="special">(</phrase><phrase role="identifier">PyObject</phrase><phrase role="special">*</phrase><phrase role="identifier"> self_</phrase><phrase role="special">,</phrase><phrase role="identifier"> Base</phrase><phrase role="keyword"> const</phrase><phrase role="special">&</phrase><phrase role="identifier"> copy</phrase><phrase role="special">)</phrase><phrase role="special">
|
||||
:</phrase><phrase role="identifier"> Base</phrase><phrase role="special">(</phrase><phrase role="identifier">copy</phrase><phrase role="special">),</phrase><phrase role="identifier"> self</phrase><phrase role="special">(</phrase><phrase role="identifier">self_</phrase><phrase role="special">)</phrase><phrase role="special"> {}</phrase><phrase role="keyword">
|
||||
int</phrase><phrase role="identifier"> f</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="identifier"> call_method</phrase><phrase role="special"><</phrase><phrase role="keyword">int</phrase><phrase role="special">>(</phrase><phrase role="identifier">self</phrase><phrase role="special">,</phrase><phrase role="string"> "f"</phrase><phrase role="special">);</phrase><phrase role="special"> }</phrase><phrase role="keyword">
|
||||
int</phrase><phrase role="identifier"> default_f</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="identifier"> Base</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">();</phrase><phrase role="special"> }</phrase><phrase role="comment"> // <<=== ***ADDED***
|
||||
</phrase><phrase role="identifier"> PyObject</phrase><phrase role="special">*</phrase><phrase role="identifier"> self</phrase><phrase role="special">;</phrase><phrase role="special">
|
||||
<phrase role="keyword">struct</phrase><phrase role="identifier"> BaseWrap</phrase><phrase role="special"> :</phrase><phrase role="identifier"> Base</phrase><phrase role="special">,</phrase><phrase role="identifier"> wrapper</phrase><phrase role="special"><</phrase><phrase role="identifier">Base</phrase><phrase role="special">></phrase><phrase role="special">
|
||||
{</phrase><phrase role="keyword">
|
||||
int</phrase><phrase role="identifier"> f</phrase><phrase role="special">()</phrase><phrase role="special">
|
||||
{</phrase><phrase role="keyword">
|
||||
return</phrase><phrase role="keyword"> this</phrase><phrase role="special">-></phrase><phrase role="identifier">get_override</phrase><phrase role="special">(</phrase><phrase role="string">"f"</phrase><phrase role="special">)();</phrase><phrase role="special">
|
||||
}</phrase><phrase role="special">
|
||||
};</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Notice too that in addition to inheriting from <code><phrase role="identifier">Base</phrase></code>, we also multiply-
|
||||
inherited <code><phrase role="identifier">wrapper</phrase><phrase role="special"><</phrase><phrase role="identifier">Base</phrase><phrase role="special">></phrase></code> (See <ulink url="../../../v2//wrapper.html">Wrapper</ulink>). The
|
||||
<code><phrase role="identifier">wrapper</phrase></code> template makes the job of wrapping classes that are meant to
|
||||
overridden in Python, easier.</para>
|
||||
<informaltable frame="all">
|
||||
<?dbhtml table-width="74%" ?>
|
||||
<tgroup cols="1">
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<inlinemediaobject><imageobject><imagedata fileref="images/alert.png"></imagedata></imageobject></inlinemediaobject> MSVC6/7 Workaround<para/>
|
||||
<para/>
|
||||
|
||||
If you are using Microsoft Visual C++ 6 or 7, you have to write <code><phrase role="identifier">f</phrase></code> as:<para/>
|
||||
<para/>
|
||||
|
||||
<code><phrase role="keyword">return</phrase><phrase role="identifier"> call</phrase><phrase role="special"><</phrase><phrase role="keyword">int</phrase><phrase role="special">>(</phrase><phrase role="keyword">this</phrase><phrase role="special">-></phrase><phrase role="identifier">get_override</phrase><phrase role="special">(</phrase><phrase role="string">"f"</phrase><phrase role="special">).</phrase><phrase role="identifier">ptr</phrase><phrase role="special">());</phrase></code>.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
<para>
|
||||
BaseWrap's overridden virtual member function <code><phrase role="identifier">f</phrase></code> in effect calls the
|
||||
corresponding method of the Python object through <code><phrase role="identifier">get_override</phrase></code>.</para>
|
||||
<para>
|
||||
Finally, exposing <code><phrase role="identifier">Base</phrase></code>:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">BaseWrap</phrase><phrase role="special">,</phrase><phrase role="identifier"> boost</phrase><phrase role="special">::</phrase><phrase role="identifier">noncopyable</phrase><phrase role="special">>(</phrase><phrase role="string">"Base"</phrase><phrase role="special">)</phrase><phrase role="special">
|
||||
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"f"</phrase><phrase role="special">,</phrase><phrase role="identifier"> pure_virtual</phrase><phrase role="special">(&</phrase><phrase role="identifier">Base</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">))</phrase><phrase role="special">
|
||||
;</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
<code><phrase role="identifier">pure_virtual</phrase></code> signals Boost.Python that the function <code><phrase role="identifier">f</phrase></code> is a pure virtual
|
||||
function.</para>
|
||||
<informaltable frame="all">
|
||||
<?dbhtml table-width="74%" ?>
|
||||
<tgroup cols="1">
|
||||
@@ -656,147 +681,18 @@ correspond roughly to C++'s <emphasis role="bold">member functions</emphasis></e
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
<para>
|
||||
Our class wrapper <literal>BaseWrap</literal> is derived from <literal>Base</literal>. Its overridden
|
||||
virtual member function <literal>f</literal> in effect calls the corresponding method
|
||||
of the Python object <literal>self</literal>, which is a pointer back to the Python
|
||||
<literal>Base</literal> object holding our <literal>BaseWrap</literal> instance.</para>
|
||||
<informaltable frame="all">
|
||||
<?dbhtml table-width="74%" ?>
|
||||
<tgroup cols="1">
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<inlinemediaobject><imageobject><imagedata fileref="images/note.png"></imagedata></imageobject></inlinemediaobject> <emphasis role="bold">Why do we need BaseWrap?</emphasis><para/>
|
||||
<para/>
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
<para>
|
||||
<emphasis>You may ask</emphasis>, "Why do we need the <literal>BaseWrap</literal> derived class? This could
|
||||
have been designed so that everything gets done right inside of
|
||||
Base."<para/>
|
||||
<para/>
|
||||
</para>
|
||||
<para>
|
||||
One of the goals of Boost.Python is to be minimally intrusive on an
|
||||
existing C++ design. In principle, it should be possible to expose the
|
||||
interface for a 3rd party library without changing it. To unintrusively
|
||||
hook into the virtual functions so that a Python override may be called, we
|
||||
must use a derived class.<para/>
|
||||
<para/>
|
||||
</para>
|
||||
<para>
|
||||
Note however that you don't need to do this to get methods overridden
|
||||
in Python to behave virtually when called <emphasis>from</emphasis> <emphasis role="bold">Python</emphasis>. The only
|
||||
time you need to do the <literal>BaseWrap</literal> dance is when you have a virtual
|
||||
function that's going to be overridden in Python and called
|
||||
polymorphically <emphasis>from</emphasis> <emphasis role="bold">C++</emphasis>.]</para>
|
||||
<para>
|
||||
Wrapping <literal>Base</literal> and the free function <literal>call_f</literal>:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">Base</phrase><phrase role="special">,</phrase><phrase role="identifier"> BaseWrap</phrase><phrase role="special">,</phrase><phrase role="identifier"> boost</phrase><phrase role="special">::</phrase><phrase role="identifier">noncopyable</phrase><phrase role="special">>(</phrase><phrase role="string">"Base"</phrase><phrase role="special">,</phrase><phrase role="identifier"> no_init</phrase><phrase role="special">)</phrase><phrase role="special">
|
||||
;</phrase><phrase role="identifier">
|
||||
def</phrase><phrase role="special">(</phrase><phrase role="string">"call_f"</phrase><phrase role="special">,</phrase><phrase role="identifier"> call_f</phrase><phrase role="special">);</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Notice that we parameterized the <literal>class_</literal> template with <literal>BaseWrap</literal> as the
|
||||
second parameter. What is <literal>noncopyable</literal>? Without it, the library will try
|
||||
to create code for converting Base return values of wrapped functions to
|
||||
Python. To do that, it needs Base's copy constructor... which isn't
|
||||
available, since Base is an abstract class.</para>
|
||||
<para>
|
||||
In Python, let us try to instantiate our <literal>Base</literal> class:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="special">>>></phrase><phrase role="identifier"> base</phrase><phrase role="special"> =</phrase><phrase role="identifier"> Base</phrase><phrase role="special">()</phrase><phrase role="identifier">
|
||||
RuntimeError</phrase><phrase role="special">:</phrase><phrase role="identifier"> This</phrase><phrase role="keyword"> class</phrase><phrase role="identifier"> cannot</phrase><phrase role="identifier"> be</phrase><phrase role="identifier"> instantiated</phrase><phrase role="identifier"> from</phrase><phrase role="identifier"> Python</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Why is it an error? <literal>Base</literal> is an abstract class. As such it is advisable
|
||||
to define the Python wrapper with <literal>no_init</literal> as we have done above. Doing
|
||||
so will disallow abstract base classes such as <literal>Base</literal> to be instantiated.</para>
|
||||
</section>
|
||||
<section id="python.deriving_a_python_class">
|
||||
<title>Deriving a Python Class</title>
|
||||
<para>
|
||||
Continuing, we can derive from our base class Base in Python and override
|
||||
the virtual function in Python. Before we can do that, we have to set up
|
||||
our <literal>class_</literal> wrapper as:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">Base</phrase><phrase role="special">,</phrase><phrase role="identifier"> BaseWrap</phrase><phrase role="special">,</phrase><phrase role="identifier"> boost</phrase><phrase role="special">::</phrase><phrase role="identifier">noncopyable</phrase><phrase role="special">>(</phrase><phrase role="string">"Base"</phrase><phrase role="special">)</phrase><phrase role="special">
|
||||
;</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Otherwise, we have to suppress the Base class' <literal>no_init</literal> by adding an
|
||||
<literal><emphasis role="underline">_init</emphasis>_()</literal> method to all our derived classes. <literal>no_init</literal> actually adds
|
||||
an <literal><emphasis role="underline">_init</emphasis>_</literal> method that raises a Python RuntimeError exception.</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="special">>>></phrase><phrase role="keyword"> class</phrase><phrase role="identifier"> Derived</phrase><phrase role="special">(</phrase><phrase role="identifier">Base</phrase><phrase role="special">):</phrase><phrase role="special">
|
||||
...</phrase><phrase role="identifier"> def</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special">):</phrase><phrase role="special">
|
||||
...</phrase><phrase role="keyword"> return</phrase><phrase role="number"> 42</phrase><phrase role="special">
|
||||
...</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Cool eh? A Python class deriving from a C++ class!</para>
|
||||
<para>
|
||||
Let's now make an instance of our Python class <literal>Derived</literal>:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="special">>>></phrase><phrase role="identifier"> derived</phrase><phrase role="special"> =</phrase><phrase role="identifier"> Derived</phrase><phrase role="special">()</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Calling <literal>derived.f()</literal>:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="special">>>></phrase><phrase role="identifier"> derived</phrase><phrase role="special">.</phrase><phrase role="identifier">f</phrase><phrase role="special">()</phrase><phrase role="number">
|
||||
42</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Will yield the expected result. Finally, calling calling the free function
|
||||
<literal>call_f</literal> with <literal>derived</literal> as argument:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="special">>>></phrase><phrase role="identifier"> call_f</phrase><phrase role="special">(</phrase><phrase role="identifier">derived</phrase><phrase role="special">)</phrase><phrase role="number">
|
||||
42</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Will also yield the expected result.</para>
|
||||
<para>
|
||||
Here's what's happening:</para>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<literal>call_f(derived)</literal> is called in Python
|
||||
</listitem><listitem>
|
||||
This corresponds to <literal>def("call_f", call_f);</literal>. Boost.Python dispatches this call.
|
||||
</listitem><listitem>
|
||||
<literal>int call_f(Base& b) { return b.f(); }</literal> accepts the call.
|
||||
</listitem><listitem>
|
||||
The overridden virtual function <literal>f</literal> of <literal>BaseWrap</literal> is called.
|
||||
</listitem><listitem>
|
||||
<literal>call_method<int>(self, "f");</literal> dispatches the call back to Python.
|
||||
</listitem><listitem>
|
||||
<literal>def f(self): return 42</literal> is finally called.
|
||||
</listitem>
|
||||
</orderedlist></section>
|
||||
<section id="python.virtual_functions_with_default_implementations">
|
||||
<title>Virtual Functions with Default Implementations</title>
|
||||
<para>
|
||||
Recall that in the <link linkend="python.class_virtual_functions">previous section</link>, we
|
||||
wrapped a class with a pure virtual function that we then implemented in
|
||||
C++ or Python classes derived from it. Our base class:</para>
|
||||
We've seen in the previous section how classes with pure virtual functions are
|
||||
wrapped using Boost.Python's <ulink url="../../../v2//wrapper.html">class wrapper</ulink>
|
||||
facilities. If we wish to wrap <emphasis role="bold">non</emphasis>-pure-virtual functions instead, the
|
||||
mechanism is a bit different.</para>
|
||||
<para>
|
||||
Recall that in the <link linkend="python.class_virtual_functions">previous section</link>, we
|
||||
wrapped a class with a pure virtual function that we then implemented in C++, or
|
||||
Python classes derived from it. Our base class:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="keyword">struct</phrase><phrase role="identifier"> Base</phrase><phrase role="special">
|
||||
@@ -812,41 +708,63 @@ not declared as pure virtual:</para>
|
||||
<literal>
|
||||
<phrase role="keyword">struct</phrase><phrase role="identifier"> Base</phrase><phrase role="special">
|
||||
{</phrase><phrase role="keyword">
|
||||
virtual</phrase><phrase role="special"> ~</phrase><phrase role="identifier">Base</phrase><phrase role="special">()</phrase><phrase role="special"> {}</phrase><phrase role="keyword">
|
||||
virtual</phrase><phrase role="keyword"> int</phrase><phrase role="identifier"> f</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="number"> 0</phrase><phrase role="special">;</phrase><phrase role="special"> }</phrase><phrase role="special">
|
||||
};</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
and instead had a default implementation that returns <literal>0</literal>, as shown above,
|
||||
we need to add a forwarding function that calls the <literal>Base</literal> default virtual
|
||||
function <literal>f</literal> implementation:</para>
|
||||
We wrap it this way:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="keyword">struct</phrase><phrase role="identifier"> BaseWrap</phrase><phrase role="special"> :</phrase><phrase role="identifier"> Base</phrase><phrase role="special">
|
||||
{</phrase><phrase role="identifier">
|
||||
BaseWrap</phrase><phrase role="special">(</phrase><phrase role="identifier">PyObject</phrase><phrase role="special">*</phrase><phrase role="identifier"> self_</phrase><phrase role="special">)</phrase><phrase role="special">
|
||||
:</phrase><phrase role="identifier"> self</phrase><phrase role="special">(</phrase><phrase role="identifier">self_</phrase><phrase role="special">)</phrase><phrase role="special"> {}</phrase><phrase role="keyword">
|
||||
int</phrase><phrase role="identifier"> f</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="identifier"> call_method</phrase><phrase role="special"><</phrase><phrase role="keyword">int</phrase><phrase role="special">>(</phrase><phrase role="identifier">self</phrase><phrase role="special">,</phrase><phrase role="string"> "f"</phrase><phrase role="special">);</phrase><phrase role="special"> }</phrase><phrase role="keyword">
|
||||
int</phrase><phrase role="identifier"> default_f</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="identifier"> Base</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">();</phrase><phrase role="special"> }</phrase><phrase role="comment"> // <<=== ***ADDED***
|
||||
</phrase><phrase role="identifier"> PyObject</phrase><phrase role="special">*</phrase><phrase role="identifier"> self</phrase><phrase role="special">;</phrase><phrase role="special">
|
||||
<phrase role="keyword">struct</phrase><phrase role="identifier"> BaseWrap</phrase><phrase role="special"> :</phrase><phrase role="identifier"> Base</phrase><phrase role="special">,</phrase><phrase role="identifier"> wrapper</phrase><phrase role="special"><</phrase><phrase role="identifier">Base</phrase><phrase role="special">></phrase><phrase role="special">
|
||||
{</phrase><phrase role="keyword">
|
||||
int</phrase><phrase role="identifier"> f</phrase><phrase role="special">()</phrase><phrase role="special">
|
||||
{</phrase><phrase role="keyword">
|
||||
if</phrase><phrase role="special"> (</phrase><phrase role="identifier">override</phrase><phrase role="identifier"> f</phrase><phrase role="special"> =</phrase><phrase role="keyword"> this</phrase><phrase role="special">-></phrase><phrase role="identifier">get_override</phrase><phrase role="special">(</phrase><phrase role="string">"f"</phrase><phrase role="special">))</phrase><phrase role="keyword">
|
||||
return</phrase><phrase role="identifier"> f</phrase><phrase role="special">();</phrase><phrase role="comment"> // *note*
|
||||
</phrase><phrase role="keyword"> return</phrase><phrase role="identifier"> Base</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">();</phrase><phrase role="special">
|
||||
}</phrase><phrase role="keyword">
|
||||
|
||||
int</phrase><phrase role="identifier"> default_f</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="keyword"> this</phrase><phrase role="special">-></phrase><phrase role="identifier">Base</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">();</phrase><phrase role="special"> }</phrase><phrase role="special">
|
||||
};</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Then, Boost.Python needs to keep track of 1) the dispatch function <literal>f</literal> and
|
||||
2) the forwarding function to its default implementation <literal>default_f</literal>.
|
||||
There's a special <literal>def</literal> function for this purpose. Here's how it is
|
||||
applied to our example above:</para>
|
||||
Notice how we implemented <code><phrase role="identifier">BaseWrap</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase></code>. Now, we have to check if there is an
|
||||
override for <code><phrase role="identifier">f</phrase></code>. If none, then we call <code><phrase role="identifier">Base</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">()</phrase></code>.</para>
|
||||
<informaltable frame="all">
|
||||
<?dbhtml table-width="74%" ?>
|
||||
<tgroup cols="1">
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<inlinemediaobject><imageobject><imagedata fileref="images/alert.png"></imagedata></imageobject></inlinemediaobject> MSVC6/7 Workaround<para/>
|
||||
<para/>
|
||||
|
||||
If you are using Microsoft Visual C++ 6 or 7, you have to rewrite the line
|
||||
with the <code><phrase role="special">*</phrase><phrase role="identifier">note</phrase><phrase role="special">*</phrase></code> as:<para/>
|
||||
<para/>
|
||||
|
||||
<code><phrase role="keyword">return</phrase><phrase role="identifier"> call</phrase><phrase role="special"><</phrase><phrase role="keyword">char</phrase><phrase role="keyword"> const</phrase><phrase role="special">*>(</phrase><phrase role="identifier">f</phrase><phrase role="special">.</phrase><phrase role="identifier">ptr</phrase><phrase role="special">());</phrase></code>.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
<para>
|
||||
Finally, exposing:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">Base</phrase><phrase role="special">,</phrase><phrase role="identifier"> BaseWrap</phrase><phrase role="special">,</phrase><phrase role="identifier"> BaseWrap</phrase><phrase role="special">,</phrase><phrase role="identifier"> boost</phrase><phrase role="special">::</phrase><phrase role="identifier">noncopyable</phrase><phrase role="special">>(</phrase><phrase role="string">"Base"</phrase><phrase role="special">)</phrase><phrase role="special">
|
||||
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"f"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">Base</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">BaseWrap</phrase><phrase role="special">::</phrase><phrase role="identifier">default_f</phrase><phrase role="special">)</phrase>
|
||||
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">BaseWrap</phrase><phrase role="special">,</phrase><phrase role="identifier"> boost</phrase><phrase role="special">::</phrase><phrase role="identifier">noncopyable</phrase><phrase role="special">>(</phrase><phrase role="string">"Base"</phrase><phrase role="special">)</phrase><phrase role="special">
|
||||
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"f"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">Base</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">BaseWrap</phrase><phrase role="special">::</phrase><phrase role="identifier">default_f</phrase><phrase role="special">)</phrase><phrase role="special">
|
||||
;</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Note that we are allowing <literal>Base</literal> objects to be instantiated this time,
|
||||
unlike before where we specifically defined the <literal>class_<Base></literal> with
|
||||
<literal>no_init</literal>.</para>
|
||||
Take note that we expose both <code><phrase role="special">&</phrase><phrase role="identifier">Base</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase></code> and <code><phrase role="special">&</phrase><phrase role="identifier">BaseWrap</phrase><phrase role="special">::</phrase><phrase role="identifier">default_f</phrase></code>.
|
||||
Boost.Python needs to keep track of 1) the dispatch function <literal>f</literal> and 2) the
|
||||
forwarding function to its default implementation <literal>default_f</literal>. There's a
|
||||
special <literal>def</literal> function for this purpose.</para>
|
||||
<para>
|
||||
In Python, the results would be as expected:</para>
|
||||
<programlisting>
|
||||
@@ -875,22 +793,6 @@ Calling <literal>derived.f()</literal>:</para>
|
||||
42</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Calling <literal>call_f</literal>, passing in a <literal>base</literal> object:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="special">>>></phrase><phrase role="identifier"> call_f</phrase><phrase role="special">(</phrase><phrase role="identifier">base</phrase><phrase role="special">)</phrase><phrase role="number">
|
||||
0</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
Calling <literal>call_f</literal>, passing in a <literal>derived</literal> object:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="special">>>></phrase><phrase role="identifier"> call_f</phrase><phrase role="special">(</phrase><phrase role="identifier">derived</phrase><phrase role="special">)</phrase><phrase role="number">
|
||||
42</phrase>
|
||||
</literal>
|
||||
</programlisting>
|
||||
</section>
|
||||
<section id="python.class_operators_special_functions">
|
||||
<title>Class Operators/Special Functions</title>
|
||||
@@ -1823,7 +1725,7 @@ Boost.Python's static link library comes in two variants. Both are located
|
||||
in Boost's <literal>/libs/python/build/bin-stage</literal> subdirectory. On Windows, the
|
||||
variants are called <literal>boost_python.lib</literal> (for release builds) and
|
||||
<literal>boost_python_debug.lib</literal> (for debugging). If you can't find the libraries,
|
||||
you probably haven't built Boost.Python yet. See
|
||||
you probably haven't built Boost.Python yet. See
|
||||
<ulink url="../../../building.html">Building and Testing</ulink> on how to do this.</para>
|
||||
<para>
|
||||
Python's static link library can be found in the <literal>/libs</literal> subdirectory of
|
||||
@@ -2001,7 +1903,7 @@ you want to be a Dr. Frankenstein, always wrap <literal>PyObject*</literal>s in
|
||||
It's nice that <literal>handle</literal> manages the reference counting details for us, but
|
||||
other than that it doesn't do much. Often we'd like to have a more useful
|
||||
class to manipulate Python objects. But we have already seen such a class
|
||||
above, and in the <link linkend="python.object">previous section</link>: the aptly
|
||||
above, and in the <ulink url="object.html">previous section</ulink>: the aptly
|
||||
named <literal>object</literal> class and it's derivatives. We've already seen that they
|
||||
can be constructed from a <literal>handle</literal>. The following examples should further
|
||||
illustrate this fact:</para>
|
||||
@@ -2047,10 +1949,10 @@ int</phrase><phrase role="identifier"> five_squared</phrase><phrase role="specia
|
||||
take into account the different functions that <literal>object</literal> and <literal>handle</literal>
|
||||
perform.</para>
|
||||
<anchor id="using_the_interpreter.exception_handling" /><bridgehead renderas="sect2">Exception handling</bridgehead><para>
|
||||
If an exception occurs in the execution of some Python code, the <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink>
|
||||
function returns a null pointer. Constructing a <literal>handle</literal> out of this null
|
||||
pointer throws <ulink url="../../../v2/errors.html#error_already_set-spec">error_already_set</ulink>,
|
||||
so basically, the Python exception is automatically translated into a
|
||||
If an exception occurs in the execution of some Python code, the <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink>
|
||||
function returns a null pointer. Constructing a <literal>handle</literal> out of this null
|
||||
pointer throws <ulink url="../../../v2/errors.html#error_already_set-spec">error_already_set</ulink>,
|
||||
so basically, the Python exception is automatically translated into a
|
||||
C++ exception when using <literal>handle</literal>:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
@@ -2073,14 +1975,14 @@ catch</phrase><phrase role="special">(</phrase><phrase role="identifier">error_a
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
The <literal>error_already_set</literal> exception class doesn't carry any information in itself.
|
||||
To find out more about the Python exception that occurred, you need to use the
|
||||
<ulink url="http://www.python.org/doc/api/exceptionHandling.html">exception handling functions</ulink>
|
||||
of the Python/C API in your catch-statement. This can be as simple as calling
|
||||
<ulink url="http://www.python.org/doc/api/exceptionHandling.html#l2h-70">PyErr_Print()</ulink> to
|
||||
print the exception's traceback to the console, or comparing the type of the
|
||||
exception with those of the <ulink url="http://www.python.org/doc/api/standardExceptions.html">
|
||||
standard exceptions</ulink>:</para>
|
||||
The <literal>error_already_set</literal> exception class doesn't carry any information in itself.
|
||||
To find out more about the Python exception that occurred, you need to use the
|
||||
<ulink url="http://www.python.org/doc/api/exceptionHandling.html">exception handling functions</ulink>
|
||||
of the Python/C API in your catch-statement. This can be as simple as calling
|
||||
<ulink url="http://www.python.org/doc/api/exceptionHandling.html#l2h-70">PyErr_Print()</ulink> to
|
||||
print the exception's traceback to the console, or comparing the type of the
|
||||
exception with those of the <ulink url="http://www.python.org/doc/api/standardExceptions.html
|
||||
standard">exceptions</ulink>:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
<phrase role="keyword">catch</phrase><phrase role="special">(</phrase><phrase role="identifier">error_already_set</phrase><phrase role="special">)</phrase><phrase role="special">
|
||||
@@ -2098,11 +2000,11 @@ standard exceptions</ulink>:</para>
|
||||
</literal>
|
||||
</programlisting>
|
||||
<para>
|
||||
(To retrieve even more information from the exception you can use some of the other
|
||||
(To retrieve even more information from the exception you can use some of the other
|
||||
exception handling functions listed <ulink url="http://www.python.org/doc/api/exceptionHandling.html">here</ulink>.)</para>
|
||||
<para>
|
||||
If you'd rather not have <literal>handle</literal> throw a C++ exception when it is constructed, you
|
||||
can use the <ulink url="../../../v2/handle.html#allow_null-spec">allow_null</ulink> function in the same
|
||||
If you'd rather not have <literal>handle</literal> throw a C++ exception when it is constructed, you
|
||||
can use the <ulink url="../../../v2/handle.html#allow_null-spec">allow_null</ulink> function in the same
|
||||
way you'd use borrowed:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
@@ -2345,7 +2247,7 @@ actually a Python package. It can be a empty file, but can also perform some
|
||||
magic, that will be shown later.</para>
|
||||
<para>
|
||||
Now our package is ready. All the user has to do is put <literal>sounds</literal> into his
|
||||
<ulink url="http://www.python.org/doc/current/tut/node8.html#SECTION008110000000000000000">PYTHONPATH</ulink>
|
||||
<ulink url="http://www.python.org/doc/current/tut/node8.html#SECTION008110000000000000000">PYTHONPATH</ulink>
|
||||
and fire up the interpreter:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
@@ -2493,7 +2395,7 @@ BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="ident
|
||||
</programlisting>
|
||||
<para>
|
||||
If we are using the technique from the previous session,
|
||||
<link linkend="python.creating_packages">Creating Packages</link>, we can code directly
|
||||
<link linkend="python.creating_packages">Creating Packages</link>, we can code directly
|
||||
into <literal>geom/<emphasis role="underline">_init</emphasis>_.py</literal>:</para>
|
||||
<programlisting>
|
||||
<literal>
|
||||
|
||||
Reference in New Issue
Block a user