2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-20 16:52:15 +00:00
Files
python/doc/tutorial/doc/class_virtual_functions.html
Joel de Guzman 387e8aadc6 changes to no_init and deriving classes
[SVN r15979]
2002-10-24 21:33:40 +00:00

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>&amp; </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>&lt;</span><span class=keyword>int</span><span class=special>&gt;(</span><span class=identifier>self</span><span class=special>, </span><span class=string>&quot;f&quot;</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>, &quot;Why do we need the <tt>BaseWrap</tt> derived class? This could
have been designed so that everything gets done right inside of
Base.&quot;<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>&lt;</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>&gt;(</span><span class=string>&quot;Base&quot;</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>&quot;call_f&quot;</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>&gt;&gt;&gt; </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>&lt;</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>&gt;(</span><span class=string>&quot;Base&quot;</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>&gt;&gt;&gt; </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>&gt;&gt;&gt; </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>&gt;&gt;&gt; </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>&gt;&gt;&gt; </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(&quot;call_f&quot;, call_f);</tt>. Boost.Python dispatches this call.</li><li><tt>int call_f(Base&amp; 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&lt;int&gt;(self, &quot;f&quot;);</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>&lt;</span><span class=keyword>int</span><span class=special>&gt;(</span><span class=identifier>self</span><span class=special>, </span><span class=string>&quot;f&quot;</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>-&gt;</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>// &lt;&lt;=== 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>&lt;</span><span class=identifier>Base</span><span class=special>, </span><span class=identifier>BaseWrap, boost::non_copyable</span><span class=special>&gt;(</span><span class=string>&quot;Base&quot;</span><span class=special>)
</span><span class=special>.</span><span class=identifier>def</span><span class=special>(</span><span class=string>&quot;f&quot;</span><span class=special>, </span><span class=special>&amp;</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_&lt;Base&gt;</tt> with
<tt>no_init</tt>.</p>
<p>
In Python, the results would be as expected:</p>
<code><pre>
<span class=special>&gt;&gt;&gt; </span><span class=identifier>base </span><span class=special>= </span><span class=identifier>Base</span><span class=special>()
</span><span class=special>&gt;&gt;&gt; </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>&gt;&gt;&gt; </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>&gt;&gt;&gt; </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>&gt;&gt;&gt; </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>&gt;&gt;&gt; </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>&gt;&gt;&gt; </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 &copy; 2002 David Abrahams<br>Copyright &copy; 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 &quot;as is&quot; without express or implied warranty, and with
no claim as to its suitability for any purpose. </font> </p>
</body>
</html>