mirror of
https://github.com/boostorg/python.git
synced 2026-01-21 17:12:22 +00:00
196 lines
6.8 KiB
HTML
196 lines
6.8 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="example1.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>world::greet()</code> is a virtual
|
||
member function:
|
||
|
||
<blockquote><pre>
|
||
class world
|
||
{
|
||
public:
|
||
world(int);
|
||
virtual ~world();
|
||
<b>virtual</b> std::string greet() const { return "hi, world"; }
|
||
};
|
||
</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 that holds a
|
||
reference to the corresponding Python object.
|
||
|
||
<li><a name="derived_2">A</a> constructor for each exposed constructor of the
|
||
base class which stores an additional initial <code>PyObject*</code> argument
|
||
in the data member described above.
|
||
|
||
<li><a name="derived_3">An</a> implementation of each virtual function you may
|
||
wish to override in Python which uses
|
||
<code>boost::python::callback<<i>return-type</i>>::call_method()</code> to call
|
||
the Python override.
|
||
|
||
<li><a name="derived_4">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 base type 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 world_callback : world
|
||
{
|
||
world_callback(PyObject* self, int x) // <a href="#derived_2">2</a>
|
||
: world(x),
|
||
m_self(self) {}
|
||
|
||
std::string greet() const // <a href="#derived_3">3</a>
|
||
{ return boost::python::callback<std::string>::call_method(m_self, "get"); }
|
||
|
||
static std::string <a name= "default_implementation">default_get</a>(const hello::world& self) const // <a href="#derived_4">4</a>
|
||
{ return self.world::greet(); }
|
||
private:
|
||
PyObject* m_self; // <a href="#derived_1">1</a>
|
||
};
|
||
</pre></blockquote>
|
||
|
||
<p>
|
||
Finally, we add <code>world_callback</code> to the <code>
|
||
class_builder<></code> declaration in our module initialization
|
||
function, and when we define the function, we must tell py_cpp about the default
|
||
implementation:
|
||
|
||
<blockquote><pre>
|
||
// Create the <a name=
|
||
"world_class">Python type object</a> for our extension class
|
||
boost::python::class_builder<hello::world<strong>,world_callback></strong> world_class(hello, "world");
|
||
// Add a virtual member function
|
||
world_class.def(&world::get, "get", &<b>world_callback::default_get</b>);
|
||
</pre></blockquote>
|
||
|
||
<p>
|
||
Now our subclass of <code>hello.world</code> behaves as expected:
|
||
|
||
<blockquote><pre>
|
||
>>> class my_subclass(hello.world):
|
||
... def greet(self):
|
||
... return 'hello, world'
|
||
...
|
||
>>> hello.length(my_subclass())
|
||
12
|
||
</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 <code>hello::world</code>." One of the goals of py_cpp 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
|
||
<code>def()</code> on the <code>extension_class<></code> 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
|
||
<code>AttributeError</code> 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;
|
||
};
|
||
|
||
struct baz_callback {
|
||
int pure(int x) { boost::python::callback<int>::call_method(m_self, "pure", x); }
|
||
};
|
||
|
||
extern "C"
|
||
#ifdef _WIN32
|
||
__declspec(dllexport)
|
||
#endif
|
||
initfoobar()
|
||
{
|
||
try
|
||
{
|
||
boost::python::module_builder foobar("foobar");
|
||
boost::python::class_builder<baz,baz_callback> baz_class("baz");
|
||
baz_class.def(&baz::pure, "pure");
|
||
}
|
||
catch(...)
|
||
{
|
||
boost::python::handle_exception(); // Deal with the exception for Python
|
||
}
|
||
}
|
||
</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
|
||
>>> class mumble(baz):
|
||
... def pure(self, x): return x + 1
|
||
...
|
||
>>> y = mumble()
|
||
>>> y.pure(99)
|
||
100
|
||
</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>).
|
||
|
||
<p>
|
||
Next: <a href="overloading.html">Function Overloading</a>
|
||
Previous: <a href="example1.html">A Simple Example Using py_cpp</a>
|
||
Up: <a href="index.html">Top</a>
|
||
<p>
|
||
© Copyright David Abrahams 2000. 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: Nov 26, 2000
|
||
|