mirror of
https://github.com/boostorg/python.git
synced 2026-01-21 17:12:22 +00:00
209 lines
8.0 KiB
HTML
209 lines
8.0 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||
|
||
<title>Overridable Virtual Functions</title>
|
||
|
||
<img src="../../../c++boost.gif" alt="c++boost.gif (8819 bytes)" align="center"
|
||
width="277" height="86">
|
||
|
||
<h1>Overridable Virtual Functions</h1>
|
||
|
||
<p>
|
||
In the <a href="exporting_classes.html">previous example</a> we exposed a simple
|
||
C++ class in Python and showed that we could write a subclass. We even
|
||
redefined one of the functions in our derived class. Now we will learn
|
||
how to make the function behave virtually <em>when called from C++</em>.
|
||
|
||
|
||
<h2><a name="overriding_example">Example</a></h2>
|
||
|
||
<p>In this example, it is assumed that <code>hello::greet()</code> is a virtual
|
||
member function:
|
||
|
||
<blockquote><pre>
|
||
class hello
|
||
{
|
||
public:
|
||
hello(const std::string& country) { this->country = country; }
|
||
<b>virtual</b> std::string greet() const { return "Hello from " + country; }
|
||
virtual ~hello(); // Good practice
|
||
...
|
||
};
|
||
</pre></blockquote>
|
||
|
||
<p>
|
||
We'll need a derived class<a href="#why_derived">*</a> to help us
|
||
dispatch the call to Python. In our derived class, we need the following
|
||
elements:
|
||
|
||
<ol>
|
||
|
||
<li><a name="derived_1">A</a> <code>PyObject*</code> data member (usually
|
||
called <tt>self</tt>) that holds a pointer to the Python object corresponding
|
||
to our C++ <tt>hello</tt> instance.
|
||
|
||
<li><a name="derived_2">For</a> each exposed constructor of the
|
||
base class <tt>T</tt>, a constructor which takes the same parameters preceded by an initial
|
||
<code>PyObject*</code> argument. The initial argument should be stored in the <tt>self</tt> data
|
||
member described above.
|
||
|
||
<li><a name="derived_3">If</a> the class being wrapped is ever returned <i>by
|
||
value</i> from a wrapped function, be sure you do the same for the
|
||
<tt>T</tt>'s copy constructor: you'll need a constructor taking arguments
|
||
<tt>(PyObject*, const T&)</tt>.
|
||
|
||
<li><a name="derived_4">An</a> implementation of each virtual function you may
|
||
wish to override in Python which uses
|
||
<tt>callback<</tt><i>return-type</i><tt>>::call_method(self, "</tt><i>name</i><tt>", </tt><i>args...</i><tt>)</tt> to call
|
||
the Python override.
|
||
|
||
<li><a name="derived_5">For</a> each non-pure virtual function meant to be
|
||
overridable from Python, a static member function (or a free function) taking
|
||
a reference or pointer to the <tt>T</tt> as the first parameter and which
|
||
forwards any additional parameters neccessary to the <i>default</i>
|
||
implementation of the virtual function. See also <a href="#private">this
|
||
note</a> if the base class virtual function is private.
|
||
|
||
</ol>
|
||
|
||
<blockquote><pre>
|
||
struct hello_callback : hello
|
||
{
|
||
// hello constructor storing initial self_ parameter
|
||
hello_callback(PyObject* self_, const std::string& x) // <a href="#derived_2">2</a>
|
||
: hello(x), self(self_) {}
|
||
|
||
// In case hello is returned by-value from a wrapped function
|
||
hello_callback(PyObject* self_, const hello& x) // <a href="#derived_3">3</a>
|
||
: hello(x), self(self_) {}
|
||
|
||
// Override greet to call back into Python
|
||
std::string greet() const // <a href="#derived_4">4</a>
|
||
{ return boost::python::callback<std::string>::call_method(self, "greet"); }
|
||
|
||
// Supplies the default implementation of greet
|
||
static std::string <a name= "default_implementation">default_greet</a>(const hello& self_) const // <a href="#derived_5">5</a>
|
||
{ return self_.hello::greet(); }
|
||
private:
|
||
PyObject* self; // <a href="#derived_1">1</a>
|
||
};
|
||
</pre></blockquote>
|
||
|
||
<p>
|
||
Finally, we add <tt>hello_callback</tt> to the <tt>
|
||
class_builder<></tt> declaration in our module initialization
|
||
function, and when we define the function, we must tell Boost.Python about the default
|
||
implementation:
|
||
|
||
<blockquote><pre>
|
||
// Create the <a name=
|
||
"hello_class">Python type object</a> for our extension class
|
||
boost::python::class_builder<hello<strong>,hello_callback></strong> hello_class(hello, "hello");
|
||
// Add a virtual member function
|
||
hello_class.def(&hello::greet, "greet", &<b>hello_callback::default_greet</b>);
|
||
</pre></blockquote>
|
||
|
||
<p>
|
||
Now our Python subclass of <tt>hello</tt> behaves as expected:
|
||
|
||
<blockquote><pre>
|
||
>>> class wordy(hello):
|
||
... def greet(self):
|
||
... return hello.greet(self) + ', where the weather is fine'
|
||
...
|
||
>>> hi2 = wordy('Florida')
|
||
>>> hi2.greet()
|
||
'Hello from Florida, where the weather is fine'
|
||
>>> invite(hi2)
|
||
'Hello from Florida, where the weather is fine! Please come soon!'
|
||
</pre></blockquote>
|
||
<p>
|
||
<a name="why_derived">*</a>You may ask, "Why do we need this derived
|
||
class? This could have been designed so that everything gets done right
|
||
inside of <tt>hello</tt>." 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.
|
||
|
||
<h2>Pure Virtual Functions</h2>
|
||
|
||
<p>
|
||
A pure virtual function with no implementation is actually a lot easier to
|
||
deal with than a virtual function with a default implementation. First of
|
||
all, you obviously don't need to <a href="#default_implementation"> supply
|
||
a default implementation</a>. Secondly, you don't need to call
|
||
<tt>def()</tt> on the <tt>extension_class<></tt> instance
|
||
for the virtual function. In fact, you wouldn't <em>want</em> to: if the
|
||
corresponding attribute on the Python class stays undefined, you'll get an
|
||
<tt>AttributeError</tt> in Python when you try to call the function,
|
||
indicating that it should have been implemented. For example:
|
||
<blockquote>
|
||
<pre>
|
||
struct baz {
|
||
<strong>virtual</strong> int pure(int) = 0;
|
||
int calls_pure(int x) { return pure(x) + 1000; }
|
||
};
|
||
|
||
struct baz_callback {
|
||
int pure(int x) { boost::python::callback<int>::call_method(m_self, "pure", x); }
|
||
};
|
||
|
||
BOOST_PYTHON_MODULE_INIT(foobar)
|
||
{
|
||
boost::python::module_builder foobar("foobar");
|
||
boost::python::class_builder<baz,baz_callback> baz_class("baz");
|
||
baz_class.def(&baz::calls_pure, "calls_pure");
|
||
}
|
||
</pre>
|
||
</blockquote>
|
||
<p>
|
||
Now in Python:
|
||
<blockquote>
|
||
<pre>
|
||
>>> from foobar import baz
|
||
>>> x = baz()
|
||
>>> x.pure(1)
|
||
Traceback (innermost last):
|
||
File "<stdin>", line 1, in ?
|
||
AttributeError: pure
|
||
>>> x.calls_pure(1)
|
||
Traceback (innermost last):
|
||
File "<stdin>", line 1, in ?
|
||
AttributeError: pure
|
||
>>> class mumble(baz):
|
||
... def pure(self, x): return x + 1
|
||
...
|
||
>>> y = mumble()
|
||
>>> y.pure(99)
|
||
100
|
||
>>> y.calls_pure(99)
|
||
1100
|
||
</pre></blockquote>
|
||
|
||
<a name="private"><h2>Private Non-Pure Virtual Functions</h2></a>
|
||
|
||
<p>This is one area where some minor intrusiveness on the wrapped library is
|
||
required. Once it has been overridden, the only way to call the base class
|
||
implementation of a private virtual function is to make the derived class a
|
||
friend of the base class. You didn't hear it from me, but most C++
|
||
implementations will allow you to change the declaration of the base class in
|
||
this limited way without breaking binary compatibility (though it will certainly
|
||
break the <a
|
||
href="http://cs.calvin.edu/c++/C++Standard-Nov97/basic.html#basic.def.odr">ODR</a>).
|
||
|
||
<hr>
|
||
<p>
|
||
Next: <a href="overloading.html">Function Overloading</a>
|
||
Previous: <a href="exporting_classes.html">Exporting Classes</a>
|
||
Up: <a href="index.html">Top</a>
|
||
<p>
|
||
© Copyright David Abrahams 2001. Permission to copy, use, modify,
|
||
sell and distribute this document is granted provided this copyright
|
||
notice appears in all copies. This document is provided "as is" without
|
||
express or implied warranty, and with no claim as to its suitability for
|
||
any purpose.
|
||
<p>
|
||
Updated: Mar 21, 2001
|
||
|