mirror of
https://github.com/boostorg/python.git
synced 2026-01-20 16:52:15 +00:00
238 lines
16 KiB
HTML
238 lines
16 KiB
HTML
<html>
|
|
<head>
|
|
<!-- Generated by the Spirit (http://spirit.sf.net) QuickDoc -->
|
|
<title>Class Virtual Functions</title>
|
|
<link rel="stylesheet" href="theme/style.css" type="text/css">
|
|
<link rel="prev" href="inheritance.html">
|
|
<link rel="next" href="class_operators_special_functions.html">
|
|
</head>
|
|
<body>
|
|
<table width="100%" height="48" border="0" cellspacing="2">
|
|
<tr>
|
|
<td><img src="theme/c%2B%2Bboost.gif">
|
|
</td>
|
|
<td width="85%">
|
|
<font size="6" face="Verdana, Arial, Helvetica, sans-serif"><b>Class Virtual Functions</b></font>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<br>
|
|
<table border="0">
|
|
<tr>
|
|
<td width="30"><a href="../index.html"><img src="theme/u_arr.gif" border="0"></a></td>
|
|
<td width="30"><a href="inheritance.html"><img src="theme/l_arr.gif" border="0"></a></td>
|
|
<td width="20"><a href="class_operators_special_functions.html"><img src="theme/r_arr.gif" border="0"></a></td>
|
|
</tr>
|
|
</table>
|
|
<p>
|
|
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 <tt>Base</tt> class:</p>
|
|
<code><pre>
|
|
<span class=keyword>struct </span><span class=identifier>Base
|
|
</span><span class=special>{
|
|
</span><span class=keyword>virtual </span><span class=keyword>int </span><span class=identifier>f</span><span class=special>() </span><span class=special>= </span><span class=number>0</span><span class=special>;
|
|
</span><span class=special>};
|
|
</span></pre></code>
|
|
<p>
|
|
Since <tt>f</tt> is a pure virtual function, <tt>Base</tt> is now an abstract
|
|
class. Given an instance of our class, the free function <tt>call_f</tt>
|
|
calls some implementation of this virtual function in a concrete
|
|
derived class:</p>
|
|
<code><pre>
|
|
<span class=keyword>int </span><span class=identifier>call_f</span><span class=special>(</span><span class=identifier>Base</span><span class=special>& </span><span class=identifier>b</span><span class=special>) </span><span class=special>{ </span><span class=keyword>return </span><span class=identifier>b</span><span class=special>.</span><span class=identifier>f</span><span class=special>(); </span><span class=special>}
|
|
</span></pre></code>
|
|
<p>
|
|
To allow this function to be implemented in a Python derived class, we
|
|
need to create a class wrapper:</p>
|
|
<code><pre>
|
|
<span class=keyword>struct </span><span class=identifier>BaseWrap </span><span class=special>: </span><span class=identifier>Base
|
|
</span><span class=special>{
|
|
</span><span class=identifier>BaseWrap</span><span class=special>(</span><span class=identifier>PyObject</span><span class=special>* </span><span class=identifier>self_</span><span class=special>)
|
|
</span><span class=special>: </span><span class=identifier>self</span><span class=special>(</span><span class=identifier>self_</span><span class=special>) </span><span class=special>{}
|
|
</span><span class=keyword>int </span><span class=identifier>f</span><span class=special>() </span><span class=special>{ </span><span class=keyword>return </span><span class=identifier>call_method</span><span class=special><</span><span class=keyword>int</span><span class=special>>(</span><span class=identifier>self</span><span class=special>, </span><span class=string>"f"</span><span class=special>); </span><span class=special>}
|
|
</span><span class=identifier>PyObject</span><span class=special>* </span><span class=identifier>self</span><span class=special>;
|
|
</span><span class=special>};
|
|
</span></pre></code>
|
|
<table width="80%" border="0" align="center">
|
|
<tr>
|
|
<td class="note_box">
|
|
<img src="theme/lens.gif"></img> <b>member function and methods</b><br><br> Python, like
|
|
many object oriented languages uses the term <b>methods</b>. Methods
|
|
correspond roughly to C++'s <b>member functions</b> </td>
|
|
</tr>
|
|
</table>
|
|
<p>
|
|
Our class wrapper <tt>BaseWrap</tt> is derived from <tt>Base</tt>. Its overridden
|
|
virtual member function <tt>f</tt> in effect calls the corresponding method
|
|
of the Python object <tt>self</tt>, which is a pointer back to the Python
|
|
<tt>Base</tt> object holding our <tt>BaseWrap</tt> instance.</p>
|
|
<table width="80%" border="0" align="center">
|
|
<tr>
|
|
<td class="note_box">
|
|
<img src="theme/note.gif"></img> <b>Why do we need BaseWrap?</b><br><br>
|
|
|
|
<i>You may ask</i>, "Why do we need the <tt>BaseWrap</tt> derived class? This could
|
|
have been designed so that everything gets done right inside of
|
|
Base."<br><br>
|
|
|
|
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.<br><br>
|
|
|
|
Note however that you don't need to do this to get methods overridden
|
|
in Python to behave virtually when called <i>from</i> <b>Python</b>. The only
|
|
time you need to do the <tt>BaseWrap</tt> dance is when you have a virtual
|
|
function that's going to be overridden in Python and called
|
|
polymorphically <i>from</i> <b>C++</b>. </td>
|
|
</tr>
|
|
</table>
|
|
<p>
|
|
Wrapping <tt>Base</tt> and the free function <tt>call_f</tt>:</p>
|
|
<code><pre>
|
|
<span class=identifier>class_</span><span class=special><</span><span class=identifier>Base</span><span class=special>, </span><span class=identifier>BaseWrap</span><span class=special>, </span><span class=identifier>boost</span><span class=special>::</span><span class=identifier>noncopyable</span><span class=special>>(</span><span class=string>"Base"</span><span class=special>, </span><span class=identifier>no_init</span><span class=special>)
|
|
</span><span class=special>;
|
|
</span><span class=identifier>def</span><span class=special>(</span><span class=string>"call_f"</span><span class=special>, </span><span class=identifier>call_f</span><span class=special>);
|
|
</span></pre></code>
|
|
<p>
|
|
Notice that we parameterized the <tt>class_</tt> template with <tt>BaseWrap</tt> as the
|
|
second parameter. What is <tt>noncopyable</tt>? 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.</p>
|
|
<p>
|
|
In Python, let us try to instantiate our <tt>Base</tt> class:</p>
|
|
<code>
|
|
<pre>
|
|
<span class=special>>>> </span><span class=identifier>base </span><span class=special>= </span><span class=identifier>Base</span><span class=special>()
|
|
</span>RuntimeError: This class cannot be instantiated from Python</pre>
|
|
</code>
|
|
<p>
|
|
Why is it an error? <tt>Base</tt> is an abstract class. As such it is advisable
|
|
to define the Python wrapper with <tt>no_init</tt> as we have done above. Doing
|
|
so will disallow abstract base classes such as <tt>Base</tt> to be instantiated.</p>
|
|
<a name="deriving_a_python_class"></a><h2>Deriving a Python class</h2>
|
|
<p> Now, at last, we can even derive from our base class <tt>Base</tt> in Python.
|
|
Before we can do that, we have to set up our <tt>class_</tt> wrapper as:</p>
|
|
<pre> <code><span class=identifier>class_</span><span class=special><</span><span class=identifier>Base</span><span class=special>, </span><span class=identifier>BaseWrap</span><span class=special>, </span><span class=identifier>boost</span><span class=special>::</span><span class=identifier>noncopyable</span><span class=special>>(</span><span class=string>"Base"</span><span class=special>)
|
|
</span><span class=special>;</span></code></pre>
|
|
<p>Otherwise, we have to suppress the Base class' <tt>no_init</tt> by adding an
|
|
<tt>__init__()</tt> method to all our derived classes. <tt>no_init </tt>actually
|
|
adds an <tt>__init__</tt> method that raises a Python RuntimeError exception.</p>
|
|
<code>
|
|
<pre>
|
|
<span class=special>>>> </span><span class=keyword>class </span><span class=identifier>Derived</span><span class=special>(</span><span class=identifier>Base</span><span class=special>):
|
|
</span><span class=special>... </span><span class=identifier>def </span><span class=identifier>f</span><span class=special>(</span><span class=identifier>self</span><span class=special>):
|
|
</span><span class=special>... </span><span class=keyword>return </span><span class=number>42
|
|
</span><span class=special>...
|
|
</span></pre>
|
|
</code>
|
|
<p>
|
|
Cool eh? A Python class deriving from a C++ class!</p>
|
|
<p>
|
|
Let's now make an instance of our Python class <tt>Derived</tt>:</p>
|
|
<code><pre>
|
|
<span class=special>>>> </span><span class=identifier>derived </span><span class=special>= </span><span class=identifier>Derived</span><span class=special>()
|
|
</span></pre></code>
|
|
<p>
|
|
Calling <tt>derived.f()</tt>:</p>
|
|
<code><pre>
|
|
<span class=special>>>> </span><span class=identifier>derived</span><span class=special>.</span><span class=identifier>f</span><span class=special>()
|
|
</span><span class=number>42
|
|
</span></pre></code>
|
|
<p>
|
|
Will yield the expected result. Finally, calling calling the free function
|
|
<tt>call_f</tt> with <tt>derived</tt> as argument:</p>
|
|
<code><pre>
|
|
<span class=special>>>> </span><span class=identifier>call_f</span><span class=special>(</span><span class=identifier>derived</span><span class=special>)
|
|
</span><span class=number>42
|
|
</span></pre></code>
|
|
<p>
|
|
Will also yield the expected result.</p>
|
|
<p>
|
|
Here's what's happening:</p>
|
|
<ol><li><tt>call_f(derived)</tt> is called in Python</li><li>This corresponds to <tt>def("call_f", call_f);</tt>. Boost.Python dispatches this call.</li><li><tt>int call_f(Base& b) { return b.f(); }</tt> accepts the call.</li><li>The overridden virtual function <tt>f</tt> of <tt>BaseWrap</tt> is called.</li><li><tt>call_method<int>(self, "f");</tt> dispatches the call back to Python.</li><li><tt>def f(self): return 42</tt> is finally called.</li></ol><p>
|
|
Rewind back to our <tt>Base</tt> class, if its member function <tt>f</tt> was not
|
|
declared as pure virtual:</p>
|
|
<code><pre>
|
|
<span class=keyword>struct </span><span class=identifier>Base
|
|
</span><span class=special>{
|
|
</span><span class=keyword>virtual </span><span class=keyword>int </span><span class=identifier>f</span><span class=special>() </span><span class=special>{ </span><span class=keyword>return </span><span class=number>0</span><span class=special>; </span><span class=special>}
|
|
</span><span class=special>};
|
|
</span></pre></code>
|
|
<p>
|
|
And instead is implemented to return <tt>0</tt>, as shown above.</p>
|
|
<code><pre>
|
|
<span class=keyword>struct </span><span class=identifier>BaseWrap </span><span class=special>: </span><span class=identifier>Base
|
|
</span><span class=special>{
|
|
</span><span class=identifier>BaseWrap</span><span class=special>(</span><span class=identifier>PyObject</span><span class=special>* </span><span class=identifier>self_</span><span class=special>)
|
|
</span><span class=special>: </span><span class=identifier>self</span><span class=special>(</span><span class=identifier>self_</span><span class=special>) </span><span class=special>{}
|
|
</span><span class=keyword>int </span><span class=identifier>f</span><span class=special>() </span><span class=special>{ </span><span class=keyword>return </span><span class=identifier>call_method</span><span class=special><</span><span class=keyword>int</span><span class=special>>(</span><span class=identifier>self</span><span class=special>, </span><span class=string>"f"</span><span class=special>); </span><span class=special>}
|
|
</span><span class=keyword>static </span><span class=keyword>int </span><span class=identifier>default_f</span><span class=special>(</span><span class=identifier>Base</span><span class=special>* </span><span class=identifier>b</span><span class=special>) </span><span class=special>{ </span><span class=keyword>return </span><span class=identifier>b</span><span class=special>-></span><span class=identifier>Base</span><span class=special>::</span><span class=identifier>f</span><span class=special>(); </span><span class=special>} </span><span class=comment>// <<=== added
|
|
</span><span class=identifier>PyObject</span><span class=special>* </span><span class=identifier>self</span><span class=special>;
|
|
</span><span class=special>};
|
|
</span></pre></code>
|
|
<p>
|
|
then, our Boost.Python wrapper:</p>
|
|
<code>
|
|
<pre> <span class=identifier>class_</span><span class=special><</span><span class=identifier>Base</span><span class=special>, </span><span class=identifier>BaseWrap, boost::non_copyable</span><span class=special>>(</span><span class=string>"Base"</span><span class=special>)
|
|
</span><span class=special>.</span><span class=identifier>def</span><span class=special>(</span><span class=string>"f"</span><span class=special>, </span><span class=special>&</span><span class=identifier>BaseWrap</span><span class=special>::</span><span class=identifier>default_f</span><span class=special>)
|
|
</span><span class=special>;
|
|
</span></pre>
|
|
</code>
|
|
<p>
|
|
Note that we are allowing <tt>Base</tt> objects to be instantiated this time,
|
|
unlike before where we specifically defined the <tt>class_<Base></tt> with
|
|
<tt>no_init</tt>.</p>
|
|
<p>
|
|
In Python, the results would be as expected:</p>
|
|
<code><pre>
|
|
<span class=special>>>> </span><span class=identifier>base </span><span class=special>= </span><span class=identifier>Base</span><span class=special>()
|
|
</span><span class=special>>>> </span><span class=keyword>class </span><span class=identifier>Derived</span><span class=special>(</span><span class=identifier>Base</span><span class=special>):
|
|
</span><span class=special>... </span><span class=identifier>def </span><span class=identifier>f</span><span class=special>(</span><span class=identifier>self</span><span class=special>):
|
|
</span><span class=special>... </span><span class=keyword>return </span><span class=number>42
|
|
</span><span class=special>...
|
|
</span><span class=special>>>> </span><span class=identifier>derived </span><span class=special>= </span><span class=identifier>Derived</span><span class=special>()
|
|
</span></pre></code>
|
|
<p>
|
|
Calling <tt>base.f()</tt>:</p>
|
|
<code><pre>
|
|
<span class=special>>>> </span><span class=identifier>base</span><span class=special>.</span><span class=identifier>f</span><span class=special>()
|
|
</span><span class=number>0
|
|
</span></pre></code>
|
|
<p>
|
|
Calling <tt>derived.f()</tt>:</p>
|
|
<code><pre>
|
|
<span class=special>>>> </span><span class=identifier>derived</span><span class=special>.</span><span class=identifier>f</span><span class=special>()
|
|
</span><span class=number>42
|
|
</span></pre></code>
|
|
<p>
|
|
Calling <tt>call_f</tt>, passing in a <tt>base</tt> object:</p>
|
|
<code><pre>
|
|
<span class=special>>>> </span><span class=identifier>call_f</span><span class=special>(</span><span class=identifier>base</span><span class=special>)
|
|
</span><span class=number>0
|
|
</span></pre></code>
|
|
<p>
|
|
Calling <tt>call_f</tt>, passing in a <tt>derived</tt> object:</p>
|
|
<code><pre>
|
|
<span class=special>>>> </span><span class=identifier>call_f</span><span class=special>(</span><span class=identifier>derived</span><span class=special>)
|
|
</span><span class=number>42
|
|
</span></pre></code>
|
|
<table border="0">
|
|
<tr>
|
|
<td width="30"><a href="../index.html"><img src="theme/u_arr.gif" border="0"></a></td>
|
|
<td width="30"><a href="inheritance.html"><img src="theme/l_arr.gif" border="0"></a></td>
|
|
<td width="20"><a href="class_operators_special_functions.html"><img src="theme/r_arr.gif" border="0"></a></td>
|
|
</tr>
|
|
</table>
|
|
<br>
|
|
<hr size="1"><p class="copyright">Copyright © 2002 David Abrahams<br>Copyright © 2002 Joel de Guzman<br><br>
|
|
<font size="2">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. </font> </p>
|
|
</body>
|
|
</html>
|