mirror of
https://github.com/boostorg/python.git
synced 2026-01-19 16:32:16 +00:00
2571 lines
196 KiB
XML
2571 lines
196 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
|
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
|
|
<library
|
|
id="python"
|
|
name="python"
|
|
dirname="python"
|
|
last-revision="$Date$"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude">
|
|
<libraryinfo>
|
|
<author>
|
|
<firstname>Joel</firstname>
|
|
<surname>de Guzman</surname>
|
|
</author>
|
|
<author>
|
|
<firstname>David</firstname>
|
|
<surname>Abrahams</surname>
|
|
</author>
|
|
|
|
<copyright>
|
|
<year>2002</year>
|
|
<year>2003</year>
|
|
<year>2004</year>
|
|
<holder>Joel de Guzman, David Abrahams</holder>
|
|
</copyright>
|
|
|
|
<legalnotice>
|
|
<para>
|
|
Distributed under the Boost Software License, Version 1.0.
|
|
(See accompanying file LICENSE_1_0.txt or copy at
|
|
<ulink url="http://www.boost.org/LICENSE_1_0.txt">
|
|
http://www.boost.org/LICENSE_1_0.txt
|
|
</ulink>)
|
|
|
|
</para>
|
|
</legalnotice>
|
|
|
|
<librarypurpose>
|
|
Reflects C++ classes and functions into Python
|
|
</librarypurpose>
|
|
|
|
<librarycategory name="category:inter-language support"></librarycategory>
|
|
|
|
</libraryinfo>
|
|
|
|
<title>python 1.0</title>
|
|
|
|
|
|
|
|
<section id="python.quickstart">
|
|
<title>QuickStart</title>
|
|
<para>
|
|
The Boost Python Library is a framework for interfacing Python and
|
|
C++. It allows you to quickly and seamlessly expose C++ classes
|
|
functions and objects to Python, and vice-versa, using no special
|
|
tools -- just your C++ compiler. It is designed to wrap C++ interfaces
|
|
non-intrusively, so that you should not have to change the C++ code at
|
|
all in order to wrap it, making Boost.Python ideal for exposing
|
|
3rd-party libraries to Python. The library's use of advanced
|
|
metaprogramming techniques simplifies its syntax for users, so that
|
|
wrapping code takes on the look of a kind of declarative interface
|
|
definition language (IDL).</para>
|
|
<anchor id="quickstart.hello_world" /><bridgehead renderas="sect2">Hello World</bridgehead><para>
|
|
Following C/C++ tradition, let's start with the "hello, world". A C++
|
|
Function:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">char</phrase><phrase role="keyword"> const</phrase><phrase role="special">*</phrase><phrase role="identifier"> greet</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
{</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="string"> "hello, world"</phrase><phrase role="special">;</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
can be exposed to Python by writing a Boost.Python wrapper:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="preprocessor">#include</phrase><phrase role="special"> <</phrase><phrase role="identifier">boost</phrase><phrase role="special">/</phrase><phrase role="identifier">python</phrase><phrase role="special">.</phrase><phrase role="identifier">hpp</phrase><phrase role="special">></phrase><phrase role="keyword">
|
|
using</phrase><phrase role="keyword"> namespace</phrase><phrase role="identifier"> boost</phrase><phrase role="special">::</phrase><phrase role="identifier">python</phrase><phrase role="special">;</phrase><phrase role="identifier">
|
|
|
|
BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="identifier">hello</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
def</phrase><phrase role="special">(</phrase><phrase role="string">"greet"</phrase><phrase role="special">,</phrase><phrase role="identifier"> greet</phrase><phrase role="special">);</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
That's it. We're done. We can now build this as a shared library. The
|
|
resulting DLL is now visible to Python. Here's a sample Python session:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> import</phrase><phrase role="identifier"> hello</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> print</phrase><phrase role="identifier"> hello</phrase><phrase role="special">.</phrase><phrase role="identifier">greet</phrase><phrase role="special">()</phrase><phrase role="identifier">
|
|
hello</phrase><phrase role="special">,</phrase><phrase role="identifier"> world</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<blockquote><para><emphasis><emphasis role="bold">Next stop... Building your Hello World module from start to finish...</emphasis></emphasis></para></blockquote></section>
|
|
<section id="python.hello">
|
|
<title> Building Hello World</title>
|
|
<anchor id="hello.from_start_to_finish" /><bridgehead renderas="sect2">From Start To Finish</bridgehead><para>
|
|
Now the first thing you'd want to do is to build the Hello World module and
|
|
try it for yourself in Python. In this section, we shall outline the steps
|
|
necessary to achieve that. We shall use the build tool that comes bundled
|
|
with every boost distribution: <emphasis role="bold">bjam</emphasis>.</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">Building without bjam</emphasis><para/>
|
|
<para/>
|
|
|
|
Besides bjam, there are of course other ways to get your module built.
|
|
What's written here should not be taken as "the one and only way".
|
|
There are of course other build tools apart from <literal>bjam</literal>.<para/>
|
|
<para/>
|
|
|
|
Take note however that the preferred build tool for Boost.Python is bjam.
|
|
There are so many ways to set up the build incorrectly. Experience shows
|
|
that 90% of the "I can't build Boost.Python" problems come from people
|
|
who had to use a different tool.
|
|
</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
<para>
|
|
We shall skip over the details. Our objective will be to simply create the
|
|
hello world module and run it in Python. For a complete reference to
|
|
building Boost.Python, check out: <ulink url="../../../building.html">building.html</ulink>.
|
|
After this brief <emphasis>bjam</emphasis> tutorial, we should have built two DLLs:</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
boost_python.dll
|
|
</listitem><listitem>
|
|
hello.pyd
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
if you are on Windows, and</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
libboost_python.so
|
|
</listitem><listitem>
|
|
hello.so
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
if you are on Unix.</para>
|
|
<para>
|
|
The tutorial example can be found in the directory:
|
|
<literal>libs/python/example/tutorial</literal>. There, you can find:</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
hello.cpp
|
|
</listitem><listitem>
|
|
Jamfile
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
The <literal>hello.cpp</literal> file is our C++ hello world example. The <literal>Jamfile</literal> is a
|
|
minimalist <emphasis>bjam</emphasis> script that builds the DLLs for us.</para>
|
|
<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
|
|
<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>
|
|
<para>
|
|
Here is our minimalist Jamfile:</para>
|
|
<programlisting><literal> subproject libs/python/example/tutorial ;
|
|
|
|
SEARCH on python.jam = $(BOOST_BUILD_PATH) ;
|
|
include python.jam ;
|
|
|
|
extension hello # Declare a Python extension called hello
|
|
: hello.cpp # source
|
|
<dll>../../build/boost_python # dependencies
|
|
;
|
|
</literal></programlisting><para>
|
|
First, we need to specify our location in the boost project hierarchy.
|
|
It so happens that the tutorial example is located in <literal>/libs/python/example/tutorial</literal>.
|
|
Thus:</para>
|
|
<programlisting><literal> subproject libs/python/example/tutorial ;
|
|
</literal></programlisting><para>
|
|
Then we will include the definitions needed by Python modules:</para>
|
|
<programlisting><literal> SEARCH on python.jam = $(BOOST_BUILD_PATH) ;
|
|
include python.jam ;
|
|
</literal></programlisting><para>
|
|
Finally we declare our <literal>hello</literal> extension:</para>
|
|
<programlisting><literal> extension hello # Declare a Python extension called hello
|
|
: hello.cpp # source
|
|
<dll>../../build/boost_python # dependencies
|
|
;
|
|
</literal></programlisting><anchor id="hello.running_bjam" /><bridgehead renderas="sect2">Running bjam</bridgehead><para>
|
|
<emphasis>bjam</emphasis> is run using your operating system's command line interpreter.</para>
|
|
<blockquote><para>Start it up.</para></blockquote><para>
|
|
Make sure that the environment is set so that we can invoke the C++
|
|
compiler. With MSVC, that would mean running the <literal>Vcvars32.bat</literal> batch
|
|
file. For instance:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">C</phrase><phrase role="special">:\</phrase><phrase role="identifier">Program</phrase><phrase role="identifier"> Files</phrase><phrase role="special">\</phrase><phrase role="identifier">Microsoft</phrase><phrase role="identifier"> Visual</phrase><phrase role="identifier"> Studio</phrase><phrase role="special">\</phrase><phrase role="identifier">VC98</phrase><phrase role="special">\</phrase><phrase role="identifier">bin</phrase><phrase role="special">\</phrase><phrase role="identifier">Vcvars32</phrase><phrase role="special">.</phrase><phrase role="identifier">bat</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Some environment variables will have to be setup for proper building of our
|
|
Python modules. Example:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">set</phrase><phrase role="identifier"> PYTHON_ROOT</phrase><phrase role="special">=</phrase><phrase role="identifier">c</phrase><phrase role="special">:/</phrase><phrase role="identifier">dev</phrase><phrase role="special">/</phrase><phrase role="identifier">tools</phrase><phrase role="special">/</phrase><phrase role="identifier">python</phrase><phrase role="identifier">
|
|
set</phrase><phrase role="identifier"> PYTHON_VERSION</phrase><phrase role="special">=</phrase><phrase role="number">2.2</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
The above assumes that the Python installation is in <literal>c:/dev/tools/python</literal>
|
|
and that we are using Python version 2.2. You'll have to tweak this path
|
|
appropriately.</para>
|
|
<informaltable frame="all">
|
|
<?dbhtml table-width="74%" ?>
|
|
<tgroup cols="1">
|
|
<tbody>
|
|
<row>
|
|
<entry>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/tip.png"></imagedata></imageobject></inlinemediaobject> Be sure not to include a third number, e.g. <emphasis role="bold">not</emphasis> "2.2.1",
|
|
even if that's the version you have.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
<para>
|
|
Now we are ready... Be sure to <literal>cd</literal> to <literal>libs/python/example/tutorial</literal>
|
|
where the tutorial <literal>"hello.cpp"</literal> and the <literal>"Jamfile"</literal> is situated.</para>
|
|
<para>
|
|
Finally:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">bjam</phrase><phrase role="special"> -</phrase><phrase role="identifier">sTOOLS</phrase><phrase role="special">=</phrase><phrase role="identifier">msvc</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
We are again assuming that we are using Microsoft Visual C++ version 6. If
|
|
not, then you will have to specify the appropriate tool. See
|
|
<ulink url="../../../../../../tools/build/index.html">Building Boost Libraries</ulink> for
|
|
further details.</para>
|
|
<para>
|
|
It should be building now:</para>
|
|
<programlisting><literal> cd C:\dev\boost\libs\python\example\tutorial
|
|
bjam -sTOOLS=msvc
|
|
...patience...
|
|
...found 1703 targets...
|
|
...updating 40 targets...
|
|
</literal></programlisting><para>
|
|
And so on... Finally:</para>
|
|
<programlisting><literal> vc-C++ ........\libs\python\example\tutorial\bin\hello.pyd\msvc\debug\
|
|
runtime-link-dynamic\hello.obj
|
|
hello.cpp
|
|
vc-Link ........\libs\python\example\tutorial\bin\hello.pyd\msvc\debug\
|
|
runtime-link-dynamic\hello.pyd ........\libs\python\example\tutorial\bin\
|
|
hello.pyd\msvc\debug\runtime-link-dynamic\hello.lib
|
|
Creating library ........\libs\python\example\tutorial\bin\hello.pyd\
|
|
msvc\debug\runtime-link-dynamic\hello.lib and object ........\libs\python\
|
|
example\tutorial\bin\hello.pyd\msvc\debug\runtime-link-dynamic\hello.exp
|
|
...updated 40 targets...
|
|
</literal></programlisting><para>
|
|
If all is well, you should now have:</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
boost_python.dll
|
|
</listitem><listitem>
|
|
hello.pyd
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
if you are on Windows, and</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
libboost_python.so
|
|
</listitem><listitem>
|
|
hello.so
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
if you are on Unix.</para>
|
|
<para>
|
|
<literal>boost_python.dll</literal> can be found somewhere in <literal>libs\python\build\bin</literal>
|
|
while <literal>hello.pyd</literal> can be found somewhere in
|
|
<literal>libs\python\example\tutorial\bin</literal>. After a successful build, you can just
|
|
link in these DLLs with the Python interpreter. In Windows for example, you
|
|
can simply put these libraries inside the directory where the Python
|
|
executable is.</para>
|
|
<para>
|
|
You may now fire up Python and run our hello module:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> import</phrase><phrase role="identifier"> hello</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> print</phrase><phrase role="identifier"> hello</phrase><phrase role="special">.</phrase><phrase role="identifier">greet</phrase><phrase role="special">()</phrase><phrase role="identifier">
|
|
hello</phrase><phrase role="special">,</phrase><phrase role="identifier"> world</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<blockquote><para><emphasis role="bold">There you go... Have fun!</emphasis></para></blockquote></section>
|
|
<section id="python.exposing">
|
|
<title> Exposing Classes</title>
|
|
<para>
|
|
Now let's expose a C++ class to Python.</para>
|
|
<para>
|
|
Consider a C++ class/struct that we want to expose to Python:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">struct</phrase><phrase role="identifier"> World</phrase><phrase role="special">
|
|
{</phrase><phrase role="keyword">
|
|
void</phrase><phrase role="identifier"> set</phrase><phrase role="special">(</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="identifier"> msg</phrase><phrase role="special">)</phrase><phrase role="special"> {</phrase><phrase role="keyword"> this</phrase><phrase role="special">-></phrase><phrase role="identifier">msg</phrase><phrase role="special"> =</phrase><phrase role="identifier"> msg</phrase><phrase role="special">;</phrase><phrase role="special"> }</phrase><phrase role="identifier">
|
|
std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="identifier"> greet</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="identifier"> msg</phrase><phrase role="special">;</phrase><phrase role="special"> }</phrase><phrase role="identifier">
|
|
std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="identifier"> msg</phrase><phrase role="special">;</phrase><phrase role="special">
|
|
};</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
We can expose this to Python by writing a corresponding Boost.Python
|
|
C++ Wrapper:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="preprocessor">#include</phrase><phrase role="special"> <</phrase><phrase role="identifier">boost</phrase><phrase role="special">/</phrase><phrase role="identifier">python</phrase><phrase role="special">.</phrase><phrase role="identifier">hpp</phrase><phrase role="special">></phrase><phrase role="keyword">
|
|
using</phrase><phrase role="keyword"> namespace</phrase><phrase role="identifier"> boost</phrase><phrase role="special">::</phrase><phrase role="identifier">python</phrase><phrase role="special">;</phrase><phrase role="identifier">
|
|
|
|
BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="identifier">hello</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
class_</phrase><phrase role="special"><</phrase><phrase role="identifier">World</phrase><phrase role="special">>(</phrase><phrase role="string">"World"</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"greet"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">World</phrase><phrase role="special">::</phrase><phrase role="identifier">greet</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"set"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">World</phrase><phrase role="special">::</phrase><phrase role="identifier">set</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
;</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Here, we wrote a C++ class wrapper that exposes the member functions
|
|
<literal>greet</literal> and <literal>set</literal>. Now, after building our module as a shared library, we
|
|
may use our class <literal>World</literal> in Python. Here's a sample Python session:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> import</phrase><phrase role="identifier"> hello</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> planet</phrase><phrase role="special"> =</phrase><phrase role="identifier"> hello</phrase><phrase role="special">.</phrase><phrase role="identifier">World</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> planet</phrase><phrase role="special">.</phrase><phrase role="identifier">set</phrase><phrase role="special">(</phrase><phrase role="char">'howdy'</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> planet</phrase><phrase role="special">.</phrase><phrase role="identifier">greet</phrase><phrase role="special">()</phrase><phrase role="char">
|
|
'howdy'</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
|
|
<section id="python.constructors">
|
|
<title>Constructors</title>
|
|
<para>
|
|
Our previous example didn't have any explicit constructors.
|
|
Since <literal>World</literal> is declared as a plain struct, it has an implicit default
|
|
constructor. Boost.Python exposes the default constructor by default,
|
|
which is why we were able to write</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> planet</phrase><phrase role="special"> =</phrase><phrase role="identifier"> hello</phrase><phrase role="special">.</phrase><phrase role="identifier">World</phrase><phrase role="special">()</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
We may wish to wrap a class with a non-default constructor. Let us
|
|
build on our previous example:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">struct</phrase><phrase role="identifier"> World</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
World</phrase><phrase role="special">(</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="identifier"> msg</phrase><phrase role="special">):</phrase><phrase role="identifier"> msg</phrase><phrase role="special">(</phrase><phrase role="identifier">msg</phrase><phrase role="special">)</phrase><phrase role="special"> {}</phrase><phrase role="comment"> // added constructor
|
|
</phrase><phrase role="keyword"> void</phrase><phrase role="identifier"> set</phrase><phrase role="special">(</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="identifier"> msg</phrase><phrase role="special">)</phrase><phrase role="special"> {</phrase><phrase role="keyword"> this</phrase><phrase role="special">-></phrase><phrase role="identifier">msg</phrase><phrase role="special"> =</phrase><phrase role="identifier"> msg</phrase><phrase role="special">;</phrase><phrase role="special"> }</phrase><phrase role="identifier">
|
|
std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="identifier"> greet</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="identifier"> msg</phrase><phrase role="special">;</phrase><phrase role="special"> }</phrase><phrase role="identifier">
|
|
std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="identifier"> msg</phrase><phrase role="special">;</phrase><phrase role="special">
|
|
};</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
This time <literal>World</literal> has no default constructor; our previous
|
|
wrapping code would fail to compile when the library tried to expose
|
|
it. We have to tell <literal>class_<World></literal> about the constructor we want to
|
|
expose instead.</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="preprocessor">#include</phrase><phrase role="special"> <</phrase><phrase role="identifier">boost</phrase><phrase role="special">/</phrase><phrase role="identifier">python</phrase><phrase role="special">.</phrase><phrase role="identifier">hpp</phrase><phrase role="special">></phrase><phrase role="keyword">
|
|
using</phrase><phrase role="keyword"> namespace</phrase><phrase role="identifier"> boost</phrase><phrase role="special">::</phrase><phrase role="identifier">python</phrase><phrase role="special">;</phrase><phrase role="identifier">
|
|
|
|
BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="identifier">hello</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
class_</phrase><phrase role="special"><</phrase><phrase role="identifier">World</phrase><phrase role="special">>(</phrase><phrase role="string">"World"</phrase><phrase role="special">,</phrase><phrase role="identifier"> init</phrase><phrase role="special"><</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="special">>())</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"greet"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">World</phrase><phrase role="special">::</phrase><phrase role="identifier">greet</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"set"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">World</phrase><phrase role="special">::</phrase><phrase role="identifier">set</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
;</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
<literal>init<std::string>()</literal> exposes the constructor taking in a
|
|
<literal>std::string</literal> (in Python, constructors are spelled
|
|
"<literal>"<emphasis role="underline">_init</emphasis>_"</literal>").</para>
|
|
<para>
|
|
We can expose additional constructors by passing more <literal>init<...></literal>s to
|
|
the <literal>def()</literal> member function. Say for example we have another World
|
|
constructor taking in two doubles:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">World</phrase><phrase role="special">>(</phrase><phrase role="string">"World"</phrase><phrase role="special">,</phrase><phrase role="identifier"> init</phrase><phrase role="special"><</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="special">>())</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="identifier">init</phrase><phrase role="special"><</phrase><phrase role="keyword">double</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="special">>())</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"greet"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">World</phrase><phrase role="special">::</phrase><phrase role="identifier">greet</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"set"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">World</phrase><phrase role="special">::</phrase><phrase role="identifier">set</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
;</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
On the other hand, if we do not wish to expose any constructors at
|
|
all, we may use <literal>no_init</literal> instead:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">Abstract</phrase><phrase role="special">>(</phrase><phrase role="string">"Abstract"</phrase><phrase role="special">,</phrase><phrase role="identifier"> no_init</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
This actually adds an <literal><emphasis role="underline">_init</emphasis>_</literal> method which always raises a
|
|
Python RuntimeError exception.</para>
|
|
</section>
|
|
<section id="python.class_data_members">
|
|
<title>Class Data Members</title>
|
|
<para>
|
|
Data members may also be exposed to Python so that they can be
|
|
accessed as attributes of the corresponding Python class. Each data
|
|
member that we wish to be exposed may be regarded as <emphasis role="bold">read-only</emphasis> or
|
|
<emphasis role="bold">read-write</emphasis>. Consider this class <literal>Var</literal>:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">struct</phrase><phrase role="identifier"> Var</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
Var</phrase><phrase role="special">(</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="identifier"> name</phrase><phrase role="special">)</phrase><phrase role="special"> :</phrase><phrase role="identifier"> name</phrase><phrase role="special">(</phrase><phrase role="identifier">name</phrase><phrase role="special">),</phrase><phrase role="identifier"> value</phrase><phrase role="special">()</phrase><phrase role="special"> {}</phrase><phrase role="identifier">
|
|
std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="keyword"> const</phrase><phrase role="identifier"> name</phrase><phrase role="special">;</phrase><phrase role="keyword">
|
|
float</phrase><phrase role="identifier"> value</phrase><phrase role="special">;</phrase><phrase role="special">
|
|
};</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Our C++ <literal>Var</literal> class and its data members can be exposed to Python:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">Var</phrase><phrase role="special">>(</phrase><phrase role="string">"Var"</phrase><phrase role="special">,</phrase><phrase role="identifier"> init</phrase><phrase role="special"><</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="special">>())</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def_readonly</phrase><phrase role="special">(</phrase><phrase role="string">"name"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">Var</phrase><phrase role="special">::</phrase><phrase role="identifier">name</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def_readwrite</phrase><phrase role="special">(</phrase><phrase role="string">"value"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">Var</phrase><phrase role="special">::</phrase><phrase role="identifier">value</phrase><phrase role="special">);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Then, in Python, assuming we have placed our Var class inside the namespace
|
|
hello as we did before:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> x</phrase><phrase role="special"> =</phrase><phrase role="identifier"> hello</phrase><phrase role="special">.</phrase><phrase role="identifier">Var</phrase><phrase role="special">(</phrase><phrase role="char">'pi'</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">value</phrase><phrase role="special"> =</phrase><phrase role="number"> 3.14</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> print</phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">name</phrase><phrase role="special">,</phrase><phrase role="char"> 'is around'</phrase><phrase role="special">,</phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">value</phrase><phrase role="identifier">
|
|
pi</phrase><phrase role="identifier"> is</phrase><phrase role="identifier"> around</phrase><phrase role="number"> 3.14</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Note that <literal>name</literal> is exposed as <emphasis role="bold">read-only</emphasis> while <literal>value</literal> is exposed
|
|
as <emphasis role="bold">read-write</emphasis>.</para>
|
|
<programlisting><literal> >>> x.name = 'e' # can't change name
|
|
Traceback (most recent call last):
|
|
File "<stdin>", line 1, in ?
|
|
AttributeError: can't set attribute
|
|
</literal></programlisting></section>
|
|
<section id="python.class_properties">
|
|
<title>Class Properties</title>
|
|
<para>
|
|
In C++, classes with public data members are usually frowned
|
|
upon. Well designed classes that take advantage of encapsulation hide
|
|
the class' data members. The only way to access the class' data is
|
|
through access (getter/setter) functions. Access functions expose class
|
|
properties. Here's an example:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">struct</phrase><phrase role="identifier"> Num</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
Num</phrase><phrase role="special">();</phrase><phrase role="keyword">
|
|
float</phrase><phrase role="identifier"> get</phrase><phrase role="special">()</phrase><phrase role="keyword"> const</phrase><phrase role="special">;</phrase><phrase role="keyword">
|
|
void</phrase><phrase role="identifier"> set</phrase><phrase role="special">(</phrase><phrase role="keyword">float</phrase><phrase role="identifier"> value</phrase><phrase role="special">);</phrase><phrase role="special">
|
|
...</phrase><phrase role="special">
|
|
};</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
However, in Python attribute access is fine; it doesn't neccessarily break
|
|
encapsulation to let users handle attributes directly, because the
|
|
attributes can just be a different syntax for a method call. Wrapping our
|
|
<literal>Num</literal> class using Boost.Python:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">Num</phrase><phrase role="special">>(</phrase><phrase role="string">"Num"</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">add_property</phrase><phrase role="special">(</phrase><phrase role="string">"rovalue"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">Num</phrase><phrase role="special">::</phrase><phrase role="identifier">get</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">add_property</phrase><phrase role="special">(</phrase><phrase role="string">"value"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">Num</phrase><phrase role="special">::</phrase><phrase role="identifier">get</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">Num</phrase><phrase role="special">::</phrase><phrase role="identifier">set</phrase><phrase role="special">);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
And at last, in Python:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> x</phrase><phrase role="special"> =</phrase><phrase role="identifier"> Num</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">value</phrase><phrase role="special"> =</phrase><phrase role="number"> 3.14</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">value</phrase><phrase role="special">,</phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">rovalue</phrase><phrase role="special">
|
|
(</phrase><phrase role="number">3.14</phrase><phrase role="special">,</phrase><phrase role="number"> 3.14</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">rovalue</phrase><phrase role="special"> =</phrase><phrase role="number"> 2.17</phrase> #<phrase role="identifier"> error</phrase><phrase role="special">!</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Take note that the class property <literal>rovalue</literal> is exposed as <emphasis role="bold">read-only</emphasis>
|
|
since the <literal>rovalue</literal> setter member function is not passed in:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">.</phrase><phrase role="identifier">add_property</phrase><phrase role="special">(</phrase><phrase role="string">"rovalue"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">Num</phrase><phrase role="special">::</phrase><phrase role="identifier">get</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
</section>
|
|
<section id="python.inheritance">
|
|
<title>Inheritance</title>
|
|
<para>
|
|
In the previous examples, we dealt with classes that are not polymorphic.
|
|
This is not often the case. Much of the time, we will be wrapping
|
|
polymorphic classes and class hierarchies related by inheritance. We will
|
|
often have to write Boost.Python wrappers for classes that are derived from
|
|
abstract base classes.</para>
|
|
<para>
|
|
Consider this trivial inheritance structure:</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">
|
|
struct</phrase><phrase role="identifier"> Derived</phrase><phrase role="special"> :</phrase><phrase role="identifier"> Base</phrase><phrase role="special"> {};</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
And a set of C++ functions operating on <literal>Base</literal> and <literal>Derived</literal> object
|
|
instances:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">void</phrase><phrase role="identifier"> b</phrase><phrase role="special">(</phrase><phrase role="identifier">Base</phrase><phrase role="special">*);</phrase><phrase role="keyword">
|
|
void</phrase><phrase role="identifier"> d</phrase><phrase role="special">(</phrase><phrase role="identifier">Derived</phrase><phrase role="special">*);</phrase><phrase role="identifier">
|
|
Base</phrase><phrase role="special">*</phrase><phrase role="identifier"> factory</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="keyword"> new</phrase><phrase role="identifier"> Derived</phrase><phrase role="special">;</phrase><phrase role="special"> }</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
We've seen how we can wrap the base class <literal>Base</literal>:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">Base</phrase><phrase role="special">>(</phrase><phrase role="string">"Base"</phrase><phrase role="special">)</phrase><phrase role="comment">
|
|
/*...*/</phrase><phrase role="special">
|
|
;</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Now we can inform Boost.Python of the inheritance relationship between
|
|
<literal>Derived</literal> and its base class <literal>Base</literal>. Thus:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">Derived</phrase><phrase role="special">,</phrase><phrase role="identifier"> bases</phrase><phrase role="special"><</phrase><phrase role="identifier">Base</phrase><phrase role="special">></phrase><phrase role="special"> >(</phrase><phrase role="string">"Derived"</phrase><phrase role="special">)</phrase><phrase role="comment">
|
|
/*...*/</phrase><phrase role="special">
|
|
;</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Doing so, we get some things for free:</para>
|
|
<orderedlist>
|
|
<listitem>
|
|
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.
|
|
</listitem>
|
|
</orderedlist><para>
|
|
Now, we shall expose the C++ free functions <literal>b</literal> and <literal>d</literal> and <literal>factory</literal>:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"b"</phrase><phrase role="special">,</phrase><phrase role="identifier"> b</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
def</phrase><phrase role="special">(</phrase><phrase role="string">"d"</phrase><phrase role="special">,</phrase><phrase role="identifier"> d</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
def</phrase><phrase role="special">(</phrase><phrase role="string">"factory"</phrase><phrase role="special">,</phrase><phrase role="identifier"> factory</phrase><phrase role="special">);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Note that free function <literal>factory</literal> is being used to generate new
|
|
instances of class <literal>Derived</literal>. In such cases, we use
|
|
<literal>return_value_policy<manage_new_object></literal> to instruct Python to adopt
|
|
the pointer to <literal>Base</literal> and hold the instance in a new Python <literal>Base</literal>
|
|
object until the the Python object is destroyed. We shall see more of
|
|
Boost.Python <link linkend="python.call_policies">call policies</link> later.</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="comment">// Tell Python to take ownership of factory's result
|
|
</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"factory"</phrase><phrase role="special">,</phrase><phrase role="identifier"> factory</phrase><phrase role="special">,</phrase><phrase role="identifier">
|
|
return_value_policy</phrase><phrase role="special"><</phrase><phrase role="identifier">manage_new_object</phrase><phrase role="special">>());</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
</section>
|
|
<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>
|
|
<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>
|
|
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">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">
|
|
<tbody>
|
|
<row>
|
|
<entry>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/note.png"></imagedata></imageobject></inlinemediaobject> <emphasis role="bold">member function and methods</emphasis><para/>
|
|
<para/>
|
|
Python, like
|
|
many object oriented languages uses the term <emphasis role="bold">methods</emphasis>. Methods
|
|
correspond roughly to C++'s <emphasis role="bold">member functions</emphasis></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
</section>
|
|
<section id="python.virtual_functions_with_default_implementations">
|
|
<title>Virtual Functions with Default Implementations</title>
|
|
<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">
|
|
{</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>
|
|
had a pure virtual function <literal>f</literal>. If, however, its member function <literal>f</literal> was
|
|
not declared as pure virtual:</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="keyword"> return</phrase><phrase role="number"> 0</phrase><phrase role="special">;</phrase><phrase role="special"> }</phrase><phrase role="special">
|
|
};</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<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"> 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>
|
|
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">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>
|
|
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>
|
|
<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="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><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>base.f()</literal>:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> base</phrase><phrase role="special">.</phrase><phrase role="identifier">f</phrase><phrase role="special">()</phrase><phrase role="number">
|
|
0</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>
|
|
</section>
|
|
<section id="python.class_operators_special_functions">
|
|
<title>Class Operators/Special Functions</title>
|
|
<anchor id="class_operators_special_functions.python_operators" /><bridgehead renderas="sect2">Python Operators</bridgehead><para>
|
|
C is well known for the abundance of operators. C++ extends this to the
|
|
extremes by allowing operator overloading. Boost.Python takes advantage of
|
|
this and makes it easy to wrap C++ operator-powered classes.</para>
|
|
<para>
|
|
Consider a file position class <literal>FilePos</literal> and a set of operators that take
|
|
on FilePos instances:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">class</phrase><phrase role="identifier"> FilePos</phrase><phrase role="special"> {</phrase><phrase role="comment"> /*...*/</phrase><phrase role="special"> };</phrase><phrase role="identifier">
|
|
|
|
FilePos</phrase><phrase role="keyword"> operator</phrase><phrase role="special">+(</phrase><phrase role="identifier">FilePos</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
FilePos</phrase><phrase role="keyword"> operator</phrase><phrase role="special">+(</phrase><phrase role="keyword">int</phrase><phrase role="special">,</phrase><phrase role="identifier"> FilePos</phrase><phrase role="special">);</phrase><phrase role="keyword">
|
|
int</phrase><phrase role="keyword"> operator</phrase><phrase role="special">-(</phrase><phrase role="identifier">FilePos</phrase><phrase role="special">,</phrase><phrase role="identifier"> FilePos</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
FilePos</phrase><phrase role="keyword"> operator</phrase><phrase role="special">-(</phrase><phrase role="identifier">FilePos</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
FilePos</phrase><phrase role="special">&</phrase><phrase role="keyword"> operator</phrase><phrase role="special">+=(</phrase><phrase role="identifier">FilePos</phrase><phrase role="special">&,</phrase><phrase role="keyword"> int</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
FilePos</phrase><phrase role="special">&</phrase><phrase role="keyword"> operator</phrase><phrase role="special">-=(</phrase><phrase role="identifier">FilePos</phrase><phrase role="special">&,</phrase><phrase role="keyword"> int</phrase><phrase role="special">);</phrase><phrase role="keyword">
|
|
bool</phrase><phrase role="keyword"> operator</phrase><phrase role="special"><(</phrase><phrase role="identifier">FilePos</phrase><phrase role="special">,</phrase><phrase role="identifier"> FilePos</phrase><phrase role="special">);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
The class and the various operators can be mapped to Python rather easily
|
|
and intuitively:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">FilePos</phrase><phrase role="special">>(</phrase><phrase role="string">"FilePos"</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special"> +</phrase><phrase role="keyword"> int</phrase><phrase role="special">())</phrase><phrase role="comment"> // __add__
|
|
</phrase><phrase role="special"> .</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="keyword">int</phrase><phrase role="special">()</phrase><phrase role="special"> +</phrase><phrase role="identifier"> self</phrase><phrase role="special">)</phrase><phrase role="comment"> // __radd__
|
|
</phrase><phrase role="special"> .</phrase><phrase role="identifier">def</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="comment"> // __sub__
|
|
</phrase><phrase role="special"> .</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special"> -</phrase><phrase role="keyword"> int</phrase><phrase role="special">())</phrase><phrase role="comment"> // __sub__
|
|
</phrase><phrase role="special"> .</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special"> +=</phrase><phrase role="keyword"> int</phrase><phrase role="special">())</phrase><phrase role="comment"> // __iadd__
|
|
</phrase><phrase role="special"> .</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special"> -=</phrase><phrase role="identifier"> other</phrase><phrase role="special"><</phrase><phrase role="keyword">int</phrase><phrase role="special">>())</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</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="comment"> // __lt__
|
|
</phrase></literal>
|
|
</programlisting>
|
|
<para>
|
|
The code snippet above is very clear and needs almost no explanation at
|
|
all. It is virtually the same as the operators' signatures. Just take
|
|
note that <literal>self</literal> refers to FilePos object. Also, not every class <literal>T</literal> that
|
|
you might need to interact with in an operator expression is (cheaply)
|
|
default-constructible. You can use <literal>other<T>()</literal> in place of an actual
|
|
<literal>T</literal> instance when writing "self expressions".</para>
|
|
<anchor id="class_operators_special_functions.special_methods" /><bridgehead renderas="sect2">Special Methods</bridgehead><para>
|
|
Python has a few more <emphasis>Special Methods</emphasis>. Boost.Python supports all of the
|
|
standard special method names supported by real Python class instances. A
|
|
similar set of intuitive interfaces can also be used to wrap C++ functions
|
|
that correspond to these Python <emphasis>special functions</emphasis>. Example:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">class</phrase><phrase role="identifier"> Rational</phrase><phrase role="special">
|
|
{</phrase><phrase role="keyword"> operator</phrase><phrase role="keyword"> double</phrase><phrase role="special">()</phrase><phrase role="keyword"> const</phrase><phrase role="special">;</phrase><phrase role="special"> };</phrase><phrase role="identifier">
|
|
|
|
Rational</phrase><phrase role="identifier"> pow</phrase><phrase role="special">(</phrase><phrase role="identifier">Rational</phrase><phrase role="special">,</phrase><phrase role="identifier"> Rational</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
Rational</phrase><phrase role="identifier"> abs</phrase><phrase role="special">(</phrase><phrase role="identifier">Rational</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
ostream</phrase><phrase role="special">&</phrase><phrase role="keyword"> operator</phrase><phrase role="special"><<(</phrase><phrase role="identifier">ostream</phrase><phrase role="special">&,</phrase><phrase role="identifier">Rational</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
|
|
class_</phrase><phrase role="special"><</phrase><phrase role="identifier">Rational</phrase><phrase role="special">>()</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="identifier">float_</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special">))</phrase><phrase role="comment"> // __float__
|
|
</phrase><phrase role="special"> .</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="identifier">pow</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special">,</phrase><phrase role="identifier"> other</phrase><phrase role="special"><</phrase><phrase role="identifier">Rational</phrase><phrase role="special">>))</phrase><phrase role="comment"> // __pow__
|
|
</phrase><phrase role="special"> .</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="identifier">abs</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special">))</phrase><phrase role="comment"> // __abs__
|
|
</phrase><phrase role="special"> .</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="identifier">str</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special">))</phrase><phrase role="comment"> // __str__
|
|
</phrase><phrase role="special"> ;</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Need we say more?</para>
|
|
<informaltable frame="all">
|
|
<?dbhtml table-width="74%" ?>
|
|
<tgroup cols="1">
|
|
<tbody>
|
|
<row>
|
|
<entry>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/note.png"></imagedata></imageobject></inlinemediaobject> What is the business of <literal>operator<<</literal> <literal>.def(str(self))</literal>?
|
|
Well, the method <literal>str</literal> requires the <literal>operator<<</literal> to do its work (i.e.
|
|
<literal>operator<<</literal> is used by the method defined by def(str(self)).</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
</section></section>
|
|
<section id="python.functions">
|
|
<title>Functions</title>
|
|
<para>
|
|
In this chapter, we'll look at Boost.Python powered functions in closer
|
|
detail. We shall see some facilities to make exposing C++ functions to
|
|
Python safe from potential pifalls such as dangling pointers and
|
|
references. We shall also see facilities that will make it even easier for
|
|
us to expose C++ functions that take advantage of C++ features such as
|
|
overloading and default arguments.</para>
|
|
<blockquote><para><emphasis>Read on...</emphasis></para></blockquote><para>
|
|
But before you do, you might want to fire up Python 2.2 or later and type
|
|
<literal>>>> import this</literal>.</para>
|
|
<programlisting><literal> >>> import this
|
|
The Zen of Python, by Tim Peters
|
|
Beautiful is better than ugly.
|
|
Explicit is better than implicit.
|
|
Simple is better than complex.
|
|
Complex is better than complicated.
|
|
Flat is better than nested.
|
|
Sparse is better than dense.
|
|
Readability counts.
|
|
Special cases aren't special enough to break the rules.
|
|
Although practicality beats purity.
|
|
Errors should never pass silently.
|
|
Unless explicitly silenced.
|
|
In the face of ambiguity, refuse the temptation to guess.
|
|
There should be one-- and preferably only one --obvious way to do it
|
|
Although that way may not be obvious at first unless you're Dutch.
|
|
Now is better than never.
|
|
Although never is often better than <emphasis role="bold">right</emphasis> now.
|
|
If the implementation is hard to explain, it's a bad idea.
|
|
If the implementation is easy to explain, it may be a good idea.
|
|
Namespaces are one honking great idea -- let's do more of those!
|
|
</literal></programlisting>
|
|
<section id="python.call_policies">
|
|
<title>Call Policies</title>
|
|
<para>
|
|
In C++, we often deal with arguments and return types such as pointers
|
|
and references. Such primitive types are rather, ummmm, low level and
|
|
they really don't tell us much. At the very least, we don't know the
|
|
owner of the pointer or the referenced object. No wonder languages
|
|
such as Java and Python never deal with such low level entities. In
|
|
C++, it's usually considered a good practice to use smart pointers
|
|
which exactly describe ownership semantics. Still, even good C++
|
|
interfaces use raw references and pointers sometimes, so Boost.Python
|
|
must deal with them. To do this, it may need your help. Consider the
|
|
following C++ function:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">X</phrase><phrase role="special">&</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">Y</phrase><phrase role="special">&</phrase><phrase role="identifier"> y</phrase><phrase role="special">,</phrase><phrase role="identifier"> Z</phrase><phrase role="special">*</phrase><phrase role="identifier"> z</phrase><phrase role="special">);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
How should the library wrap this function? A naive approach builds a
|
|
Python X object around result reference. This strategy might or might
|
|
not work out. Here's an example where it didn't</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> x</phrase><phrase role="special"> =</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">y</phrase><phrase role="special">,</phrase><phrase role="identifier"> z</phrase><phrase role="special">)</phrase> #<phrase role="identifier"> x</phrase><phrase role="identifier"> refers</phrase><phrase role="identifier"> to</phrase><phrase role="identifier"> some</phrase><phrase role="identifier"> C</phrase><phrase role="special">++</phrase><phrase role="identifier"> X</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> del</phrase><phrase role="identifier"> y</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">some_method</phrase><phrase role="special">()</phrase> #<phrase role="identifier"> CRASH</phrase><phrase role="special">!</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
What's the problem?</para>
|
|
<para>
|
|
Well, what if f() was implemented as shown below:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">X</phrase><phrase role="special">&</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">Y</phrase><phrase role="special">&</phrase><phrase role="identifier"> y</phrase><phrase role="special">,</phrase><phrase role="identifier"> Z</phrase><phrase role="special">*</phrase><phrase role="identifier"> z</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
y</phrase><phrase role="special">.</phrase><phrase role="identifier">z</phrase><phrase role="special"> =</phrase><phrase role="identifier"> z</phrase><phrase role="special">;</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="identifier"> y</phrase><phrase role="special">.</phrase><phrase role="identifier">x</phrase><phrase role="special">;</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
The problem is that the lifetime of result X& is tied to the lifetime
|
|
of y, because the f() returns a reference to a member of the y
|
|
object. This idiom is is not uncommon and perfectly acceptable in the
|
|
context of C++. However, Python users should not be able to crash the
|
|
system just by using our C++ interface. In this case deleting y will
|
|
invalidate the reference to X. We have a dangling reference.</para>
|
|
<para>
|
|
Here's what's happening:</para>
|
|
<orderedlist>
|
|
<listitem>
|
|
<literal>f</literal> is called passing in a reference to <literal>y</literal> and a pointer to <literal>z</literal>
|
|
</listitem><listitem>
|
|
A reference to <literal>y.x</literal> is returned
|
|
</listitem><listitem>
|
|
<literal>y</literal> is deleted. <literal>x</literal> is a dangling reference
|
|
</listitem><listitem>
|
|
<literal>x.some_method()</literal> is called
|
|
</listitem><listitem>
|
|
<emphasis role="bold">BOOM!</emphasis>
|
|
</listitem>
|
|
</orderedlist><para>
|
|
We could copy result into a new object:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">y</phrase><phrase role="special">,</phrase><phrase role="identifier"> z</phrase><phrase role="special">).</phrase><phrase role="identifier">set</phrase><phrase role="special">(</phrase><phrase role="number">42</phrase><phrase role="special">)</phrase> #<phrase role="identifier"> Result</phrase><phrase role="identifier"> disappears</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> y</phrase><phrase role="special">.</phrase><phrase role="identifier">x</phrase><phrase role="special">.</phrase><phrase role="identifier">get</phrase><phrase role="special">()</phrase> #<phrase role="identifier"> No</phrase><phrase role="identifier"> crash</phrase><phrase role="special">,</phrase><phrase role="identifier"> but</phrase><phrase role="identifier"> still</phrase><phrase role="identifier"> bad</phrase><phrase role="number">
|
|
3.14</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
This is not really our intent of our C++ interface. We've broken our
|
|
promise that the Python interface should reflect the C++ interface as
|
|
closely as possible.</para>
|
|
<para>
|
|
Our problems do not end there. Suppose Y is implemented as follows:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">struct</phrase><phrase role="identifier"> Y</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
X</phrase><phrase role="identifier"> x</phrase><phrase role="special">;</phrase><phrase role="identifier"> Z</phrase><phrase role="special">*</phrase><phrase role="identifier"> z</phrase><phrase role="special">;</phrase><phrase role="keyword">
|
|
int</phrase><phrase role="identifier"> z_value</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword"> return</phrase><phrase role="identifier"> z</phrase><phrase role="special">-></phrase><phrase role="identifier">value</phrase><phrase role="special">();</phrase><phrase role="special"> }</phrase><phrase role="special">
|
|
};</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Notice that the data member <literal>z</literal> is held by class Y using a raw
|
|
pointer. Now we have a potential dangling pointer problem inside Y:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> x</phrase><phrase role="special"> =</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">y</phrase><phrase role="special">,</phrase><phrase role="identifier"> z</phrase><phrase role="special">)</phrase> #<phrase role="identifier"> y</phrase><phrase role="identifier"> refers</phrase><phrase role="identifier"> to</phrase><phrase role="identifier"> z</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> del</phrase><phrase role="identifier"> z</phrase> #<phrase role="identifier"> Kill</phrase><phrase role="identifier"> the</phrase><phrase role="identifier"> z</phrase><phrase role="identifier"> object</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> y</phrase><phrase role="special">.</phrase><phrase role="identifier">z_value</phrase><phrase role="special">()</phrase> #<phrase role="identifier"> CRASH</phrase><phrase role="special">!</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
For reference, here's the implementation of <literal>f</literal> again:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">X</phrase><phrase role="special">&</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">Y</phrase><phrase role="special">&</phrase><phrase role="identifier"> y</phrase><phrase role="special">,</phrase><phrase role="identifier"> Z</phrase><phrase role="special">*</phrase><phrase role="identifier"> z</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
y</phrase><phrase role="special">.</phrase><phrase role="identifier">z</phrase><phrase role="special"> =</phrase><phrase role="identifier"> z</phrase><phrase role="special">;</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="identifier"> y</phrase><phrase role="special">.</phrase><phrase role="identifier">x</phrase><phrase role="special">;</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Here's what's happening:</para>
|
|
<orderedlist>
|
|
<listitem>
|
|
<literal>f</literal> is called passing in a reference to <literal>y</literal> and a pointer to <literal>z</literal>
|
|
</listitem><listitem>
|
|
A pointer to <literal>z</literal> is held by <literal>y</literal>
|
|
</listitem><listitem>
|
|
A reference to <literal>y.x</literal> is returned
|
|
</listitem><listitem>
|
|
<literal>z</literal> is deleted. <literal>y.z</literal> is a dangling pointer
|
|
</listitem><listitem>
|
|
<literal>y.z_value()</literal> is called
|
|
</listitem><listitem>
|
|
<literal>z->value()</literal> is called
|
|
</listitem><listitem>
|
|
<emphasis role="bold">BOOM!</emphasis>
|
|
</listitem>
|
|
</orderedlist><anchor id="call_policies.call_policies" /><bridgehead renderas="sect2">Call Policies</bridgehead><para>
|
|
Call Policies may be used in situations such as the example detailed above.
|
|
In our example, <literal>return_internal_reference</literal> and <literal>with_custodian_and_ward</literal>
|
|
are our friends:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"f"</phrase><phrase role="special">,</phrase><phrase role="identifier"> f</phrase><phrase role="special">,</phrase><phrase role="identifier">
|
|
return_internal_reference</phrase><phrase role="special"><</phrase><phrase role="number">1</phrase><phrase role="special">,</phrase><phrase role="identifier">
|
|
with_custodian_and_ward</phrase><phrase role="special"><</phrase><phrase role="number">1</phrase><phrase role="special">,</phrase><phrase role="number"> 2</phrase><phrase role="special">></phrase><phrase role="special"> >());</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
What are the <literal>1</literal> and <literal>2</literal> parameters, you ask?</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">return_internal_reference</phrase><phrase role="special"><</phrase><phrase role="number">1</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Informs Boost.Python that the first argument, in our case <literal>Y& y</literal>, is the
|
|
owner of the returned reference: <literal>X&</literal>. The "<literal>1</literal>" simply specifies the
|
|
first argument. In short: "return an internal reference <literal>X&</literal> owned by the
|
|
1st argument <literal>Y& y</literal>".</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">with_custodian_and_ward</phrase><phrase role="special"><</phrase><phrase role="number">1</phrase><phrase role="special">,</phrase><phrase role="number"> 2</phrase><phrase role="special">></phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Informs Boost.Python that the lifetime of the argument indicated by ward
|
|
(i.e. the 2nd argument: <literal>Z* z</literal>) is dependent on the lifetime of the
|
|
argument indicated by custodian (i.e. the 1st argument: <literal>Y& y</literal>).</para>
|
|
<para>
|
|
It is also important to note that we have defined two policies above. Two
|
|
or more policies can be composed by chaining. Here's the general syntax:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">policy1</phrase><phrase role="special"><</phrase><phrase role="identifier">args</phrase><phrase role="special">...,</phrase><phrase role="identifier">
|
|
policy2</phrase><phrase role="special"><</phrase><phrase role="identifier">args</phrase><phrase role="special">...,</phrase><phrase role="identifier">
|
|
policy3</phrase><phrase role="special"><</phrase><phrase role="identifier">args</phrase><phrase role="special">...></phrase><phrase role="special"> ></phrase><phrase role="special"> ></phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Here is the list of predefined call policies. A complete reference detailing
|
|
these can be found <ulink url="../../../v2/reference.html#models_of_call_policies">here</ulink>.</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<emphasis role="bold">with_custodian_and_ward</emphasis><para/>
|
|
Ties lifetimes of the arguments
|
|
</listitem><listitem>
|
|
<emphasis role="bold">with_custodian_and_ward_postcall</emphasis><para/>
|
|
Ties lifetimes of the arguments and results
|
|
</listitem><listitem>
|
|
<emphasis role="bold">return_internal_reference</emphasis><para/>
|
|
Ties lifetime of one argument to that of result
|
|
</listitem><listitem>
|
|
<emphasis role="bold">return_value_policy<T> with T one of:</emphasis><para/>
|
|
|
|
</listitem><listitem>
|
|
<emphasis role="bold">reference_existing_object</emphasis><para/>
|
|
naive (dangerous) approach
|
|
</listitem><listitem>
|
|
<emphasis role="bold">copy_const_reference</emphasis><para/>
|
|
Boost.Python v1 approach
|
|
</listitem><listitem>
|
|
<emphasis role="bold">copy_non_const_reference</emphasis><para/>
|
|
|
|
</listitem><listitem>
|
|
<emphasis role="bold">manage_new_object</emphasis><para/>
|
|
Adopt a pointer and hold the instance
|
|
</listitem>
|
|
</itemizedlist><informaltable frame="all">
|
|
<?dbhtml table-width="74%" ?>
|
|
<tgroup cols="1">
|
|
<tbody>
|
|
<row>
|
|
<entry>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/smiley.png"></imagedata></imageobject></inlinemediaobject> <emphasis role="bold">Remember the Zen, Luke:</emphasis><para/>
|
|
<para/>
|
|
|
|
"Explicit is better than implicit"<para/>
|
|
|
|
"In the face of ambiguity, refuse the temptation to guess"<para/>
|
|
</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
</section>
|
|
<section id="python.overloading">
|
|
<title>Overloading</title>
|
|
<para>
|
|
The following illustrates a scheme for manually wrapping an overloaded
|
|
member functions. Of course, the same technique can be applied to wrapping
|
|
overloaded non-member functions.</para>
|
|
<para>
|
|
We have here our C++ class:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">struct</phrase><phrase role="identifier"> X</phrase><phrase role="special">
|
|
{</phrase><phrase role="keyword">
|
|
bool</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="keyword">int</phrase><phrase role="identifier"> a</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="keyword"> true</phrase><phrase role="special">;</phrase><phrase role="special">
|
|
}</phrase><phrase role="keyword">
|
|
|
|
bool</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="keyword">int</phrase><phrase role="identifier"> a</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="identifier"> b</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="keyword"> true</phrase><phrase role="special">;</phrase><phrase role="special">
|
|
}</phrase><phrase role="keyword">
|
|
|
|
bool</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="keyword">int</phrase><phrase role="identifier"> a</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="identifier"> b</phrase><phrase role="special">,</phrase><phrase role="keyword"> char</phrase><phrase role="identifier"> c</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="keyword"> true</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="keyword">int</phrase><phrase role="identifier"> a</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="identifier"> b</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="identifier"> c</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="identifier"> a</phrase><phrase role="special"> +</phrase><phrase role="identifier"> b</phrase><phrase role="special"> +</phrase><phrase role="identifier"> c</phrase><phrase role="special">;</phrase><phrase role="special">
|
|
};</phrase><phrase role="special">
|
|
};</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Class X has 4 overloaded functions. We shall start by introducing some
|
|
member function pointer variables:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">bool</phrase><phrase role="special"> (</phrase><phrase role="identifier">X</phrase><phrase role="special">::*</phrase><phrase role="identifier">fx1</phrase><phrase role="special">)(</phrase><phrase role="keyword">int</phrase><phrase role="special">)</phrase><phrase role="special"> =</phrase><phrase role="special"> &</phrase><phrase role="identifier">X</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">;</phrase><phrase role="keyword">
|
|
bool</phrase><phrase role="special"> (</phrase><phrase role="identifier">X</phrase><phrase role="special">::*</phrase><phrase role="identifier">fx2</phrase><phrase role="special">)(</phrase><phrase role="keyword">int</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="special">)</phrase><phrase role="special"> =</phrase><phrase role="special"> &</phrase><phrase role="identifier">X</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">;</phrase><phrase role="keyword">
|
|
bool</phrase><phrase role="special"> (</phrase><phrase role="identifier">X</phrase><phrase role="special">::*</phrase><phrase role="identifier">fx3</phrase><phrase role="special">)(</phrase><phrase role="keyword">int</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="special">,</phrase><phrase role="keyword"> char</phrase><phrase role="special">)=</phrase><phrase role="special"> &</phrase><phrase role="identifier">X</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">;</phrase><phrase role="keyword">
|
|
int</phrase><phrase role="special"> (</phrase><phrase role="identifier">X</phrase><phrase role="special">::*</phrase><phrase role="identifier">fx4</phrase><phrase role="special">)(</phrase><phrase role="keyword">int</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="special">)</phrase><phrase role="special"> =</phrase><phrase role="special"> &</phrase><phrase role="identifier">X</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">;</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
With these in hand, we can proceed to define and wrap this for Python:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<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"> fx1</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"> fx2</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"> fx3</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"> fx4</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
</section>
|
|
<section id="python.default_arguments">
|
|
<title>Default Arguments</title>
|
|
<para>
|
|
Boost.Python wraps (member) function pointers. Unfortunately, C++ function
|
|
pointers carry no default argument info. Take a function <literal>f</literal> with default
|
|
arguments:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">int</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="keyword">int</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="special"> =</phrase><phrase role="number"> 3.14</phrase><phrase role="special">,</phrase><phrase role="keyword"> char</phrase><phrase role="keyword"> const</phrase><phrase role="special">*</phrase><phrase role="special"> =</phrase><phrase role="string"> "hello"</phrase><phrase role="special">);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
But the type of a pointer to the function <literal>f</literal> has no information
|
|
about its default arguments:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">int</phrase><phrase role="special">(*</phrase><phrase role="identifier">g</phrase><phrase role="special">)(</phrase><phrase role="keyword">int</phrase><phrase role="special">,</phrase><phrase role="keyword">double</phrase><phrase role="special">,</phrase><phrase role="keyword">char</phrase><phrase role="keyword"> const</phrase><phrase role="special">*)</phrase><phrase role="special"> =</phrase><phrase role="identifier"> f</phrase><phrase role="special">;</phrase><phrase role="comment"> // defaults lost!
|
|
</phrase></literal>
|
|
</programlisting>
|
|
<para>
|
|
When we pass this function pointer to the <literal>def</literal> function, there is no way
|
|
to retrieve the default arguments:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"f"</phrase><phrase role="special">,</phrase><phrase role="identifier"> f</phrase><phrase role="special">);</phrase><phrase role="comment"> // defaults lost!
|
|
</phrase></literal>
|
|
</programlisting>
|
|
<para>
|
|
Because of this, when wrapping C++ code, we had to resort to manual
|
|
wrapping as outlined in the <link linkend="python.overloading">previous section</link>, or
|
|
writing thin wrappers:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="comment">// write "thin wrappers"
|
|
</phrase><phrase role="keyword">int</phrase><phrase role="identifier"> f1</phrase><phrase role="special">(</phrase><phrase role="keyword">int</phrase><phrase role="identifier"> x</phrase><phrase role="special">)</phrase><phrase role="special"> {</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">x</phrase><phrase role="special">);</phrase><phrase role="special"> }</phrase><phrase role="keyword">
|
|
int</phrase><phrase role="identifier"> f2</phrase><phrase role="special">(</phrase><phrase role="keyword">int</phrase><phrase role="identifier"> x</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="identifier"> y</phrase><phrase role="special">)</phrase><phrase role="special"> {</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">x</phrase><phrase role="special">,</phrase><phrase role="identifier">y</phrase><phrase role="special">);</phrase><phrase role="special"> }</phrase><phrase role="comment">
|
|
|
|
/*...*/
|
|
|
|
// in module init
|
|
</phrase><phrase role="identifier"> def</phrase><phrase role="special">(</phrase><phrase role="string">"f"</phrase><phrase role="special">,</phrase><phrase role="identifier"> f</phrase><phrase role="special">);</phrase><phrase role="comment"> // all arguments
|
|
</phrase><phrase role="identifier"> def</phrase><phrase role="special">(</phrase><phrase role="string">"f"</phrase><phrase role="special">,</phrase><phrase role="identifier"> f2</phrase><phrase role="special">);</phrase><phrase role="comment"> // two arguments
|
|
</phrase><phrase role="identifier"> def</phrase><phrase role="special">(</phrase><phrase role="string">"f"</phrase><phrase role="special">,</phrase><phrase role="identifier"> f1</phrase><phrase role="special">);</phrase><phrase role="comment"> // one argument
|
|
</phrase></literal>
|
|
</programlisting>
|
|
<para>
|
|
When you want to wrap functions (or member functions) that either:</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
have default arguments, or
|
|
</listitem><listitem>
|
|
are overloaded with a common sequence of initial arguments
|
|
</listitem>
|
|
</itemizedlist><anchor id="default_arguments.boost_python_function_overloads" /><bridgehead renderas="sect2">BOOST_PYTHON_FUNCTION_OVERLOADS</bridgehead><para>
|
|
Boost.Python now has a way to make it easier. For instance, given a function:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">int</phrase><phrase role="identifier"> foo</phrase><phrase role="special">(</phrase><phrase role="keyword">int</phrase><phrase role="identifier"> a</phrase><phrase role="special">,</phrase><phrase role="keyword"> char</phrase><phrase role="identifier"> b</phrase><phrase role="special"> =</phrase><phrase role="number"> 1</phrase><phrase role="special">,</phrase><phrase role="keyword"> unsigned</phrase><phrase role="identifier"> c</phrase><phrase role="special"> =</phrase><phrase role="number"> 2</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="identifier"> d</phrase><phrase role="special"> =</phrase><phrase role="number"> 3</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
/*...*/</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
The macro invocation:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">BOOST_PYTHON_FUNCTION_OVERLOADS</phrase><phrase role="special">(</phrase><phrase role="identifier">foo_overloads</phrase><phrase role="special">,</phrase><phrase role="identifier"> foo</phrase><phrase role="special">,</phrase><phrase role="number"> 1</phrase><phrase role="special">,</phrase><phrase role="number"> 4</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
will automatically create the thin wrappers for us. This macro will create
|
|
a class <literal>foo_overloads</literal> that can be passed on to <literal>def(...)</literal>. The third
|
|
and fourth macro argument are the minimum arguments and maximum arguments,
|
|
respectively. In our <literal>foo</literal> function the minimum number of arguments is 1
|
|
and the maximum number of arguments is 4. The <literal>def(...)</literal> function will
|
|
automatically add all the foo variants for us:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"foo"</phrase><phrase role="special">,</phrase><phrase role="identifier"> foo</phrase><phrase role="special">,</phrase><phrase role="identifier"> foo_overloads</phrase><phrase role="special">());</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<anchor id="default_arguments.boost_python_member_function_overloads" /><bridgehead renderas="sect2">BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS</bridgehead><para>
|
|
Objects here, objects there, objects here there everywhere. More frequently
|
|
than anything else, we need to expose member functions of our classes to
|
|
Python. Then again, we have the same inconveniences as before when default
|
|
arguments or overloads with a common sequence of initial arguments come
|
|
into play. Another macro is provided to make this a breeze.</para>
|
|
<para>
|
|
Like <literal>BOOST_PYTHON_FUNCTION_OVERLOADS</literal>,
|
|
<literal>BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS</literal> may be used to automatically create
|
|
the thin wrappers for wrapping member functions. Let's have an example:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">struct</phrase><phrase role="identifier"> george</phrase><phrase role="special">
|
|
{</phrase><phrase role="keyword">
|
|
void</phrase><phrase role="identifier">
|
|
wack_em</phrase><phrase role="special">(</phrase><phrase role="keyword">int</phrase><phrase role="identifier"> a</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="identifier"> b</phrase><phrase role="special"> =</phrase><phrase role="number"> 0</phrase><phrase role="special">,</phrase><phrase role="keyword"> char</phrase><phrase role="identifier"> c</phrase><phrase role="special"> =</phrase><phrase role="char"> 'x'</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
/*...*/</phrase><phrase role="special">
|
|
}</phrase><phrase role="special">
|
|
};</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
The macro invocation:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS</phrase><phrase role="special">(</phrase><phrase role="identifier">george_overloads</phrase><phrase role="special">,</phrase><phrase role="identifier"> wack_em</phrase><phrase role="special">,</phrase><phrase role="number"> 1</phrase><phrase role="special">,</phrase><phrase role="number"> 3</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
will generate a set of thin wrappers for george's <literal>wack_em</literal> member function
|
|
accepting a minimum of 1 and a maximum of 3 arguments (i.e. the third and
|
|
fourth macro argument). The thin wrappers are all enclosed in a class named
|
|
<literal>george_overloads</literal> that can then be used as an argument to <literal>def(...)</literal>:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"wack_em"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">george</phrase><phrase role="special">::</phrase><phrase role="identifier">wack_em</phrase><phrase role="special">,</phrase><phrase role="identifier"> george_overloads</phrase><phrase role="special">());</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
See the <ulink url="../../../v2/overloads.html#BOOST_PYTHON_FUNCTION_OVERLOADS-spec">overloads reference</ulink>
|
|
for details.</para>
|
|
<anchor id="default_arguments.init_and_optional" /><bridgehead renderas="sect2">init and optional</bridgehead><para>
|
|
A similar facility is provided for class constructors, again, with
|
|
default arguments or a sequence of overloads. Remember <literal>init<...></literal>? For example,
|
|
given a class X with a constructor:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">struct</phrase><phrase role="identifier"> X</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
X</phrase><phrase role="special">(</phrase><phrase role="keyword">int</phrase><phrase role="identifier"> a</phrase><phrase role="special">,</phrase><phrase role="keyword"> char</phrase><phrase role="identifier"> b</phrase><phrase role="special"> =</phrase><phrase role="char"> 'D'</phrase><phrase role="special">,</phrase><phrase role="identifier"> std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="identifier"> c</phrase><phrase role="special"> =</phrase><phrase role="string"> "constructor"</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="identifier"> d</phrase><phrase role="special"> =</phrase><phrase role="number"> 0.0</phrase><phrase role="special">);</phrase><phrase role="comment">
|
|
/*...*/</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
You can easily add this constructor to Boost.Python in one shot:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="identifier">init</phrase><phrase role="special"><</phrase><phrase role="keyword">int</phrase><phrase role="special">,</phrase><phrase role="identifier"> optional</phrase><phrase role="special"><</phrase><phrase role="keyword">char</phrase><phrase role="special">,</phrase><phrase role="identifier"> std</phrase><phrase role="special">::</phrase><phrase role="identifier">string</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="special">></phrase><phrase role="special"> >())</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Notice the use of <literal>init<...></literal> and <literal>optional<...></literal> to signify the default
|
|
(optional arguments).</para>
|
|
</section>
|
|
<section id="python.auto_overloading">
|
|
<title>Auto-Overloading</title>
|
|
<para>
|
|
It was mentioned in passing in the previous section that
|
|
<literal>BOOST_PYTHON_FUNCTION_OVERLOADS</literal> and <literal>BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS</literal>
|
|
can also be used for overloaded functions and member functions with a
|
|
common sequence of initial arguments. Here is an example:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">void</phrase><phrase role="identifier"> foo</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
/*...*/</phrase><phrase role="special">
|
|
}</phrase><phrase role="keyword">
|
|
|
|
void</phrase><phrase role="identifier"> foo</phrase><phrase role="special">(</phrase><phrase role="keyword">bool</phrase><phrase role="identifier"> a</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
/*...*/</phrase><phrase role="special">
|
|
}</phrase><phrase role="keyword">
|
|
|
|
void</phrase><phrase role="identifier"> foo</phrase><phrase role="special">(</phrase><phrase role="keyword">bool</phrase><phrase role="identifier"> a</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="identifier"> b</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
/*...*/</phrase><phrase role="special">
|
|
}</phrase><phrase role="keyword">
|
|
|
|
void</phrase><phrase role="identifier"> foo</phrase><phrase role="special">(</phrase><phrase role="keyword">bool</phrase><phrase role="identifier"> a</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="identifier"> b</phrase><phrase role="special">,</phrase><phrase role="keyword"> char</phrase><phrase role="identifier"> c</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
/*...*/</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Like in the previous section, we can generate thin wrappers for these
|
|
overloaded functions in one-shot:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">BOOST_PYTHON_FUNCTION_OVERLOADS</phrase><phrase role="special">(</phrase><phrase role="identifier">foo_overloads</phrase><phrase role="special">,</phrase><phrase role="identifier"> foo</phrase><phrase role="special">,</phrase><phrase role="number"> 0</phrase><phrase role="special">,</phrase><phrase role="number"> 3</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Then...</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"foo"</phrase><phrase role="special">,</phrase><phrase role="identifier"> foo</phrase><phrase role="special">,</phrase><phrase role="identifier"> foo_overloads</phrase><phrase role="special">());</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Notice though that we have a situation now where we have a minimum of zero
|
|
(0) arguments and a maximum of 3 arguments.</para>
|
|
<anchor id="auto_overloading.manual_wrapping" /><bridgehead renderas="sect2">Manual Wrapping</bridgehead><para>
|
|
It is important to emphasize however that <emphasis role="bold">the overloaded functions must
|
|
have a common sequence of initial arguments</emphasis>. Otherwise, our scheme above
|
|
will not work. If this is not the case, we have to wrap our functions
|
|
<link linkend="python.overloading">manually</link>.</para>
|
|
<para>
|
|
Actually, we can mix and match manual wrapping of overloaded functions and
|
|
automatic wrapping through <literal>BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS</literal> and
|
|
its sister, <literal>BOOST_PYTHON_FUNCTION_OVERLOADS</literal>. Following up on our example
|
|
presented in the section <link linkend="python.overloading">on overloading</link>, since the
|
|
first 4 overload functins have a common sequence of initial arguments, we
|
|
can use <literal>BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS</literal> to automatically wrap the
|
|
first three of the <literal>def</literal>s and manually wrap just the last. Here's
|
|
how we'll do this:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS</phrase><phrase role="special">(</phrase><phrase role="identifier">xf_overloads</phrase><phrase role="special">,</phrase><phrase role="identifier"> f</phrase><phrase role="special">,</phrase><phrase role="number"> 1</phrase><phrase role="special">,</phrase><phrase role="number"> 4</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Create a member function pointers as above for both X::f overloads:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">bool</phrase><phrase role="special"> (</phrase><phrase role="identifier">X</phrase><phrase role="special">::*</phrase><phrase role="identifier">fx1</phrase><phrase role="special">)(</phrase><phrase role="keyword">int</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="special">,</phrase><phrase role="keyword"> char</phrase><phrase role="special">)</phrase><phrase role="special"> =</phrase><phrase role="special"> &</phrase><phrase role="identifier">X</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">;</phrase><phrase role="keyword">
|
|
int</phrase><phrase role="special"> (</phrase><phrase role="identifier">X</phrase><phrase role="special">::*</phrase><phrase role="identifier">fx2</phrase><phrase role="special">)(</phrase><phrase role="keyword">int</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="special">)</phrase><phrase role="special"> =</phrase><phrase role="special"> &</phrase><phrase role="identifier">X</phrase><phrase role="special">::</phrase><phrase role="identifier">f</phrase><phrase role="special">;</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Then...</para>
|
|
<programlisting>
|
|
<literal>
|
|
<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"> fx1</phrase><phrase role="special">,</phrase><phrase role="identifier"> xf_overloads</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"> fx2</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
</section></section>
|
|
<section id="python.object">
|
|
<title> Object Interface</title>
|
|
<para>
|
|
Python is dynamically typed, unlike C++ which is statically typed. Python
|
|
variables may hold an integer, a float, list, dict, tuple, str, long etc.,
|
|
among other things. In the viewpoint of Boost.Python and C++, these
|
|
Pythonic variables are just instances of class <literal>object</literal>. We shall see in
|
|
this chapter how to deal with Python objects.</para>
|
|
<para>
|
|
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++ <literal>object</literal>s are as close as possible to Python. This
|
|
should minimize the learning curve significantly.</para>
|
|
<para>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/python.png"></imagedata></imageobject></inlinemediaobject></para>
|
|
|
|
<section id="python.basic_interface">
|
|
<title>Basic Interface</title>
|
|
<para>
|
|
Class <literal>object</literal> wraps <literal>PyObject*</literal>. All the intricacies of dealing with
|
|
<literal>PyObject</literal>s such as managing reference counting are handled by the
|
|
<literal>object</literal> class. C++ object interoperability is seamless. Boost.Python C++
|
|
<literal>object</literal>s can in fact be explicitly constructed from any C++ object.</para>
|
|
<para>
|
|
To illustrate, this Python code snippet:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">def</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">x</phrase><phrase role="special">,</phrase><phrase role="identifier"> y</phrase><phrase role="special">):</phrase><phrase role="keyword">
|
|
if</phrase><phrase role="special"> (</phrase><phrase role="identifier">y</phrase><phrase role="special"> ==</phrase><phrase role="char"> 'foo'</phrase><phrase role="special">):</phrase><phrase role="identifier">
|
|
x</phrase><phrase role="special">[</phrase><phrase role="number">3</phrase><phrase role="special">:</phrase><phrase role="number">7</phrase><phrase role="special">]</phrase><phrase role="special"> =</phrase><phrase role="char"> 'bar'</phrase><phrase role="keyword">
|
|
else</phrase><phrase role="special">:</phrase><phrase role="identifier">
|
|
x</phrase><phrase role="special">.</phrase><phrase role="identifier">items</phrase><phrase role="special"> +=</phrase><phrase role="identifier"> y</phrase><phrase role="special">(</phrase><phrase role="number">3</phrase><phrase role="special">,</phrase><phrase role="identifier"> x</phrase><phrase role="special">)</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="identifier"> x</phrase><phrase role="identifier">
|
|
|
|
def</phrase><phrase role="identifier"> getfunc</phrase><phrase role="special">():</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="identifier"> f</phrase><phrase role="special">;</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Can be rewritten in C++ using Boost.Python facilities this way:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">object</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">object</phrase><phrase role="identifier"> x</phrase><phrase role="special">,</phrase><phrase role="identifier"> object</phrase><phrase role="identifier"> y</phrase><phrase role="special">)</phrase><phrase role="special"> {</phrase><phrase role="keyword">
|
|
if</phrase><phrase role="special"> (</phrase><phrase role="identifier">y</phrase><phrase role="special"> ==</phrase><phrase role="string"> "foo"</phrase><phrase role="special">)</phrase><phrase role="identifier">
|
|
x</phrase><phrase role="special">.</phrase><phrase role="identifier">slice</phrase><phrase role="special">(</phrase><phrase role="number">3</phrase><phrase role="special">,</phrase><phrase role="number">7</phrase><phrase role="special">)</phrase><phrase role="special"> =</phrase><phrase role="string"> "bar"</phrase><phrase role="special">;</phrase><phrase role="keyword">
|
|
else</phrase><phrase role="identifier">
|
|
x</phrase><phrase role="special">.</phrase><phrase role="identifier">attr</phrase><phrase role="special">(</phrase><phrase role="string">"items"</phrase><phrase role="special">)</phrase><phrase role="special"> +=</phrase><phrase role="identifier"> y</phrase><phrase role="special">(</phrase><phrase role="number">3</phrase><phrase role="special">,</phrase><phrase role="identifier"> x</phrase><phrase role="special">);</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="identifier"> x</phrase><phrase role="special">;</phrase><phrase role="special">
|
|
}</phrase><phrase role="identifier">
|
|
object</phrase><phrase role="identifier"> getfunc</phrase><phrase role="special">()</phrase><phrase role="special"> {</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="identifier"> object</phrase><phrase role="special">(</phrase><phrase role="identifier">f</phrase><phrase role="special">);</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Apart from cosmetic differences due to the fact that we are writing the
|
|
code in C++, the look and feel should be immediately apparent to the Python
|
|
coder.</para>
|
|
</section>
|
|
<section id="python.derived_object_types">
|
|
<title>Derived Object types</title>
|
|
<para>
|
|
Boost.Python comes with a set of derived <literal>object</literal> types corresponding to
|
|
that of Python's:</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
list
|
|
</listitem><listitem>
|
|
dict
|
|
</listitem><listitem>
|
|
tuple
|
|
</listitem><listitem>
|
|
str
|
|
</listitem><listitem>
|
|
long_
|
|
</listitem><listitem>
|
|
enum
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
These derived <literal>object</literal> types act like real Python types. For instance:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">str</phrase><phrase role="special">(</phrase><phrase role="number">1</phrase><phrase role="special">)</phrase><phrase role="special"> ==></phrase><phrase role="string"> "1"</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Wherever appropriate, a particular derived <literal>object</literal> has corresponding
|
|
Python type's methods. For instance, <literal>dict</literal> has a <literal>keys()</literal> method:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">d</phrase><phrase role="special">.</phrase><phrase role="identifier">keys</phrase><phrase role="special">()</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
<literal>make_tuple</literal> is provided for declaring <emphasis>tuple literals</emphasis>. Example:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">make_tuple</phrase><phrase role="special">(</phrase><phrase role="number">123</phrase><phrase role="special">,</phrase><phrase role="char"> 'D'</phrase><phrase role="special">,</phrase><phrase role="string"> "Hello, World"</phrase><phrase role="special">,</phrase><phrase role="number"> 0.0</phrase><phrase role="special">);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
In C++, when Boost.Python <literal>object</literal>s are used as arguments to functions,
|
|
subtype matching is required. For example, when a function <literal>f</literal>, as
|
|
declared below, is wrapped, it will only accept instances of Python's
|
|
<literal>str</literal> type and subtypes.</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">void</phrase><phrase role="identifier"> f</phrase><phrase role="special">(</phrase><phrase role="identifier">str</phrase><phrase role="identifier"> name</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
object</phrase><phrase role="identifier"> n2</phrase><phrase role="special"> =</phrase><phrase role="identifier"> name</phrase><phrase role="special">.</phrase><phrase role="identifier">attr</phrase><phrase role="special">(</phrase><phrase role="string">"upper"</phrase><phrase role="special">)();</phrase><phrase role="comment"> // NAME = name.upper()
|
|
</phrase><phrase role="identifier"> str</phrase><phrase role="identifier"> NAME</phrase><phrase role="special"> =</phrase><phrase role="identifier"> name</phrase><phrase role="special">.</phrase><phrase role="identifier">upper</phrase><phrase role="special">();</phrase><phrase role="comment"> // better
|
|
</phrase><phrase role="identifier"> object</phrase><phrase role="identifier"> msg</phrase><phrase role="special"> =</phrase><phrase role="string"> "%s is bigger than %s"</phrase><phrase role="special"> %</phrase><phrase role="identifier"> make_tuple</phrase><phrase role="special">(</phrase><phrase role="identifier">NAME</phrase><phrase role="special">,</phrase><phrase role="identifier">name</phrase><phrase role="special">);</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
In finer detail:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">str</phrase><phrase role="identifier"> NAME</phrase><phrase role="special"> =</phrase><phrase role="identifier"> name</phrase><phrase role="special">.</phrase><phrase role="identifier">upper</phrase><phrase role="special">();</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Illustrates that we provide versions of the str type's methods as C++
|
|
member functions.</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">object</phrase><phrase role="identifier"> msg</phrase><phrase role="special"> =</phrase><phrase role="string"> "%s is bigger than %s"</phrase><phrase role="special"> %</phrase><phrase role="identifier"> make_tuple</phrase><phrase role="special">(</phrase><phrase role="identifier">NAME</phrase><phrase role="special">,</phrase><phrase role="identifier">name</phrase><phrase role="special">);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Demonstrates that you can write the C++ equivalent of <literal>"format" % x,y,z</literal>
|
|
in Python, which is useful since there's no easy way to do that in std C++.</para>
|
|
<para>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/alert.png"></imagedata></imageobject></inlinemediaobject> <emphasis role="bold">Beware</emphasis> the common pitfall of forgetting that the constructors
|
|
of most of Python's mutable types make copies, just as in Python.</para>
|
|
<para>
|
|
Python:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> d</phrase><phrase role="special"> =</phrase><phrase role="identifier"> dict</phrase><phrase role="special">(</phrase><phrase role="identifier">x</phrase><phrase role="special">.</phrase><phrase role="identifier">__dict__</phrase><phrase role="special">)</phrase> #<phrase role="identifier"> copies</phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">__dict__</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> d</phrase><phrase role="special">[</phrase><phrase role="char">'whatever'</phrase><phrase role="special">]</phrase> #<phrase role="identifier"> modifies</phrase><phrase role="identifier"> the</phrase><phrase role="identifier"> copy</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
C++:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">dict</phrase><phrase role="identifier"> d</phrase><phrase role="special">(</phrase><phrase role="identifier">x</phrase><phrase role="special">.</phrase><phrase role="identifier">attr</phrase><phrase role="special">(</phrase><phrase role="string">"__dict__"</phrase><phrase role="special">));</phrase> #<phrase role="identifier"> copies</phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">__dict__</phrase><phrase role="identifier">
|
|
d</phrase><phrase role="special">[</phrase><phrase role="char">'whatever'</phrase><phrase role="special">]</phrase><phrase role="special"> =</phrase><phrase role="number"> 3</phrase><phrase role="special">;</phrase> #<phrase role="identifier"> modifies</phrase><phrase role="identifier"> the</phrase><phrase role="identifier"> copy</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<anchor id="derived_object_types.class__lt_t_gt__as_objects" /><bridgehead renderas="sect2">class_<T> as objects</bridgehead><para>
|
|
Due to the dynamic nature of Boost.Python objects, any <literal>class_<T></literal> may
|
|
also be one of these types! The following code snippet wraps the class
|
|
(type) object.</para>
|
|
<para>
|
|
We can use this to create wrapped instances. Example:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">object</phrase><phrase role="identifier"> vec345</phrase><phrase role="special"> =</phrase><phrase role="special"> (</phrase><phrase role="identifier">
|
|
class_</phrase><phrase role="special"><</phrase><phrase role="identifier">Vec2</phrase><phrase role="special">>(</phrase><phrase role="string">"Vec2"</phrase><phrase role="special">,</phrase><phrase role="identifier"> init</phrase><phrase role="special"><</phrase><phrase role="keyword">double</phrase><phrase role="special">,</phrase><phrase role="keyword"> double</phrase><phrase role="special">>())</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def_readonly</phrase><phrase role="special">(</phrase><phrase role="string">"length"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">Point</phrase><phrase role="special">::</phrase><phrase role="identifier">length</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def_readonly</phrase><phrase role="special">(</phrase><phrase role="string">"angle"</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">Point</phrase><phrase role="special">::</phrase><phrase role="identifier">angle</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
)(</phrase><phrase role="number">3.0</phrase><phrase role="special">,</phrase><phrase role="number"> 4.0</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
|
|
assert</phrase><phrase role="special">(</phrase><phrase role="identifier">vec345</phrase><phrase role="special">.</phrase><phrase role="identifier">attr</phrase><phrase role="special">(</phrase><phrase role="string">"length"</phrase><phrase role="special">)</phrase><phrase role="special"> ==</phrase><phrase role="number"> 5.0</phrase><phrase role="special">);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
</section>
|
|
<section id="python.extracting_c___objects">
|
|
<title>Extracting C++ objects</title>
|
|
<para>
|
|
At some point, we will need to get C++ values out of object instances. This
|
|
can be achieved with the <literal>extract<T></literal> function. Consider the following:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">double</phrase><phrase role="identifier"> x</phrase><phrase role="special"> =</phrase><phrase role="identifier"> o</phrase><phrase role="special">.</phrase><phrase role="identifier">attr</phrase><phrase role="special">(</phrase><phrase role="string">"length"</phrase><phrase role="special">);</phrase><phrase role="comment"> // compile error
|
|
</phrase></literal>
|
|
</programlisting>
|
|
<para>
|
|
In the code above, we got a compiler error because Boost.Python
|
|
<literal>object</literal> can't be implicitly converted to <literal>double</literal>s. Instead, what
|
|
we wanted to do above can be achieved by writing:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">double</phrase><phrase role="identifier"> l</phrase><phrase role="special"> =</phrase><phrase role="identifier"> extract</phrase><phrase role="special"><</phrase><phrase role="keyword">double</phrase><phrase role="special">>(</phrase><phrase role="identifier">o</phrase><phrase role="special">.</phrase><phrase role="identifier">attr</phrase><phrase role="special">(</phrase><phrase role="string">"length"</phrase><phrase role="special">));</phrase><phrase role="identifier">
|
|
Vec2</phrase><phrase role="special">&</phrase><phrase role="identifier"> v</phrase><phrase role="special"> =</phrase><phrase role="identifier"> extract</phrase><phrase role="special"><</phrase><phrase role="identifier">Vec2</phrase><phrase role="special">&>(</phrase><phrase role="identifier">o</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
assert</phrase><phrase role="special">(</phrase><phrase role="identifier">l</phrase><phrase role="special"> ==</phrase><phrase role="identifier"> v</phrase><phrase role="special">.</phrase><phrase role="identifier">length</phrase><phrase role="special">());</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
The first line attempts to extract the "length" attribute of the
|
|
Boost.Python <literal>object</literal> <literal>o</literal>. The second line attempts to <emphasis>extract</emphasis> the
|
|
<literal>Vec2</literal> object from held by the Boost.Python <literal>object</literal> <literal>o</literal>.</para>
|
|
<para>
|
|
Take note that we said "attempt to" above. What if the Boost.Python
|
|
<literal>object</literal> <literal>o</literal> does not really hold a <literal>Vec2</literal> type? This is certainly
|
|
a possibility considering the dynamic nature of Python <literal>object</literal>s. To
|
|
be on the safe side, if the C++ type can't be extracted, an
|
|
appropriate exception is thrown. To avoid an exception, we need to
|
|
test for extractibility:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">extract</phrase><phrase role="special"><</phrase><phrase role="identifier">Vec2</phrase><phrase role="special">&></phrase><phrase role="identifier"> x</phrase><phrase role="special">(</phrase><phrase role="identifier">o</phrase><phrase role="special">);</phrase><phrase role="keyword">
|
|
if</phrase><phrase role="special"> (</phrase><phrase role="identifier">x</phrase><phrase role="special">.</phrase><phrase role="identifier">check</phrase><phrase role="special">())</phrase><phrase role="special"> {</phrase><phrase role="identifier">
|
|
Vec2</phrase><phrase role="special">&</phrase><phrase role="identifier"> v</phrase><phrase role="special"> =</phrase><phrase role="identifier"> x</phrase><phrase role="special">();</phrase><phrase role="special"> ...</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/tip.png"></imagedata></imageobject></inlinemediaobject> The astute reader might have noticed that the <literal>extract<T></literal>
|
|
facility in fact solves the mutable copying problem:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">dict</phrase><phrase role="identifier"> d</phrase><phrase role="special"> =</phrase><phrase role="identifier"> extract</phrase><phrase role="special"><</phrase><phrase role="identifier">dict</phrase><phrase role="special">>(</phrase><phrase role="identifier">x</phrase><phrase role="special">.</phrase><phrase role="identifier">attr</phrase><phrase role="special">(</phrase><phrase role="string">"__dict__"</phrase><phrase role="special">));</phrase><phrase role="identifier">
|
|
d</phrase><phrase role="special">[</phrase><phrase role="char">'whatever'</phrase><phrase role="special">]</phrase><phrase role="special"> =</phrase><phrase role="number"> 3</phrase><phrase role="special">;</phrase> #<phrase role="identifier"> modifies</phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">__dict__</phrase><phrase role="special"> !</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
</section>
|
|
<section id="python.enums">
|
|
<title>Enums</title>
|
|
<para>
|
|
Boost.Python has a nifty facility to capture and wrap C++ enums. While
|
|
Python has no <literal>enum</literal> type, we'll often want to expose our C++ enums to
|
|
Python as an <literal>int</literal>. Boost.Python's enum facility makes this easy while
|
|
taking care of the proper conversions from Python's dynamic typing to C++'s
|
|
strong static typing (in C++, ints cannot be implicitly converted to
|
|
enums). To illustrate, given a C++ enum:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">enum</phrase><phrase role="identifier"> choice</phrase><phrase role="special"> {</phrase><phrase role="identifier"> red</phrase><phrase role="special">,</phrase><phrase role="identifier"> blue</phrase><phrase role="special"> };</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
the construct:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">enum_</phrase><phrase role="special"><</phrase><phrase role="identifier">choice</phrase><phrase role="special">>(</phrase><phrase role="string">"choice"</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">value</phrase><phrase role="special">(</phrase><phrase role="string">"red"</phrase><phrase role="special">,</phrase><phrase role="identifier"> red</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">value</phrase><phrase role="special">(</phrase><phrase role="string">"blue"</phrase><phrase role="special">,</phrase><phrase role="identifier"> blue</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
;</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
can be used to expose to Python. The new enum type is created in the
|
|
current <literal>scope()</literal>, which is usually the current module. The snippet above
|
|
creates a Python class derived from Python's <literal>int</literal> type which is
|
|
associated with the C++ type passed as its first parameter.</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">what is a scope?</emphasis><para/>
|
|
<para/>
|
|
The scope is a class that has an
|
|
associated global Python object which controls the Python namespace in
|
|
which new extension classes and wrapped functions will be defined as
|
|
attributes. Details can be found <ulink url="../../../v2/scope.html">here</ulink>.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
<para>
|
|
You can access those values in Python as</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> my_module</phrase><phrase role="special">.</phrase><phrase role="identifier">choice</phrase><phrase role="special">.</phrase><phrase role="identifier">red</phrase><phrase role="identifier">
|
|
my_module</phrase><phrase role="special">.</phrase><phrase role="identifier">choice</phrase><phrase role="special">.</phrase><phrase role="identifier">red</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
where my_module is the module where the enum is declared. You can also
|
|
create a new scope around a class:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">scope</phrase><phrase role="identifier"> in_X</phrase><phrase role="special"> =</phrase><phrase role="identifier"> class_</phrase><phrase role="special"><</phrase><phrase role="identifier">X</phrase><phrase role="special">>(</phrase><phrase role="string">"X"</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="special"> ...</phrase><phrase role="special"> )</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="special"> ...</phrase><phrase role="special"> )</phrase><phrase role="special">
|
|
;</phrase><phrase role="comment">
|
|
|
|
// Expose X::nested as X.nested
|
|
</phrase><phrase role="identifier">enum_</phrase><phrase role="special"><</phrase><phrase role="identifier">X</phrase><phrase role="special">::</phrase><phrase role="identifier">nested</phrase><phrase role="special">>(</phrase><phrase role="string">"nested"</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">value</phrase><phrase role="special">(</phrase><phrase role="string">"red"</phrase><phrase role="special">,</phrase><phrase role="identifier"> red</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">value</phrase><phrase role="special">(</phrase><phrase role="string">"blue"</phrase><phrase role="special">,</phrase><phrase role="identifier"> blue</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
;</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
</section></section>
|
|
<section id="python.embedding">
|
|
<title>Embedding</title>
|
|
<para>
|
|
By now you should know how to use Boost.Python to call your C++ code from
|
|
Python. However, sometimes you may need to do the reverse: call Python code
|
|
from the C++-side. This requires you to <emphasis>embed</emphasis> the Python interpreter
|
|
into your C++ program.</para>
|
|
<para>
|
|
Currently, Boost.Python does not directly support everything you'll need
|
|
when embedding. Therefore you'll need to use the
|
|
<ulink url="http://www.python.org/doc/current/api/api.html">Python/C API</ulink> to fill in
|
|
the gaps. However, Boost.Python already makes embedding a lot easier and,
|
|
in a future version, it may become unnecessary to touch the Python/C API at
|
|
all. So stay tuned... <inlinemediaobject><imageobject><imagedata fileref="images/smiley.png"></imagedata></imageobject></inlinemediaobject></para>
|
|
<anchor id="embedding.building_embedded_programs" /><bridgehead renderas="sect2">Building embedded programs</bridgehead><para>
|
|
To be able to use embedding in your programs, they have to be linked to
|
|
both Boost.Python's and Python's static link library.</para>
|
|
<para>
|
|
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
|
|
<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
|
|
your Python directory. On Windows it is called pythonXY.lib where X.Y is
|
|
your major Python version number.</para>
|
|
<para>
|
|
Additionally, Python's <literal>/include</literal> subdirectory has to be added to your
|
|
include path.</para>
|
|
<para>
|
|
In a Jamfile, all the above boils down to:</para>
|
|
<programlisting><literal> projectroot c:\projects\embedded_program ; # location of the program
|
|
|
|
# bring in the rules for python
|
|
SEARCH on python.jam = $(BOOST_BUILD_PATH) ;
|
|
include python.jam ;
|
|
|
|
exe embedded_program # name of the executable
|
|
: #sources
|
|
embedded_program.cpp
|
|
: # requirements
|
|
<find-library>boost_python <library-path>c:\boost\libs\python
|
|
$(PYTHON_PROPERTIES)
|
|
<library-path>$(PYTHON_LIB_PATH)
|
|
<find-library>$(PYTHON_EMBEDDED_LIBRARY) ;
|
|
</literal></programlisting><anchor id="embedding.getting_started" /><bridgehead renderas="sect2">Getting started</bridgehead><para>
|
|
Being able to build is nice, but there is nothing to build yet. Embedding
|
|
the Python interpreter into one of your C++ programs requires these 4
|
|
steps:</para>
|
|
<orderedlist>
|
|
<listitem>
|
|
#include <literal><boost/python.hpp></literal><para/>
|
|
<para/>
|
|
|
|
</listitem><listitem>
|
|
Call <ulink url="http://www.python.org/doc/current/api/initialization.html#l2h-652">Py_Initialize</ulink>() to start the interpreter and create the <literal><emphasis role="underline">_main</emphasis>_</literal> module.<para/>
|
|
<para/>
|
|
|
|
</listitem><listitem>
|
|
Call other Python C API routines to use the interpreter.<para/>
|
|
<para/>
|
|
|
|
</listitem><listitem>
|
|
Call <ulink url="http://www.python.org/doc/current/api/initialization.html#l2h-656">Py_Finalize</ulink>() to stop the interpreter and release its resources.
|
|
</listitem>
|
|
</orderedlist><para>
|
|
(Of course, there can be other C++ code between all of these steps.)</para>
|
|
<blockquote><para><emphasis><emphasis role="bold">Now that we can embed the interpreter in our programs, lets see how to put it to use...</emphasis></emphasis></para></blockquote>
|
|
<section id="python.using_the_interpreter">
|
|
<title>Using the interpreter</title>
|
|
<para>
|
|
As you probably already know, objects in Python are reference-counted.
|
|
Naturally, the <literal>PyObject</literal>s of the Python/C API are also reference-counted.
|
|
There is a difference however. While the reference-counting is fully
|
|
automatic in Python, the Python/C API requires you to do it
|
|
<ulink url="http://www.python.org/doc/current/api/refcounts.html">by hand</ulink>. This is
|
|
messy and especially hard to get right in the presence of C++ exceptions.
|
|
Fortunately Boost.Python provides the <ulink url="../../../v2/handle.html">handle</ulink> and
|
|
<ulink url="../../../v2/object.html">object</ulink> class templates to automate the process.</para>
|
|
<anchor id="using_the_interpreter.reference_counting_handles_and_objects" /><bridgehead renderas="sect2">Reference-counting handles and objects</bridgehead><para>
|
|
There are two ways in which a function in the Python/C API can return a
|
|
<literal>PyObject*</literal>: as a <emphasis>borrowed reference</emphasis> or as a <emphasis>new reference</emphasis>. Which of
|
|
these a function uses, is listed in that function's documentation. The two
|
|
require slightely different approaches to reference-counting but both can
|
|
be 'handled' by Boost.Python.</para>
|
|
<para>
|
|
For a function returning a <emphasis>borrowed reference</emphasis> we'll have to tell the
|
|
<literal>handle</literal> that the <literal>PyObject*</literal> is borrowed with the aptly named
|
|
<ulink url="../../../v2/handle.html#borrowed-spec">borrowed</ulink> function. Two functions
|
|
returning borrowed references are <ulink url="http://www.python.org/doc/current/api/importing.html#l2h-125">PyImport_AddModule</ulink> and <ulink url="http://www.python.org/doc/current/api/moduleObjects.html#l2h-594">PyModule_GetDict</ulink>.
|
|
The former returns a reference to an already imported module, the latter
|
|
retrieves a module's namespace dictionary. Let's use them to retrieve the
|
|
namespace of the <literal><emphasis role="underline">_main</emphasis>_</literal> module:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">object</phrase><phrase role="identifier"> main_module</phrase><phrase role="special">((</phrase><phrase role="identifier">
|
|
handle</phrase><phrase role="special"><>(</phrase><phrase role="identifier">borrowed</phrase><phrase role="special">(</phrase><ulink url="http://www.python.org/doc/current/api/importing.html#l2h-125">PyImport_AddModule</ulink><phrase role="special">(</phrase><phrase role="string">"__main__"</phrase><phrase role="special">)))));</phrase><phrase role="identifier">
|
|
|
|
object</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special"> =</phrase><phrase role="identifier"> main_module</phrase><phrase role="special">.</phrase><phrase role="identifier">attr</phrase><phrase role="special">(</phrase><phrase role="string">"__dict__"</phrase><phrase role="special">);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
For a function returning a <emphasis>new reference</emphasis> we can just create a <literal>handle</literal>
|
|
out of the raw <literal>PyObject*</literal> without wrapping it in a call to borrowed. One
|
|
such function that returns a new reference is <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink> which we'll
|
|
discuss in the next section.</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">Handle is a class <emphasis>template</emphasis>, so why haven't we been using any template parameters?</emphasis><para/>
|
|
|
|
<para/>
|
|
|
|
<literal>handle</literal> has a single template parameter specifying the type of the managed object. This type is <literal>PyObject</literal> 99% of the time, so the parameter was defaulted to <literal>PyObject</literal> for convenience. Therefore we can use the shorthand <literal>handle<></literal> instead of the longer, but equivalent, <literal>handle<PyObject></literal>.
|
|
</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
<anchor id="using_the_interpreter.running_python_code" /><bridgehead renderas="sect2">Running Python code</bridgehead><para>
|
|
To run Python code from C++ there is a family of functions in the API
|
|
starting with the PyRun prefix. You can find the full list of these
|
|
functions <ulink url="http://www.python.org/doc/current/api/veryhigh.html">here</ulink>. They
|
|
all work similarly so we will look at only one of them, namely:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">PyObject</phrase><phrase role="special">*</phrase> <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink><phrase role="special">(</phrase><phrase role="keyword">char</phrase><phrase role="special"> *</phrase><phrase role="identifier">str</phrase><phrase role="special">,</phrase><phrase role="keyword"> int</phrase><phrase role="identifier"> start</phrase><phrase role="special">,</phrase><phrase role="identifier"> PyObject</phrase><phrase role="special"> *</phrase><phrase role="identifier">globals</phrase><phrase role="special">,</phrase><phrase role="identifier"> PyObject</phrase><phrase role="special"> *</phrase><phrase role="identifier">locals</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
<ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink> takes the code to execute as a null-terminated (C-style)
|
|
string in its <literal>str</literal> parameter. The function returns a new reference to a
|
|
Python object. Which object is returned depends on the <literal>start</literal> paramater.</para>
|
|
<para>
|
|
The <literal>start</literal> parameter is the start symbol from the Python grammar to use
|
|
for interpreting the code. The possible values are:</para>
|
|
<informaltable frame="all">
|
|
<bridgehead renderas="sect4"><phrase role="table-title">Start symbols</phrase></bridgehead>
|
|
<tgroup cols="2">
|
|
<thead><row><entry><ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-58">Py_eval_input</ulink></entry><entry>for interpreting isolated expressions</entry></row>
|
|
</thead>
|
|
<tbody>
|
|
<row><entry><ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-59">Py_file_input</ulink></entry><entry>for interpreting sequences of statements</entry></row>
|
|
<row><entry><ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-60">Py_single_input</ulink></entry><entry>for interpreting a single statement</entry></row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
<para>
|
|
When using <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-58">Py_eval_input</ulink>, the input string must contain a single expression
|
|
and its result is returned. When using <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-59">Py_file_input</ulink>, the string can
|
|
contain an abitrary number of statements and None is returned.
|
|
<ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-60">Py_single_input</ulink> works in the same way as <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-59">Py_file_input</ulink> but only accepts a
|
|
single statement.</para>
|
|
<para>
|
|
Lastly, the <literal>globals</literal> and <literal>locals</literal> parameters are Python dictionaries
|
|
containing the globals and locals of the context in which to run the code.
|
|
For most intents and purposes you can use the namespace dictionary of the
|
|
<literal><emphasis role="underline">_main</emphasis>_</literal> module for both parameters.</para>
|
|
<para>
|
|
We have already seen how to get the <literal><emphasis role="underline">_main</emphasis>_</literal> module's namespace so let's
|
|
run some Python code in it:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">object</phrase><phrase role="identifier"> main_module</phrase><phrase role="special">((</phrase><phrase role="identifier">
|
|
handle</phrase><phrase role="special"><>(</phrase><phrase role="identifier">borrowed</phrase><phrase role="special">(</phrase><ulink url="http://www.python.org/doc/current/api/importing.html#l2h-125">PyImport_AddModule</ulink><phrase role="special">(</phrase><phrase role="string">"__main__"</phrase><phrase role="special">)))));</phrase><phrase role="identifier">
|
|
|
|
object</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special"> =</phrase><phrase role="identifier"> main_module</phrase><phrase role="special">.</phrase><phrase role="identifier">attr</phrase><phrase role="special">(</phrase><phrase role="string">"__dict__"</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
|
|
handle</phrase><phrase role="special"><></phrase><phrase role="identifier"> ignored</phrase><phrase role="special">((</phrase><ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink><phrase role="special">(</phrase><phrase role="string">
|
|
|
|
"hello = file('hello.txt', 'w')\n"</phrase><phrase role="string">
|
|
"hello.write('Hello world!')\n"</phrase><phrase role="string">
|
|
"hello.close()"</phrase><phrase role="special">
|
|
|
|
,</phrase> <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-59">Py_file_input</ulink><phrase role="special">
|
|
,</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special">.</phrase><phrase role="identifier">ptr</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
,</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special">.</phrase><phrase role="identifier">ptr</phrase><phrase role="special">())</phrase><phrase role="special">
|
|
));</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Because the Python/C API doesn't know anything about <literal>object</literal>s, we used
|
|
the object's <literal>ptr</literal> member function to retrieve the <literal>PyObject*</literal>.</para>
|
|
<para>
|
|
This should create a file called 'hello.txt' in the current directory
|
|
containing a phrase that is well-known in programming circles.</para>
|
|
<para>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/note.png"></imagedata></imageobject></inlinemediaobject> <emphasis role="bold">Note</emphasis> that we wrap the return value of <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink> in a
|
|
(nameless) <literal>handle</literal> even though we are not interested in it. If we didn't
|
|
do this, the the returned object would be kept alive unnecessarily. Unless
|
|
you want to be a Dr. Frankenstein, always wrap <literal>PyObject*</literal>s in <literal>handle</literal>s.</para>
|
|
<anchor id="using_the_interpreter.beyond_handles" /><bridgehead renderas="sect2">Beyond handles</bridgehead><para>
|
|
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 <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>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">object</phrase><phrase role="identifier"> main_module</phrase><phrase role="special">((</phrase><phrase role="identifier">
|
|
handle</phrase><phrase role="special"><>(</phrase><phrase role="identifier">borrowed</phrase><phrase role="special">(</phrase><ulink url="http://www.python.org/doc/current/api/importing.html#l2h-125">PyImport_AddModule</ulink><phrase role="special">(</phrase><phrase role="string">"__main__"</phrase><phrase role="special">)))));</phrase><phrase role="identifier">
|
|
|
|
object</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special"> =</phrase><phrase role="identifier"> main_module</phrase><phrase role="special">.</phrase><phrase role="identifier">attr</phrase><phrase role="special">(</phrase><phrase role="string">"__dict__"</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
|
|
handle</phrase><phrase role="special"><></phrase><phrase role="identifier"> ignored</phrase><phrase role="special">((</phrase><ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink><phrase role="special">(</phrase><phrase role="string">
|
|
|
|
"result = 5 ** 2"</phrase><phrase role="special">
|
|
|
|
,</phrase> <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-59">Py_file_input</ulink><phrase role="special">
|
|
,</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special">.</phrase><phrase role="identifier">ptr</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
,</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special">.</phrase><phrase role="identifier">ptr</phrase><phrase role="special">())</phrase><phrase role="special">
|
|
));</phrase><phrase role="keyword">
|
|
|
|
int</phrase><phrase role="identifier"> five_squared</phrase><phrase role="special"> =</phrase><phrase role="identifier"> extract</phrase><phrase role="special"><</phrase><phrase role="keyword">int</phrase><phrase role="special">>(</phrase><phrase role="identifier">main_namespace</phrase><phrase role="special">[</phrase><phrase role="string">"result"</phrase><phrase role="special">]);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Here we create a dictionary object for the <literal><emphasis role="underline">_main</emphasis>_</literal> module's namespace.
|
|
Then we assign 5 squared to the result variable and read this variable from
|
|
the dictionary. Another way to achieve the same result is to let
|
|
<ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink> return the result directly with <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-58">Py_eval_input</ulink>:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">object</phrase><phrase role="identifier"> result</phrase><phrase role="special">((</phrase><phrase role="identifier">handle</phrase><phrase role="special"><>(</phrase>
|
|
<ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink><phrase role="special">(</phrase><phrase role="string">"5 ** 2"</phrase><phrase role="special">
|
|
,</phrase> <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-58">Py_eval_input</ulink><phrase role="special">
|
|
,</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special">.</phrase><phrase role="identifier">ptr</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
,</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special">.</phrase><phrase role="identifier">ptr</phrase><phrase role="special">()))</phrase><phrase role="special">
|
|
));</phrase><phrase role="keyword">
|
|
|
|
int</phrase><phrase role="identifier"> five_squared</phrase><phrase role="special"> =</phrase><phrase role="identifier"> extract</phrase><phrase role="special"><</phrase><phrase role="keyword">int</phrase><phrase role="special">>(</phrase><phrase role="identifier">result</phrase><phrase role="special">);</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/note.png"></imagedata></imageobject></inlinemediaobject> <emphasis role="bold">Note</emphasis> that <literal>object</literal>'s member function to return the wrapped
|
|
<literal>PyObject*</literal> is called <literal>ptr</literal> instead of <literal>get</literal>. This makes sense if you
|
|
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
|
|
C++ exception when using <literal>handle</literal>:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">try</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
object</phrase><phrase role="identifier"> result</phrase><phrase role="special">((</phrase><phrase role="identifier">handle</phrase><phrase role="special"><>(</phrase><ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink><phrase role="special">(</phrase><phrase role="string">
|
|
"5/0"</phrase><phrase role="special">
|
|
,</phrase> <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-58">Py_eval_input</ulink><phrase role="special">
|
|
,</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special">.</phrase><phrase role="identifier">ptr</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
,</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special">.</phrase><phrase role="identifier">ptr</phrase><phrase role="special">()))</phrase><phrase role="special">
|
|
));</phrase><phrase role="comment">
|
|
|
|
// execution will never get here:
|
|
</phrase><phrase role="keyword"> int</phrase><phrase role="identifier"> five_divided_by_zero</phrase><phrase role="special"> =</phrase><phrase role="identifier"> extract</phrase><phrase role="special"><</phrase><phrase role="keyword">int</phrase><phrase role="special">>(</phrase><phrase role="identifier">result</phrase><phrase role="special">);</phrase><phrase role="special">
|
|
}</phrase><phrase role="keyword">
|
|
catch</phrase><phrase role="special">(</phrase><phrase role="identifier">error_already_set</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
// handle the exception in some way
|
|
</phrase><phrase role="special">}</phrase>
|
|
</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>
|
|
<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">
|
|
{</phrase><phrase role="keyword">
|
|
if</phrase><phrase role="special"> (</phrase><phrase role="identifier">PyErr_ExceptionMatches</phrase><phrase role="special">(</phrase><phrase role="identifier">PyExc_ZeroDivisionError</phrase><phrase role="special">))</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
// handle ZeroDivisionError specially
|
|
</phrase><phrase role="special"> }</phrase><phrase role="keyword">
|
|
else</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
// print all other errors to stderr
|
|
</phrase><phrase role="identifier"> PyErr_Print</phrase><phrase role="special">();</phrase><phrase role="special">
|
|
}</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
(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
|
|
way you'd use borrowed:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">handle</phrase><phrase role="special"><></phrase><phrase role="identifier"> result</phrase><phrase role="special">((</phrase><phrase role="identifier">allow_null</phrase><phrase role="special">(</phrase><ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-55">PyRun_String</ulink><phrase role="special">(</phrase><phrase role="string">
|
|
"5/0"</phrase><phrase role="special">
|
|
,</phrase> <ulink url="http://www.python.org/doc/current/api/veryhigh.html#l2h-58">Py_eval_input</ulink><phrase role="special">
|
|
,</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special">.</phrase><phrase role="identifier">ptr</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
,</phrase><phrase role="identifier"> main_namespace</phrase><phrase role="special">.</phrase><phrase role="identifier">ptr</phrase><phrase role="special">()))));</phrase><phrase role="keyword">
|
|
|
|
if</phrase><phrase role="special"> (!</phrase><phrase role="identifier">result</phrase><phrase role="special">)</phrase><phrase role="comment">
|
|
// Python exception occurred
|
|
</phrase><phrase role="keyword">else</phrase><phrase role="comment">
|
|
// everything went okay, it's safe to use the result
|
|
</phrase></literal>
|
|
</programlisting>
|
|
</section></section>
|
|
<section id="python.iterators">
|
|
<title>Iterators</title>
|
|
<para>
|
|
In C++, and STL in particular, we see iterators everywhere. Python also has
|
|
iterators, but these are two very different beasts.</para>
|
|
<para>
|
|
<emphasis role="bold">C++ iterators:</emphasis></para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
C++ has 5 type categories (random-access, bidirectional, forward, input, output)
|
|
</listitem><listitem>
|
|
There are 2 Operation categories: reposition, access
|
|
</listitem><listitem>
|
|
A pair of iterators is needed to represent a (first/last) range.
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
<emphasis role="bold">Python Iterators:</emphasis></para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
1 category (forward)
|
|
</listitem><listitem>
|
|
1 operation category (next())
|
|
</listitem><listitem>
|
|
Raises StopIteration exception at end
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
The typical Python iteration protocol: <literal><emphasis role="bold">for y in x...</emphasis></literal> is as follows:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">iter</phrase><phrase role="special"> =</phrase><phrase role="identifier"> x</phrase><phrase role="special">.</phrase><phrase role="identifier">__iter__</phrase><phrase role="special">()</phrase> #<phrase role="identifier"> get</phrase><phrase role="identifier"> iterator</phrase><phrase role="keyword">
|
|
try</phrase><phrase role="special">:</phrase><phrase role="keyword">
|
|
while</phrase><phrase role="number"> 1</phrase><phrase role="special">:</phrase><phrase role="identifier">
|
|
y</phrase><phrase role="special"> =</phrase><phrase role="identifier"> iter</phrase><phrase role="special">.</phrase><phrase role="identifier">next</phrase><phrase role="special">()</phrase> #<phrase role="identifier"> get</phrase><phrase role="identifier"> each</phrase><phrase role="identifier"> item</phrase><phrase role="special">
|
|
...</phrase> #<phrase role="identifier"> process</phrase><phrase role="identifier"> y</phrase><phrase role="identifier">
|
|
except</phrase><phrase role="identifier"> StopIteration</phrase><phrase role="special">:</phrase><phrase role="identifier"> pass</phrase> #<phrase role="identifier"> iterator</phrase><phrase role="identifier"> exhausted</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Boost.Python provides some mechanisms to make C++ iterators play along
|
|
nicely as Python iterators. What we need to do is to produce
|
|
appropriate <emphasis role="underline">_iter</emphasis>_ function from C++ iterators that is compatible
|
|
with the Python iteration protocol. For example:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">object</phrase><phrase role="identifier"> get_iterator</phrase><phrase role="special"> =</phrase><phrase role="identifier"> iterator</phrase><phrase role="special"><</phrase><phrase role="identifier">vector</phrase><phrase role="special"><</phrase><phrase role="keyword">int</phrase><phrase role="special">></phrase><phrase role="special"> >();</phrase><phrase role="identifier">
|
|
object</phrase><phrase role="identifier"> iter</phrase><phrase role="special"> =</phrase><phrase role="identifier"> get_iterator</phrase><phrase role="special">(</phrase><phrase role="identifier">v</phrase><phrase role="special">);</phrase><phrase role="identifier">
|
|
object</phrase><phrase role="identifier"> first</phrase><phrase role="special"> =</phrase><phrase role="identifier"> iter</phrase><phrase role="special">.</phrase><phrase role="identifier">next</phrase><phrase role="special">();</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Or for use in class_<>:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">.</phrase><phrase role="identifier">def</phrase><phrase role="special">(</phrase><phrase role="string">"__iter__"</phrase><phrase role="special">,</phrase><phrase role="identifier"> iterator</phrase><phrase role="special"><</phrase><phrase role="identifier">vector</phrase><phrase role="special"><</phrase><phrase role="keyword">int</phrase><phrase role="special">></phrase><phrase role="special"> >())</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
<emphasis role="bold">range</emphasis></para>
|
|
<para>
|
|
We can create a Python savvy iterator using the range function:</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
range(start, finish)
|
|
</listitem><listitem>
|
|
range<Policies,Target>(start, finish)
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
Here, start/finish may be one of:</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
member data pointers
|
|
</listitem><listitem>
|
|
member function pointers
|
|
</listitem><listitem>
|
|
adaptable function object (use Target parameter)
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
<emphasis role="bold">iterator</emphasis></para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
iterator<T, Policies>()
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
Given a container <literal>T</literal>, iterator is a shortcut that simply calls <literal>range</literal>
|
|
with &T::begin, &T::end.</para>
|
|
<para>
|
|
Let's put this into action... Here's an example from some hypothetical
|
|
bogon Particle accelerator code:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">f</phrase><phrase role="special"> =</phrase><phrase role="identifier"> Field</phrase><phrase role="special">()</phrase><phrase role="keyword">
|
|
for</phrase><phrase role="identifier"> x</phrase><phrase role="identifier"> in</phrase><phrase role="identifier"> f</phrase><phrase role="special">.</phrase><phrase role="identifier">pions</phrase><phrase role="special">:</phrase><phrase role="identifier">
|
|
smash</phrase><phrase role="special">(</phrase><phrase role="identifier">x</phrase><phrase role="special">)</phrase><phrase role="keyword">
|
|
for</phrase><phrase role="identifier"> y</phrase><phrase role="identifier"> in</phrase><phrase role="identifier"> f</phrase><phrase role="special">.</phrase><phrase role="identifier">bogons</phrase><phrase role="special">:</phrase><phrase role="identifier">
|
|
count</phrase><phrase role="special">(</phrase><phrase role="identifier">y</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Now, our C++ Wrapper:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">class_</phrase><phrase role="special"><</phrase><phrase role="identifier">F</phrase><phrase role="special">>(</phrase><phrase role="string">"Field"</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">property</phrase><phrase role="special">(</phrase><phrase role="string">"pions"</phrase><phrase role="special">,</phrase><phrase role="identifier"> range</phrase><phrase role="special">(&</phrase><phrase role="identifier">F</phrase><phrase role="special">::</phrase><phrase role="identifier">p_begin</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">F</phrase><phrase role="special">::</phrase><phrase role="identifier">p_end</phrase><phrase role="special">))</phrase><phrase role="special">
|
|
.</phrase><phrase role="identifier">property</phrase><phrase role="special">(</phrase><phrase role="string">"bogons"</phrase><phrase role="special">,</phrase><phrase role="identifier"> range</phrase><phrase role="special">(&</phrase><phrase role="identifier">F</phrase><phrase role="special">::</phrase><phrase role="identifier">b_begin</phrase><phrase role="special">,</phrase><phrase role="special"> &</phrase><phrase role="identifier">F</phrase><phrase role="special">::</phrase><phrase role="identifier">b_end</phrase><phrase role="special">));</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
</section>
|
|
<section id="python.exception">
|
|
<title> Exception Translation</title>
|
|
<para>
|
|
All C++ exceptions must be caught at the boundary with Python code. This
|
|
boundary is the point where C++ meets Python. Boost.Python provides a
|
|
default exception handler that translates selected standard exceptions,
|
|
then gives up:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">raise</phrase><phrase role="identifier"> RuntimeError</phrase><phrase role="special">,</phrase><phrase role="char"> 'unidentifiable C++ Exception'</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Users may provide custom translation. Here's an example:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">struct</phrase><phrase role="identifier"> PodBayDoorException</phrase><phrase role="special">;</phrase><phrase role="keyword">
|
|
void</phrase><phrase role="identifier"> translator</phrase><phrase role="special">(</phrase><phrase role="identifier">PodBayDoorException</phrase><phrase role="keyword"> const</phrase><phrase role="special">&</phrase><phrase role="identifier"> x</phrase><phrase role="special">)</phrase><phrase role="special"> {</phrase><phrase role="identifier">
|
|
PyErr_SetString</phrase><phrase role="special">(</phrase><phrase role="identifier">PyExc_UserWarning</phrase><phrase role="special">,</phrase><phrase role="string"> "I'm sorry Dave..."</phrase><phrase role="special">);</phrase><phrase role="special">
|
|
}</phrase><phrase role="identifier">
|
|
BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="identifier">kubrick</phrase><phrase role="special">)</phrase><phrase role="special"> {</phrase><phrase role="identifier">
|
|
register_exception_translator</phrase><phrase role="special"><</phrase><phrase role="identifier">
|
|
PodBayDoorException</phrase><phrase role="special">>(</phrase><phrase role="identifier">translator</phrase><phrase role="special">);</phrase><phrase role="special">
|
|
...</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
</section>
|
|
<section id="python.techniques">
|
|
<title> General Techniques</title>
|
|
<para>
|
|
Here are presented some useful techniques that you can use while wrapping code with Boost.Python.</para>
|
|
|
|
<section id="python.creating_packages">
|
|
<title>Creating Packages</title>
|
|
<para>
|
|
A Python package is a collection of modules that provide to the user a certain
|
|
functionality. If you're not familiar on how to create packages, a good
|
|
introduction to them is provided in the
|
|
<ulink url="http://www.python.org/doc/current/tut/node8.html">Python Tutorial</ulink>.</para>
|
|
<para>
|
|
But we are wrapping C++ code, using Boost.Python. How can we provide a nice
|
|
package interface to our users? To better explain some concepts, let's work
|
|
with an example.</para>
|
|
<para>
|
|
We have a C++ library that works with sounds: reading and writing various
|
|
formats, applying filters to the sound data, etc. It is named (conveniently)
|
|
<literal>sounds</literal>. Our library already has a neat C++ namespace hierarchy, like so:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">sounds</phrase><phrase role="special">::</phrase><phrase role="identifier">core</phrase><phrase role="identifier">
|
|
sounds</phrase><phrase role="special">::</phrase><phrase role="identifier">io</phrase><phrase role="identifier">
|
|
sounds</phrase><phrase role="special">::</phrase><phrase role="identifier">filters</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
We would like to present this same hierarchy to the Python user, allowing him
|
|
to write code like this:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">import</phrase><phrase role="identifier"> sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">filters</phrase><phrase role="identifier">
|
|
sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">filters</phrase><phrase role="special">.</phrase><phrase role="identifier">echo</phrase><phrase role="special">(...)</phrase> #<phrase role="identifier"> echo</phrase><phrase role="identifier"> is</phrase><phrase role="identifier"> a</phrase><phrase role="identifier"> C</phrase><phrase role="special">++</phrase><phrase role="identifier"> function</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
The first step is to write the wrapping code. We have to export each module
|
|
separately with Boost.Python, like this:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="comment">/* file core.cpp */</phrase><phrase role="identifier">
|
|
BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="identifier">core</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
/* export everything in the sounds::core namespace */</phrase><phrase role="special">
|
|
...</phrase><phrase role="special">
|
|
}</phrase><phrase role="comment">
|
|
|
|
/* file io.cpp */</phrase><phrase role="identifier">
|
|
BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="identifier">io</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
/* export everything in the sounds::io namespace */</phrase><phrase role="special">
|
|
...</phrase><phrase role="special">
|
|
}</phrase><phrase role="comment">
|
|
|
|
/* file filters.cpp */</phrase><phrase role="identifier">
|
|
BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="identifier">filters</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="comment">
|
|
/* export everything in the sounds::filters namespace */</phrase><phrase role="special">
|
|
...</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Compiling these files will generate the following Python extensions:
|
|
<literal>core.pyd</literal>, <literal>io.pyd</literal> and <literal>filters.pyd</literal>.</para>
|
|
<informaltable frame="all">
|
|
<?dbhtml table-width="74%" ?>
|
|
<tgroup cols="1">
|
|
<tbody>
|
|
<row>
|
|
<entry>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/note.png"></imagedata></imageobject></inlinemediaobject> The extension <literal>.pyd</literal> is used for python extension modules, which
|
|
are just shared libraries. Using the default for your system, like <literal>.so</literal> for
|
|
Unix and <literal>.dll</literal> for Windows, works just as well.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
<para>
|
|
Now, we create this directory structure for our Python package:</para>
|
|
<programlisting><literal> sounds/
|
|
<emphasis role="underline">_init</emphasis>_.py
|
|
core.pyd
|
|
filters.pyd
|
|
io.pyd
|
|
</literal></programlisting><para>
|
|
The file <literal><emphasis role="underline">_init</emphasis>_.py</literal> is what tells Python that the directory <literal>sounds/</literal> is
|
|
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>
|
|
and fire up the interpreter:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> import</phrase><phrase role="identifier"> sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">io</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> import</phrase><phrase role="identifier"> sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">filters</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> sound</phrase><phrase role="special"> =</phrase><phrase role="identifier"> sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">io</phrase><phrase role="special">.</phrase><phrase role="identifier">open</phrase><phrase role="special">(</phrase><phrase role="char">'file.mp3'</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> new_sound</phrase><phrase role="special"> =</phrase><phrase role="identifier"> sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">filters</phrase><phrase role="special">.</phrase><phrase role="identifier">echo</phrase><phrase role="special">(</phrase><phrase role="identifier">sound</phrase><phrase role="special">,</phrase><phrase role="number"> 1.0</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Nice heh?</para>
|
|
<para>
|
|
This is the simplest way to create hierarchies of packages, but it is not very
|
|
flexible. What if we want to add a <emphasis>pure</emphasis> Python function to the filters
|
|
package, for instance, one that applies 3 filters in a sound object at once?
|
|
Sure, you can do this in C++ and export it, but why not do so in Python? You
|
|
don't have to recompile the extension modules, plus it will be easier to write
|
|
it.</para>
|
|
<para>
|
|
If we want this flexibility, we will have to complicate our package hierarchy a
|
|
little. First, we will have to change the name of the extension modules:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="comment">/* file core.cpp */</phrase><phrase role="identifier">
|
|
BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="identifier">_core</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="special">
|
|
...</phrase><phrase role="comment">
|
|
/* export everything in the sounds::core namespace */</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Note that we added an underscore to the module name. The filename will have to
|
|
be changed to <literal>_core.pyd</literal> as well, and we do the same to the other extension modules.
|
|
Now, we change our package hierarchy like so:</para>
|
|
<programlisting><literal> sounds/
|
|
<emphasis role="underline">_init</emphasis>_.py
|
|
core/
|
|
<emphasis role="underline">_init</emphasis>_.py
|
|
_core.pyd
|
|
filters/
|
|
<emphasis role="underline">_init</emphasis>_.py
|
|
_filters.pyd
|
|
io/
|
|
<emphasis role="underline">_init</emphasis>_.py
|
|
_io.pyd
|
|
</literal></programlisting><para>
|
|
Note that we created a directory for each extension module, and added a
|
|
<emphasis role="underline">_init</emphasis>_.py to each one. But if we leave it that way, the user will have to
|
|
access the functions in the core module with this syntax:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> import</phrase><phrase role="identifier"> sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">core</phrase><phrase role="special">.</phrase><phrase role="identifier">_core</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">core</phrase><phrase role="special">.</phrase><phrase role="identifier">_core</phrase><phrase role="special">.</phrase><phrase role="identifier">foo</phrase><phrase role="special">(...)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
which is not what we want. But here enters the <literal><emphasis role="underline">_init</emphasis>_.py</literal> magic: everything
|
|
that is brought to the <literal><emphasis role="underline">_init</emphasis>_.py</literal> namespace can be accessed directly by the
|
|
user. So, all we have to do is bring the entire namespace from <literal>_core.pyd</literal>
|
|
to <literal>core/<emphasis role="underline">_init</emphasis><emphasis role="underline">.py]. So add this line of code to [^sounds/core/</emphasis><emphasis role="underline">init</emphasis>_.py</literal>:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">from</phrase><phrase role="identifier"> _core</phrase><phrase role="identifier"> import</phrase><phrase role="special"> *</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
We do the same for the other packages. Now the user accesses the functions and
|
|
classes in the extension modules like before:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> import</phrase><phrase role="identifier"> sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">filters</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">filters</phrase><phrase role="special">.</phrase><phrase role="identifier">echo</phrase><phrase role="special">(...)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
with the additional benefit that we can easily add pure Python functions to
|
|
any module, in a way that the user can't tell the difference between a C++
|
|
function and a Python function. Let's add a <emphasis>pure</emphasis> Python function,
|
|
<literal>echo_noise</literal>, to the <literal>filters</literal> package. This function applies both the
|
|
<literal>echo</literal> and <literal>noise</literal> filters in sequence in the given <literal>sound</literal> object. We
|
|
create a file named <literal>sounds/filters/echo_noise.py</literal> and code our function:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">import</phrase><phrase role="identifier"> _filters</phrase><phrase role="identifier">
|
|
def</phrase><phrase role="identifier"> echo_noise</phrase><phrase role="special">(</phrase><phrase role="identifier">sound</phrase><phrase role="special">):</phrase><phrase role="identifier">
|
|
s</phrase><phrase role="special"> =</phrase><phrase role="identifier"> _filters</phrase><phrase role="special">.</phrase><phrase role="identifier">echo</phrase><phrase role="special">(</phrase><phrase role="identifier">sound</phrase><phrase role="special">)</phrase><phrase role="identifier">
|
|
s</phrase><phrase role="special"> =</phrase><phrase role="identifier"> _filters</phrase><phrase role="special">.</phrase><phrase role="identifier">noise</phrase><phrase role="special">(</phrase><phrase role="identifier">sound</phrase><phrase role="special">)</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="identifier"> s</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Next, we add this line to <literal>sounds<emphasis>filters</emphasis><emphasis role="underline">_init</emphasis>_.py</literal>:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">from</phrase><phrase role="identifier"> echo_noise</phrase><phrase role="identifier"> import</phrase><phrase role="identifier"> echo_noise</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
And that's it. The user now accesses this function like any other function
|
|
from the <literal>filters</literal> package:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> import</phrase><phrase role="identifier"> sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">filters</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> sounds</phrase><phrase role="special">.</phrase><phrase role="identifier">filters</phrase><phrase role="special">.</phrase><phrase role="identifier">echo_noise</phrase><phrase role="special">(...)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
</section>
|
|
<section id="python.extending_wrapped_objects_in_python">
|
|
<title>Extending Wrapped Objects in Python</title>
|
|
<para>
|
|
Thanks to Python's flexibility, you can easily add new methods to a class,
|
|
even after it was already created:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="keyword"> class</phrase><phrase role="identifier"> C</phrase><phrase role="special">(</phrase><phrase role="identifier">object</phrase><phrase role="special">):</phrase><phrase role="identifier"> pass</phrase><phrase role="special">
|
|
>>></phrase><phrase role="special">
|
|
>>></phrase> #<phrase role="identifier"> a</phrase><phrase role="identifier"> regular</phrase><phrase role="identifier"> function</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> def</phrase><phrase role="identifier"> C_str</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special">):</phrase><phrase role="keyword"> return</phrase><phrase role="char"> 'A C instance!'</phrase><phrase role="special">
|
|
>>></phrase><phrase role="special">
|
|
>>></phrase> #<phrase role="identifier"> now</phrase><phrase role="identifier"> we</phrase><phrase role="identifier"> turn</phrase><phrase role="identifier"> it</phrase><phrase role="identifier"> in</phrase><phrase role="identifier"> a</phrase><phrase role="identifier"> member</phrase><phrase role="identifier"> function</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> C</phrase><phrase role="special">.</phrase><phrase role="identifier">__str__</phrase><phrase role="special"> =</phrase><phrase role="identifier"> C_str</phrase><phrase role="special">
|
|
>>></phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> c</phrase><phrase role="special"> =</phrase><phrase role="identifier"> C</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> print</phrase><phrase role="identifier"> c</phrase><phrase role="identifier">
|
|
A</phrase><phrase role="identifier"> C</phrase><phrase role="identifier"> instance</phrase><phrase role="special">!</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> C_str</phrase><phrase role="special">(</phrase><phrase role="identifier">c</phrase><phrase role="special">)</phrase><phrase role="identifier">
|
|
A</phrase><phrase role="identifier"> C</phrase><phrase role="identifier"> instance</phrase><phrase role="special">!</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Yes, Python rox. <inlinemediaobject><imageobject><imagedata fileref="images/smiley.png"></imagedata></imageobject></inlinemediaobject></para>
|
|
<para>
|
|
We can do the same with classes that were wrapped with Boost.Python. Suppose
|
|
we have a class <literal>point</literal> in C++:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">class</phrase><phrase role="identifier"> point</phrase><phrase role="special"> {...};</phrase><phrase role="identifier">
|
|
|
|
BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="identifier">_geom</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
class_</phrase><phrase role="special"><</phrase><phrase role="identifier">point</phrase><phrase role="special">>(</phrase><phrase role="string">"point"</phrase><phrase role="special">)...;</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
If we are using the technique from the previous session,
|
|
<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>
|
|
<phrase role="identifier">from</phrase><phrase role="identifier"> _geom</phrase><phrase role="identifier"> import</phrase><phrase role="special"> *</phrase>
|
|
|
|
#<phrase role="identifier"> a</phrase><phrase role="identifier"> regular</phrase><phrase role="identifier"> function</phrase><phrase role="identifier">
|
|
def</phrase><phrase role="identifier"> point_str</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special">):</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="identifier"> str</phrase><phrase role="special">((</phrase><phrase role="identifier">self</phrase><phrase role="special">.</phrase><phrase role="identifier">x</phrase><phrase role="special">,</phrase><phrase role="identifier"> self</phrase><phrase role="special">.</phrase><phrase role="identifier">y</phrase><phrase role="special">))</phrase>
|
|
|
|
#<phrase role="identifier"> now</phrase><phrase role="identifier"> we</phrase><phrase role="identifier"> turn</phrase><phrase role="identifier"> it</phrase><phrase role="identifier"> into</phrase><phrase role="identifier"> a</phrase><phrase role="identifier"> member</phrase><phrase role="identifier"> function</phrase><phrase role="identifier">
|
|
point</phrase><phrase role="special">.</phrase><phrase role="identifier">__str__</phrase><phrase role="special"> =</phrase><phrase role="identifier"> point_str</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
<emphasis role="bold">All</emphasis> point instances created from C++ will also have this member function!
|
|
This technique has several advantages:</para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
Cut down compile times to zero for these additional functions
|
|
</listitem><listitem>
|
|
Reduce the memory footprint to virtually zero
|
|
</listitem><listitem>
|
|
Minimize the need to recompile
|
|
</listitem><listitem>
|
|
Rapid prototyping (you can move the code to C++ if required without changing the interface)
|
|
</listitem>
|
|
</itemizedlist><para>
|
|
You can even add a little syntactic sugar with the use of metaclasses. Let's
|
|
create a special metaclass that "injects" methods in other classes.</para>
|
|
<programlisting>
|
|
<literal>
|
|
#<phrase role="identifier"> The</phrase><phrase role="identifier"> one</phrase><phrase role="identifier"> Boost</phrase><phrase role="special">.</phrase><phrase role="identifier">Python</phrase><phrase role="identifier"> uses</phrase><phrase role="keyword"> for</phrase><phrase role="identifier"> all</phrase><phrase role="identifier"> wrapped</phrase><phrase role="identifier"> classes</phrase><phrase role="special">.</phrase>
|
|
#<phrase role="identifier"> You</phrase><phrase role="identifier"> can</phrase><phrase role="identifier"> use</phrase><phrase role="identifier"> here</phrase><phrase role="identifier"> any</phrase><phrase role="keyword"> class</phrase><phrase role="identifier"> exported</phrase><phrase role="identifier"> by</phrase><phrase role="identifier"> Boost</phrase><phrase role="identifier"> instead</phrase><phrase role="identifier"> of</phrase><phrase role="string"> "point"</phrase><phrase role="identifier">
|
|
BoostPythonMetaclass</phrase><phrase role="special"> =</phrase><phrase role="identifier"> point</phrase><phrase role="special">.</phrase><phrase role="identifier">__class__</phrase><phrase role="keyword">
|
|
|
|
class</phrase><phrase role="identifier"> injector</phrase><phrase role="special">(</phrase><phrase role="identifier">object</phrase><phrase role="special">):</phrase><phrase role="keyword">
|
|
class</phrase><phrase role="identifier"> __metaclass__</phrase><phrase role="special">(</phrase><phrase role="identifier">BoostPythonMetaclass</phrase><phrase role="special">):</phrase><phrase role="identifier">
|
|
def</phrase><phrase role="identifier"> __init__</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special">,</phrase><phrase role="identifier"> name</phrase><phrase role="special">,</phrase><phrase role="identifier"> bases</phrase><phrase role="special">,</phrase><phrase role="identifier"> dict</phrase><phrase role="special">):</phrase><phrase role="keyword">
|
|
for</phrase><phrase role="identifier"> b</phrase><phrase role="identifier"> in</phrase><phrase role="identifier"> bases</phrase><phrase role="special">:</phrase><phrase role="keyword">
|
|
if</phrase><phrase role="identifier"> type</phrase><phrase role="special">(</phrase><phrase role="identifier">b</phrase><phrase role="special">)</phrase><phrase role="keyword"> not</phrase><phrase role="identifier"> in</phrase><phrase role="special"> (</phrase><phrase role="identifier">self</phrase><phrase role="special">,</phrase><phrase role="identifier"> type</phrase><phrase role="special">):</phrase><phrase role="keyword">
|
|
for</phrase><phrase role="identifier"> k</phrase><phrase role="special">,</phrase><phrase role="identifier">v</phrase><phrase role="identifier"> in</phrase><phrase role="identifier"> dict</phrase><phrase role="special">.</phrase><phrase role="identifier">items</phrase><phrase role="special">():</phrase><phrase role="identifier">
|
|
setattr</phrase><phrase role="special">(</phrase><phrase role="identifier">b</phrase><phrase role="special">,</phrase><phrase role="identifier">k</phrase><phrase role="special">,</phrase><phrase role="identifier">v</phrase><phrase role="special">)</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="identifier"> type</phrase><phrase role="special">.</phrase><phrase role="identifier">__init__</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special">,</phrase><phrase role="identifier"> name</phrase><phrase role="special">,</phrase><phrase role="identifier"> bases</phrase><phrase role="special">,</phrase><phrase role="identifier"> dict</phrase><phrase role="special">)</phrase>
|
|
|
|
#<phrase role="identifier"> inject</phrase><phrase role="identifier"> some</phrase><phrase role="identifier"> methods</phrase><phrase role="identifier"> in</phrase><phrase role="identifier"> the</phrase><phrase role="identifier"> point</phrase><phrase role="identifier"> foo</phrase><phrase role="keyword">
|
|
class</phrase><phrase role="identifier"> more_point</phrase><phrase role="special">(</phrase><phrase role="identifier">injector</phrase><phrase role="special">,</phrase><phrase role="identifier"> point</phrase><phrase role="special">):</phrase><phrase role="identifier">
|
|
def</phrase><phrase role="identifier"> __repr__</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special">):</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="char"> 'Point(x=%s, y=%s)'</phrase><phrase role="special"> %</phrase><phrase role="special"> (</phrase><phrase role="identifier">self</phrase><phrase role="special">.</phrase><phrase role="identifier">x</phrase><phrase role="special">,</phrase><phrase role="identifier"> self</phrase><phrase role="special">.</phrase><phrase role="identifier">y</phrase><phrase role="special">)</phrase><phrase role="identifier">
|
|
def</phrase><phrase role="identifier"> foo</phrase><phrase role="special">(</phrase><phrase role="identifier">self</phrase><phrase role="special">):</phrase><phrase role="identifier">
|
|
print</phrase><phrase role="char"> 'foo!'</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Now let's see how it got:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="special">>>></phrase><phrase role="identifier"> print</phrase><phrase role="identifier"> point</phrase><phrase role="special">()</phrase><phrase role="identifier">
|
|
Point</phrase><phrase role="special">(</phrase><phrase role="identifier">x</phrase><phrase role="special">=</phrase><phrase role="number">10</phrase><phrase role="special">,</phrase><phrase role="identifier"> y</phrase><phrase role="special">=</phrase><phrase role="number">10</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
>>></phrase><phrase role="identifier"> point</phrase><phrase role="special">().</phrase><phrase role="identifier">foo</phrase><phrase role="special">()</phrase><phrase role="identifier">
|
|
foo</phrase><phrase role="special">!</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Another useful idea is to replace constructors with factory functions:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="identifier">_point</phrase><phrase role="special"> =</phrase><phrase role="identifier"> point</phrase><phrase role="identifier">
|
|
|
|
def</phrase><phrase role="identifier"> point</phrase><phrase role="special">(</phrase><phrase role="identifier">x</phrase><phrase role="special">=</phrase><phrase role="number">0</phrase><phrase role="special">,</phrase><phrase role="identifier"> y</phrase><phrase role="special">=</phrase><phrase role="number">0</phrase><phrase role="special">):</phrase><phrase role="keyword">
|
|
return</phrase><phrase role="identifier"> _point</phrase><phrase role="special">(</phrase><phrase role="identifier">x</phrase><phrase role="special">,</phrase><phrase role="identifier"> y</phrase><phrase role="special">)</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
In this simple case there is not much gained, but for constructurs with
|
|
many overloads and/or arguments this is often a great simplification, again
|
|
with virtually zero memory footprint and zero compile-time overhead for
|
|
the keyword support.</para>
|
|
</section>
|
|
<section id="python.reducing_compiling_time">
|
|
<title>Reducing Compiling Time</title>
|
|
<para>
|
|
If you have ever exported a lot of classes, you know that it takes quite a good
|
|
time to compile the Boost.Python wrappers. Plus the memory consumption can
|
|
easily become too high. If this is causing you problems, you can split the
|
|
class_ definitions in multiple files:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="comment">/* file point.cpp */</phrase><phrase role="preprocessor">
|
|
#include</phrase><phrase role="special"> <</phrase><phrase role="identifier">point</phrase><phrase role="special">.</phrase><phrase role="identifier">h</phrase><phrase role="special">></phrase><phrase role="preprocessor">
|
|
#include</phrase><phrase role="special"> <</phrase><phrase role="identifier">boost</phrase><phrase role="special">/</phrase><phrase role="identifier">python</phrase><phrase role="special">.</phrase><phrase role="identifier">hpp</phrase><phrase role="special">></phrase><phrase role="keyword">
|
|
|
|
void</phrase><phrase role="identifier"> export_point</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
class_</phrase><phrase role="special"><</phrase><phrase role="identifier">point</phrase><phrase role="special">>(</phrase><phrase role="string">"point"</phrase><phrase role="special">)...;</phrase><phrase role="special">
|
|
}</phrase><phrase role="comment">
|
|
|
|
/* file triangle.cpp */</phrase><phrase role="preprocessor">
|
|
#include</phrase><phrase role="special"> <</phrase><phrase role="identifier">triangle</phrase><phrase role="special">.</phrase><phrase role="identifier">h</phrase><phrase role="special">></phrase><phrase role="preprocessor">
|
|
#include</phrase><phrase role="special"> <</phrase><phrase role="identifier">boost</phrase><phrase role="special">/</phrase><phrase role="identifier">python</phrase><phrase role="special">.</phrase><phrase role="identifier">hpp</phrase><phrase role="special">></phrase><phrase role="keyword">
|
|
|
|
void</phrase><phrase role="identifier"> export_triangle</phrase><phrase role="special">()</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
class_</phrase><phrase role="special"><</phrase><phrase role="identifier">triangle</phrase><phrase role="special">>(</phrase><phrase role="string">"triangle"</phrase><phrase role="special">)...;</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Now you create a file <literal>main.cpp</literal>, which contains the <literal>BOOST_PYTHON_MODULE</literal>
|
|
macro, and call the various export functions inside it.</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="keyword">void</phrase><phrase role="identifier"> export_point</phrase><phrase role="special">();</phrase><phrase role="keyword">
|
|
void</phrase><phrase role="identifier"> export_triangle</phrase><phrase role="special">();</phrase><phrase role="identifier">
|
|
|
|
BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="identifier">_geom</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
export_point</phrase><phrase role="special">();</phrase><phrase role="identifier">
|
|
export_triangle</phrase><phrase role="special">();</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
Compiling and linking together all this files produces the same result as the
|
|
usual approach:</para>
|
|
<programlisting>
|
|
<literal>
|
|
<phrase role="preprocessor">#include</phrase><phrase role="special"> <</phrase><phrase role="identifier">boost</phrase><phrase role="special">/</phrase><phrase role="identifier">python</phrase><phrase role="special">.</phrase><phrase role="identifier">hpp</phrase><phrase role="special">></phrase><phrase role="preprocessor">
|
|
#include</phrase><phrase role="special"> <</phrase><phrase role="identifier">point</phrase><phrase role="special">.</phrase><phrase role="identifier">h</phrase><phrase role="special">></phrase><phrase role="preprocessor">
|
|
#include</phrase><phrase role="special"> <</phrase><phrase role="identifier">triangle</phrase><phrase role="special">.</phrase><phrase role="identifier">h</phrase><phrase role="special">></phrase><phrase role="identifier">
|
|
|
|
BOOST_PYTHON_MODULE</phrase><phrase role="special">(</phrase><phrase role="identifier">_geom</phrase><phrase role="special">)</phrase><phrase role="special">
|
|
{</phrase><phrase role="identifier">
|
|
class_</phrase><phrase role="special"><</phrase><phrase role="identifier">point</phrase><phrase role="special">>(</phrase><phrase role="string">"point"</phrase><phrase role="special">)...;</phrase><phrase role="identifier">
|
|
class_</phrase><phrase role="special"><</phrase><phrase role="identifier">triangle</phrase><phrase role="special">>(</phrase><phrase role="string">"triangle"</phrase><phrase role="special">)...;</phrase><phrase role="special">
|
|
}</phrase>
|
|
</literal>
|
|
</programlisting>
|
|
<para>
|
|
but the memory is kept under control.</para>
|
|
<para>
|
|
This method is recommended too if you are developing the C++ library and
|
|
exporting it to Python at the same time: changes in a class will only demand
|
|
the compilation of a single cpp, instead of the entire wrapper code.</para>
|
|
<informaltable frame="all">
|
|
<?dbhtml table-width="74%" ?>
|
|
<tgroup cols="1">
|
|
<tbody>
|
|
<row>
|
|
<entry>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/note.png"></imagedata></imageobject></inlinemediaobject> If you're exporting your classes with <ulink url="../../../../pyste/index.html">Pyste</ulink>,
|
|
take a look at the <literal>--multiple</literal> option, that generates the wrappers in
|
|
various files as demonstrated here.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
<informaltable frame="all">
|
|
<?dbhtml table-width="74%" ?>
|
|
<tgroup cols="1">
|
|
<tbody>
|
|
<row>
|
|
<entry>
|
|
<inlinemediaobject><imageobject><imagedata fileref="images/note.png"></imagedata></imageobject></inlinemediaobject> This method is useful too if you are getting the error message
|
|
<emphasis>"fatal error C1204:Compiler limit:internal structure overflow"</emphasis> when compiling
|
|
a large source file, as explained in the <ulink url="../../../v2/faq.html#c1204">FAQ</ulink>.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
</section></section>
|
|
</library>
|
|
|