mirror of
https://github.com/boostorg/python.git
synced 2026-01-22 05:22:45 +00:00
2041 lines
65 KiB
Plaintext
2041 lines
65 KiB
Plaintext
[library python
|
|
[version 1.0]
|
|
[authors [de Guzman, Joel], [Abrahams, David]]
|
|
[copyright 2002 2003 2004 Joel de Guzman, David Abrahams]
|
|
[category inter-language support]
|
|
[purpose
|
|
Reflects C++ classes and functions into Python
|
|
]
|
|
[license
|
|
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>)
|
|
]
|
|
]
|
|
|
|
[/ QuickBook Document version 0.9 ]
|
|
|
|
[def __note__ [$images/note.png]]
|
|
[def __alert__ [$images/alert.png]]
|
|
[def __tip__ [$images/tip.png]]
|
|
[def :-) [$images/smiley.png]]
|
|
|
|
[section QuickStart]
|
|
|
|
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).
|
|
|
|
[h2 Hello World]
|
|
|
|
Following C/C++ tradition, let's start with the "hello, world". A C++
|
|
Function:
|
|
|
|
char const* greet()
|
|
{
|
|
return "hello, world";
|
|
}
|
|
|
|
can be exposed to Python by writing a Boost.Python wrapper:
|
|
|
|
#include <boost/python.hpp>
|
|
using namespace boost::python;
|
|
|
|
BOOST_PYTHON_MODULE(hello)
|
|
{
|
|
def("greet", greet);
|
|
}
|
|
|
|
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:
|
|
|
|
>>> import hello
|
|
>>> print hello.greet()
|
|
hello, world
|
|
|
|
[:['[*Next stop... Building your Hello World module from start to finish...]]]
|
|
|
|
[endsect]
|
|
[section:hello Building Hello World]
|
|
|
|
[h2 From Start To Finish]
|
|
|
|
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: [*bjam].
|
|
|
|
[blurb __note__ [*Building without bjam]\n\n
|
|
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 [^bjam].\n\n
|
|
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.
|
|
]
|
|
|
|
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: [@../../../building.html building.html].
|
|
After this brief ['bjam] tutorial, we should have built two DLLs:
|
|
|
|
* boost_python.dll
|
|
* hello.pyd
|
|
|
|
if you are on Windows, and
|
|
|
|
* libboost_python.so
|
|
* hello.so
|
|
|
|
if you are on Unix.
|
|
|
|
The tutorial example can be found in the directory:
|
|
[^libs/python/example/tutorial]. There, you can find:
|
|
|
|
* hello.cpp
|
|
* Jamfile
|
|
|
|
The [^hello.cpp] file is our C++ hello world example. The [^Jamfile] is a
|
|
minimalist ['bjam] script that builds the DLLs for us.
|
|
|
|
Before anything else, you should have the bjam executable in your boost
|
|
directory or somewhere in your path such that [^bjam] 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
|
|
[@http://sourceforge.net/project/showfiles.php?group_id=7586 here].
|
|
|
|
[h2 Let's Jam!]
|
|
[$../images/jam.png]
|
|
|
|
Here is our minimalist Jamfile:
|
|
|
|
[pre
|
|
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
|
|
;
|
|
]
|
|
|
|
First, we need to specify our location in the boost project hierarchy.
|
|
It so happens that the tutorial example is located in [^/libs/python/example/tutorial].
|
|
Thus:
|
|
|
|
[pre
|
|
subproject libs/python/example/tutorial ;
|
|
]
|
|
|
|
Then we will include the definitions needed by Python modules:
|
|
|
|
[pre
|
|
SEARCH on python.jam = $(BOOST_BUILD_PATH) ;
|
|
include python.jam ;
|
|
]
|
|
|
|
Finally we declare our [^hello] extension:
|
|
|
|
[pre
|
|
extension hello # Declare a Python extension called hello
|
|
: hello.cpp # source
|
|
<dll>../../build/boost_python # dependencies
|
|
;
|
|
]
|
|
|
|
[h2 Running bjam]
|
|
|
|
['bjam] is run using your operating system's command line interpreter.
|
|
|
|
[:Start it up.]
|
|
|
|
Make sure that the environment is set so that we can invoke the C++
|
|
compiler. With MSVC, that would mean running the [^Vcvars32.bat] batch
|
|
file. For instance:
|
|
|
|
C:\Program Files\Microsoft Visual Studio\VC98\bin\Vcvars32.bat
|
|
|
|
Some environment variables will have to be setup for proper building of our
|
|
Python modules. Example:
|
|
|
|
set PYTHON_ROOT=c:/dev/tools/python
|
|
set PYTHON_VERSION=2.2
|
|
|
|
The above assumes that the Python installation is in [^c:/dev/tools/python]
|
|
and that we are using Python version 2.2. You'll have to tweak this path
|
|
appropriately.
|
|
|
|
[blurb __tip__ Be sure not to include a third number, e.g. [*not] "2.2.1",
|
|
even if that's the version you have.]
|
|
|
|
Now we are ready... Be sure to [^cd] to [^libs/python/example/tutorial]
|
|
where the tutorial [^"hello.cpp"] and the [^"Jamfile"] is situated.
|
|
|
|
Finally:
|
|
|
|
bjam -sTOOLS=msvc
|
|
|
|
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
|
|
[@../../../../../../tools/build/index.html Building Boost Libraries] for
|
|
further details.
|
|
|
|
It should be building now:
|
|
|
|
[pre
|
|
cd C:\dev\boost\libs\python\example\tutorial
|
|
bjam -sTOOLS=msvc
|
|
...patience...
|
|
...found 1703 targets...
|
|
...updating 40 targets...
|
|
]
|
|
|
|
And so on... Finally:
|
|
|
|
[pre
|
|
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...
|
|
]
|
|
|
|
If all is well, you should now have:
|
|
|
|
* boost_python.dll
|
|
* hello.pyd
|
|
|
|
if you are on Windows, and
|
|
|
|
* libboost_python.so
|
|
* hello.so
|
|
|
|
if you are on Unix.
|
|
|
|
[^boost_python.dll] can be found somewhere in [^libs\python\build\bin]
|
|
while [^hello.pyd] can be found somewhere in
|
|
[^libs\python\example\tutorial\bin]. 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.
|
|
|
|
You may now fire up Python and run our hello module:
|
|
|
|
>>> import hello
|
|
>>> print hello.greet()
|
|
hello, world
|
|
|
|
[:[*There you go... Have fun!]]
|
|
|
|
[endsect]
|
|
[section:exposing Exposing Classes]
|
|
|
|
Now let's expose a C++ class to Python.
|
|
|
|
Consider a C++ class/struct that we want to expose to Python:
|
|
|
|
struct World
|
|
{
|
|
void set(std::string msg) { this->msg = msg; }
|
|
std::string greet() { return msg; }
|
|
std::string msg;
|
|
};
|
|
|
|
We can expose this to Python by writing a corresponding Boost.Python
|
|
C++ Wrapper:
|
|
|
|
#include <boost/python.hpp>
|
|
using namespace boost::python;
|
|
|
|
BOOST_PYTHON_MODULE(hello)
|
|
{
|
|
class_<World>("World")
|
|
.def("greet", &World::greet)
|
|
.def("set", &World::set)
|
|
;
|
|
}
|
|
|
|
Here, we wrote a C++ class wrapper that exposes the member functions
|
|
[^greet] and [^set]. Now, after building our module as a shared library, we
|
|
may use our class [^World] in Python. Here's a sample Python session:
|
|
|
|
>>> import hello
|
|
>>> planet = hello.World()
|
|
>>> planet.set('howdy')
|
|
>>> planet.greet()
|
|
'howdy'
|
|
|
|
[section Constructors]
|
|
|
|
Our previous example didn't have any explicit constructors.
|
|
Since [^World] 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
|
|
|
|
>>> planet = hello.World()
|
|
|
|
We may wish to wrap a class with a non-default constructor. Let us
|
|
build on our previous example:
|
|
|
|
struct World
|
|
{
|
|
World(std::string msg): msg(msg) {} // added constructor
|
|
void set(std::string msg) { this->msg = msg; }
|
|
std::string greet() { return msg; }
|
|
std::string msg;
|
|
};
|
|
|
|
This time [^World] has no default constructor; our previous
|
|
wrapping code would fail to compile when the library tried to expose
|
|
it. We have to tell [^class_<World>] about the constructor we want to
|
|
expose instead.
|
|
|
|
#include <boost/python.hpp>
|
|
using namespace boost::python;
|
|
|
|
BOOST_PYTHON_MODULE(hello)
|
|
{
|
|
class_<World>("World", init<std::string>())
|
|
.def("greet", &World::greet)
|
|
.def("set", &World::set)
|
|
;
|
|
}
|
|
|
|
[^init<std::string>()] exposes the constructor taking in a
|
|
[^std::string] (in Python, constructors are spelled
|
|
"[^"__init__"]").
|
|
|
|
We can expose additional constructors by passing more [^init<...>]s to
|
|
the [^def()] member function. Say for example we have another World
|
|
constructor taking in two doubles:
|
|
|
|
class_<World>("World", init<std::string>())
|
|
.def(init<double, double>())
|
|
.def("greet", &World::greet)
|
|
.def("set", &World::set)
|
|
;
|
|
|
|
On the other hand, if we do not wish to expose any constructors at
|
|
all, we may use [^no_init] instead:
|
|
|
|
class_<Abstract>("Abstract", no_init)
|
|
|
|
This actually adds an [^__init__] method which always raises a
|
|
Python RuntimeError exception.
|
|
|
|
[endsect]
|
|
[section Class Data Members]
|
|
|
|
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 [*read-only] or
|
|
[*read-write]. Consider this class [^Var]:
|
|
|
|
struct Var
|
|
{
|
|
Var(std::string name) : name(name), value() {}
|
|
std::string const name;
|
|
float value;
|
|
};
|
|
|
|
Our C++ [^Var] class and its data members can be exposed to Python:
|
|
|
|
class_<Var>("Var", init<std::string>())
|
|
.def_readonly("name", &Var::name)
|
|
.def_readwrite("value", &Var::value);
|
|
|
|
Then, in Python, assuming we have placed our Var class inside the namespace
|
|
hello as we did before:
|
|
|
|
>>> x = hello.Var('pi')
|
|
>>> x.value = 3.14
|
|
>>> print x.name, 'is around', x.value
|
|
pi is around 3.14
|
|
|
|
Note that [^name] is exposed as [*read-only] while [^value] is exposed
|
|
as [*read-write].
|
|
|
|
[pre
|
|
>>> x.name = 'e' # can't change name
|
|
Traceback (most recent call last):
|
|
File "<stdin>", line 1, in ?
|
|
AttributeError: can't set attribute
|
|
]
|
|
|
|
[endsect]
|
|
[section Class Properties]
|
|
|
|
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:
|
|
|
|
struct Num
|
|
{
|
|
Num();
|
|
float get() const;
|
|
void set(float value);
|
|
...
|
|
};
|
|
|
|
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
|
|
[^Num] class using Boost.Python:
|
|
|
|
class_<Num>("Num")
|
|
.add_property("rovalue", &Num::get)
|
|
.add_property("value", &Num::get, &Num::set);
|
|
|
|
And at last, in Python:
|
|
|
|
>>> x = Num()
|
|
>>> x.value = 3.14
|
|
>>> x.value, x.rovalue
|
|
(3.14, 3.14)
|
|
>>> x.rovalue = 2.17 # error!
|
|
|
|
Take note that the class property [^rovalue] is exposed as [*read-only]
|
|
since the [^rovalue] setter member function is not passed in:
|
|
|
|
.add_property("rovalue", &Num::get)
|
|
|
|
[endsect]
|
|
[section Inheritance]
|
|
|
|
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.
|
|
|
|
Consider this trivial inheritance structure:
|
|
|
|
struct Base { virtual ~Base(); };
|
|
struct Derived : Base {};
|
|
|
|
And a set of C++ functions operating on [^Base] and [^Derived] object
|
|
instances:
|
|
|
|
void b(Base*);
|
|
void d(Derived*);
|
|
Base* factory() { return new Derived; }
|
|
|
|
We've seen how we can wrap the base class [^Base]:
|
|
|
|
class_<Base>("Base")
|
|
/*...*/
|
|
;
|
|
|
|
Now we can inform Boost.Python of the inheritance relationship between
|
|
[^Derived] and its base class [^Base]. Thus:
|
|
|
|
class_<Derived, bases<Base> >("Derived")
|
|
/*...*/
|
|
;
|
|
|
|
Doing so, we get some things for free:
|
|
|
|
# Derived automatically inherits all of Base's Python methods
|
|
(wrapped C++ member functions)
|
|
# [*If] Base is polymorphic, [^Derived] objects which have been passed to
|
|
Python via a pointer or reference to [^Base] can be passed where a pointer
|
|
or reference to [^Derived] is expected.
|
|
|
|
Now, we shall expose the C++ free functions [^b] and [^d] and [^factory]:
|
|
|
|
def("b", b);
|
|
def("d", d);
|
|
def("factory", factory);
|
|
|
|
Note that free function [^factory] is being used to generate new
|
|
instances of class [^Derived]. In such cases, we use
|
|
[^return_value_policy<manage_new_object>] to instruct Python to adopt
|
|
the pointer to [^Base] and hold the instance in a new Python [^Base]
|
|
object until the the Python object is destroyed. We shall see more of
|
|
Boost.Python [link python.call_policies call policies] later.
|
|
|
|
// Tell Python to take ownership of factory's result
|
|
def("factory", factory,
|
|
return_value_policy<manage_new_object>());
|
|
|
|
[endsect]
|
|
|
|
[section Class Virtual Functions]
|
|
|
|
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 [^Base] class:
|
|
|
|
struct Base
|
|
{
|
|
virtual ~Base() {}
|
|
virtual int f() = 0;
|
|
};
|
|
|
|
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
|
|
`Base`. Yet, when you have a virtual function that's going to be overridden in
|
|
Python and called polymorphically *from C++*, we'll need to add some
|
|
scaffoldings to make things work properly. What we'll do is write a class
|
|
wrapper that derives from `Base` that will unintrusively hook into the virtual
|
|
functions so that a Python override may be called:
|
|
|
|
struct BaseWrap : Base, wrapper<Base>
|
|
{
|
|
int f()
|
|
{
|
|
return this->get_override("f")();
|
|
}
|
|
};
|
|
|
|
Notice too that in addition to inheriting from `Base`, we also multiply-
|
|
inherited `wrapper<Base>` (See [@../../../v2/wrapper.html Wrapper]). The
|
|
`wrapper` template makes the job of wrapping classes that are meant to
|
|
overridden in Python, easier.
|
|
|
|
[blurb __alert__ MSVC6/7 Workaround\n\n
|
|
If you are using Microsoft Visual C++ 6 or 7, you have to write `f` as:\n\n
|
|
`return call<int>(this->get_override("f").ptr());`.]
|
|
|
|
BaseWrap's overridden virtual member function `f` in effect calls the
|
|
corresponding method of the Python object through `get_override`.
|
|
|
|
Finally, exposing `Base`:
|
|
|
|
class_<BaseWrap, boost::noncopyable>("Base")
|
|
.def("f", pure_virtual(&Base::f))
|
|
;
|
|
|
|
`pure_virtual` signals Boost.Python that the function `f` is a pure virtual
|
|
function.
|
|
|
|
[blurb __note__ [*member function and methods]\n\n Python, like
|
|
many object oriented languages uses the term [*methods]. Methods
|
|
correspond roughly to C++'s [*member functions]]
|
|
|
|
[endsect]
|
|
|
|
[section Virtual Functions with Default Implementations]
|
|
|
|
We've seen in the previous section how classes with pure virtual functions are
|
|
wrapped using Boost.Python's [@../../../v2/wrapper.html class wrapper]
|
|
facilities. If we wish to wrap [*non]-pure-virtual functions instead, the
|
|
mechanism is a bit different.
|
|
|
|
Recall that in the [link python.class_virtual_functions previous section], we
|
|
wrapped a class with a pure virtual function that we then implemented in C++, or
|
|
Python classes derived from it. Our base class:
|
|
|
|
struct Base
|
|
{
|
|
virtual int f() = 0;
|
|
};
|
|
|
|
had a pure virtual function [^f]. If, however, its member function [^f] was
|
|
not declared as pure virtual:
|
|
|
|
struct Base
|
|
{
|
|
virtual ~Base() {}
|
|
virtual int f() { return 0; }
|
|
};
|
|
|
|
We wrap it this way:
|
|
|
|
struct BaseWrap : Base, wrapper<Base>
|
|
{
|
|
int f()
|
|
{
|
|
if (override f = this->get_override("f"))
|
|
return f(); // *note*
|
|
return Base::f();
|
|
}
|
|
|
|
int default_f() { return this->Base::f(); }
|
|
};
|
|
|
|
Notice how we implemented `BaseWrap::f`. Now, we have to check if there is an
|
|
override for `f`. If none, then we call `Base::f()`.
|
|
|
|
[blurb __alert__ MSVC6/7 Workaround\n\n
|
|
If you are using Microsoft Visual C++ 6 or 7, you have to rewrite the line
|
|
with the `*note*` as:\n\n
|
|
`return call<char const*>(f.ptr());`.]
|
|
|
|
Finally, exposing:
|
|
|
|
class_<BaseWrap, boost::noncopyable>("Base")
|
|
.def("f", &Base::f, &BaseWrap::default_f)
|
|
;
|
|
|
|
Take note that we expose both `&Base::f` and `&BaseWrap::default_f`.
|
|
Boost.Python needs to keep track of 1) the dispatch function [^f] and 2) the
|
|
forwarding function to its default implementation [^default_f]. There's a
|
|
special [^def] function for this purpose.
|
|
|
|
In Python, the results would be as expected:
|
|
|
|
>>> base = Base()
|
|
>>> class Derived(Base):
|
|
... def f(self):
|
|
... return 42
|
|
...
|
|
>>> derived = Derived()
|
|
|
|
Calling [^base.f()]:
|
|
|
|
>>> base.f()
|
|
0
|
|
|
|
Calling [^derived.f()]:
|
|
|
|
>>> derived.f()
|
|
42
|
|
|
|
[endsect]
|
|
[section Class Operators/Special Functions]
|
|
|
|
[h2 Python Operators]
|
|
|
|
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.
|
|
|
|
Consider a file position class [^FilePos] and a set of operators that take
|
|
on FilePos instances:
|
|
|
|
class FilePos { /*...*/ };
|
|
|
|
FilePos operator+(FilePos, int);
|
|
FilePos operator+(int, FilePos);
|
|
int operator-(FilePos, FilePos);
|
|
FilePos operator-(FilePos, int);
|
|
FilePos& operator+=(FilePos&, int);
|
|
FilePos& operator-=(FilePos&, int);
|
|
bool operator<(FilePos, FilePos);
|
|
|
|
The class and the various operators can be mapped to Python rather easily
|
|
and intuitively:
|
|
|
|
class_<FilePos>("FilePos")
|
|
.def(self + int()) // __add__
|
|
.def(int() + self) // __radd__
|
|
.def(self - self) // __sub__
|
|
.def(self - int()) // __sub__
|
|
.def(self += int()) // __iadd__
|
|
.def(self -= other<int>())
|
|
.def(self < self); // __lt__
|
|
|
|
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 [^self] refers to FilePos object. Also, not every class [^T] that
|
|
you might need to interact with in an operator expression is (cheaply)
|
|
default-constructible. You can use [^other<T>()] in place of an actual
|
|
[^T] instance when writing "self expressions".
|
|
|
|
[h2 Special Methods]
|
|
|
|
Python has a few more ['Special Methods]. 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 ['special functions]. Example:
|
|
|
|
class Rational
|
|
{ operator double() const; };
|
|
|
|
Rational pow(Rational, Rational);
|
|
Rational abs(Rational);
|
|
ostream& operator<<(ostream&,Rational);
|
|
|
|
class_<Rational>()
|
|
.def(float_(self)) // __float__
|
|
.def(pow(self, other<Rational>)) // __pow__
|
|
.def(abs(self)) // __abs__
|
|
.def(str(self)) // __str__
|
|
;
|
|
|
|
Need we say more?
|
|
|
|
[blurb __note__ What is the business of [^operator<<] [^.def(str(self))]?
|
|
Well, the method [^str] requires the [^operator<<] to do its work (i.e.
|
|
[^operator<<] is used by the method defined by def(str(self)).]
|
|
|
|
[endsect]
|
|
[endsect] [/ Exposing Classes ]
|
|
|
|
[section Functions]
|
|
|
|
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.
|
|
|
|
[:['Read on...]]
|
|
|
|
But before you do, you might want to fire up Python 2.2 or later and type
|
|
[^>>> import this].
|
|
|
|
[pre
|
|
>>> 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 *right* 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!
|
|
]
|
|
|
|
[section Call Policies]
|
|
|
|
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:
|
|
|
|
X& f(Y& y, Z* z);
|
|
|
|
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
|
|
|
|
>>> x = f(y, z) # x refers to some C++ X
|
|
>>> del y
|
|
>>> x.some_method() # CRASH!
|
|
|
|
What's the problem?
|
|
|
|
Well, what if f() was implemented as shown below:
|
|
|
|
X& f(Y& y, Z* z)
|
|
{
|
|
y.z = z;
|
|
return y.x;
|
|
}
|
|
|
|
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.
|
|
|
|
Here's what's happening:
|
|
|
|
# [^f] is called passing in a reference to [^y] and a pointer to [^z]
|
|
# A reference to [^y.x] is returned
|
|
# [^y] is deleted. [^x] is a dangling reference
|
|
# [^x.some_method()] is called
|
|
# [*BOOM!]
|
|
|
|
We could copy result into a new object:
|
|
|
|
>>> f(y, z).set(42) # Result disappears
|
|
>>> y.x.get() # No crash, but still bad
|
|
3.14
|
|
|
|
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.
|
|
|
|
Our problems do not end there. Suppose Y is implemented as follows:
|
|
|
|
struct Y
|
|
{
|
|
X x; Z* z;
|
|
int z_value() { return z->value(); }
|
|
};
|
|
|
|
Notice that the data member [^z] is held by class Y using a raw
|
|
pointer. Now we have a potential dangling pointer problem inside Y:
|
|
|
|
>>> x = f(y, z) # y refers to z
|
|
>>> del z # Kill the z object
|
|
>>> y.z_value() # CRASH!
|
|
|
|
For reference, here's the implementation of [^f] again:
|
|
|
|
X& f(Y& y, Z* z)
|
|
{
|
|
y.z = z;
|
|
return y.x;
|
|
}
|
|
|
|
Here's what's happening:
|
|
|
|
# [^f] is called passing in a reference to [^y] and a pointer to [^z]
|
|
# A pointer to [^z] is held by [^y]
|
|
# A reference to [^y.x] is returned
|
|
# [^z] is deleted. [^y.z] is a dangling pointer
|
|
# [^y.z_value()] is called
|
|
# [^z->value()] is called
|
|
# [*BOOM!]
|
|
|
|
[h2 Call Policies]
|
|
|
|
Call Policies may be used in situations such as the example detailed above.
|
|
In our example, [^return_internal_reference] and [^with_custodian_and_ward]
|
|
are our friends:
|
|
|
|
def("f", f,
|
|
return_internal_reference<1,
|
|
with_custodian_and_ward<1, 2> >());
|
|
|
|
What are the [^1] and [^2] parameters, you ask?
|
|
|
|
return_internal_reference<1
|
|
|
|
Informs Boost.Python that the first argument, in our case [^Y& y], is the
|
|
owner of the returned reference: [^X&]. The "[^1]" simply specifies the
|
|
first argument. In short: "return an internal reference [^X&] owned by the
|
|
1st argument [^Y& y]".
|
|
|
|
with_custodian_and_ward<1, 2>
|
|
|
|
Informs Boost.Python that the lifetime of the argument indicated by ward
|
|
(i.e. the 2nd argument: [^Z* z]) is dependent on the lifetime of the
|
|
argument indicated by custodian (i.e. the 1st argument: [^Y& y]).
|
|
|
|
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:
|
|
|
|
policy1<args...,
|
|
policy2<args...,
|
|
policy3<args...> > >
|
|
|
|
Here is the list of predefined call policies. A complete reference detailing
|
|
these can be found [@../../../v2/reference.html#models_of_call_policies here].
|
|
|
|
* [*with_custodian_and_ward]\n Ties lifetimes of the arguments
|
|
* [*with_custodian_and_ward_postcall]\n Ties lifetimes of the arguments and results
|
|
* [*return_internal_reference]\n Ties lifetime of one argument to that of result
|
|
* [*return_value_policy<T> with T one of:]\n
|
|
* [*reference_existing_object]\nnaive (dangerous) approach
|
|
* [*copy_const_reference]\nBoost.Python v1 approach
|
|
* [*copy_non_const_reference]\n
|
|
* [*manage_new_object]\n Adopt a pointer and hold the instance
|
|
|
|
[blurb :-) [*Remember the Zen, Luke:]\n\n
|
|
"Explicit is better than implicit"\n
|
|
"In the face of ambiguity, refuse the temptation to guess"\n]
|
|
|
|
[endsect]
|
|
[section Overloading]
|
|
|
|
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.
|
|
|
|
We have here our C++ class:
|
|
|
|
struct X
|
|
{
|
|
bool f(int a)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool f(int a, double b)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool f(int a, double b, char c)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
int f(int a, int b, int c)
|
|
{
|
|
return a + b + c;
|
|
};
|
|
};
|
|
|
|
Class X has 4 overloaded functions. We shall start by introducing some
|
|
member function pointer variables:
|
|
|
|
bool (X::*fx1)(int) = &X::f;
|
|
bool (X::*fx2)(int, double) = &X::f;
|
|
bool (X::*fx3)(int, double, char)= &X::f;
|
|
int (X::*fx4)(int, int, int) = &X::f;
|
|
|
|
With these in hand, we can proceed to define and wrap this for Python:
|
|
|
|
.def("f", fx1)
|
|
.def("f", fx2)
|
|
.def("f", fx3)
|
|
.def("f", fx4)
|
|
|
|
[endsect]
|
|
[section Default Arguments]
|
|
|
|
Boost.Python wraps (member) function pointers. Unfortunately, C++ function
|
|
pointers carry no default argument info. Take a function [^f] with default
|
|
arguments:
|
|
|
|
int f(int, double = 3.14, char const* = "hello");
|
|
|
|
But the type of a pointer to the function [^f] has no information
|
|
about its default arguments:
|
|
|
|
int(*g)(int,double,char const*) = f; // defaults lost!
|
|
|
|
When we pass this function pointer to the [^def] function, there is no way
|
|
to retrieve the default arguments:
|
|
|
|
def("f", f); // defaults lost!
|
|
|
|
Because of this, when wrapping C++ code, we had to resort to manual
|
|
wrapping as outlined in the [link python.overloading previous section], or
|
|
writing thin wrappers:
|
|
|
|
// write "thin wrappers"
|
|
int f1(int x) { f(x); }
|
|
int f2(int x, double y) { f(x,y); }
|
|
|
|
/*...*/
|
|
|
|
// in module init
|
|
def("f", f); // all arguments
|
|
def("f", f2); // two arguments
|
|
def("f", f1); // one argument
|
|
|
|
When you want to wrap functions (or member functions) that either:
|
|
|
|
* have default arguments, or
|
|
* are overloaded with a common sequence of initial arguments
|
|
|
|
[h2 BOOST_PYTHON_FUNCTION_OVERLOADS]
|
|
|
|
Boost.Python now has a way to make it easier. For instance, given a function:
|
|
|
|
int foo(int a, char b = 1, unsigned c = 2, double d = 3)
|
|
{
|
|
/*...*/
|
|
}
|
|
|
|
The macro invocation:
|
|
|
|
BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads, foo, 1, 4)
|
|
|
|
will automatically create the thin wrappers for us. This macro will create
|
|
a class [^foo_overloads] that can be passed on to [^def(...)]. The third
|
|
and fourth macro argument are the minimum arguments and maximum arguments,
|
|
respectively. In our [^foo] function the minimum number of arguments is 1
|
|
and the maximum number of arguments is 4. The [^def(...)] function will
|
|
automatically add all the foo variants for us:
|
|
|
|
def("foo", foo, foo_overloads());
|
|
|
|
[h2 BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS]
|
|
|
|
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.
|
|
|
|
Like [^BOOST_PYTHON_FUNCTION_OVERLOADS],
|
|
[^BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS] may be used to automatically create
|
|
the thin wrappers for wrapping member functions. Let's have an example:
|
|
|
|
struct george
|
|
{
|
|
void
|
|
wack_em(int a, int b = 0, char c = 'x')
|
|
{
|
|
/*...*/
|
|
}
|
|
};
|
|
|
|
The macro invocation:
|
|
|
|
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(george_overloads, wack_em, 1, 3)
|
|
|
|
will generate a set of thin wrappers for george's [^wack_em] 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
|
|
[^george_overloads] that can then be used as an argument to [^def(...)]:
|
|
|
|
.def("wack_em", &george::wack_em, george_overloads());
|
|
|
|
See the [@../../../v2/overloads.html#BOOST_PYTHON_FUNCTION_OVERLOADS-spec overloads reference]
|
|
for details.
|
|
|
|
[h2 init and optional]
|
|
|
|
A similar facility is provided for class constructors, again, with
|
|
default arguments or a sequence of overloads. Remember [^init<...>]? For example,
|
|
given a class X with a constructor:
|
|
|
|
struct X
|
|
{
|
|
X(int a, char b = 'D', std::string c = "constructor", double d = 0.0);
|
|
/*...*/
|
|
}
|
|
|
|
You can easily add this constructor to Boost.Python in one shot:
|
|
|
|
.def(init<int, optional<char, std::string, double> >())
|
|
|
|
Notice the use of [^init<...>] and [^optional<...>] to signify the default
|
|
(optional arguments).
|
|
|
|
[endsect]
|
|
[section Auto-Overloading]
|
|
|
|
It was mentioned in passing in the previous section that
|
|
[^BOOST_PYTHON_FUNCTION_OVERLOADS] and [^BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS]
|
|
can also be used for overloaded functions and member functions with a
|
|
common sequence of initial arguments. Here is an example:
|
|
|
|
void foo()
|
|
{
|
|
/*...*/
|
|
}
|
|
|
|
void foo(bool a)
|
|
{
|
|
/*...*/
|
|
}
|
|
|
|
void foo(bool a, int b)
|
|
{
|
|
/*...*/
|
|
}
|
|
|
|
void foo(bool a, int b, char c)
|
|
{
|
|
/*...*/
|
|
}
|
|
|
|
Like in the previous section, we can generate thin wrappers for these
|
|
overloaded functions in one-shot:
|
|
|
|
BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads, foo, 0, 3)
|
|
|
|
Then...
|
|
|
|
.def("foo", foo, foo_overloads());
|
|
|
|
Notice though that we have a situation now where we have a minimum of zero
|
|
(0) arguments and a maximum of 3 arguments.
|
|
|
|
[h2 Manual Wrapping]
|
|
|
|
It is important to emphasize however that [*the overloaded functions must
|
|
have a common sequence of initial arguments]. Otherwise, our scheme above
|
|
will not work. If this is not the case, we have to wrap our functions
|
|
[link python.overloading manually].
|
|
|
|
Actually, we can mix and match manual wrapping of overloaded functions and
|
|
automatic wrapping through [^BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS] and
|
|
its sister, [^BOOST_PYTHON_FUNCTION_OVERLOADS]. Following up on our example
|
|
presented in the section [link python.overloading on overloading], since the
|
|
first 4 overload functins have a common sequence of initial arguments, we
|
|
can use [^BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS] to automatically wrap the
|
|
first three of the [^def]s and manually wrap just the last. Here's
|
|
how we'll do this:
|
|
|
|
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(xf_overloads, f, 1, 4)
|
|
|
|
Create a member function pointers as above for both X::f overloads:
|
|
|
|
bool (X::*fx1)(int, double, char) = &X::f;
|
|
int (X::*fx2)(int, int, int) = &X::f;
|
|
|
|
Then...
|
|
|
|
.def("f", fx1, xf_overloads());
|
|
.def("f", fx2)
|
|
|
|
[endsect]
|
|
[endsect] [/ Functions ]
|
|
|
|
[section:object Object Interface]
|
|
|
|
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 [^object]. We shall see in
|
|
this chapter how to deal with Python objects.
|
|
|
|
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++ [^object]s are as close as possible to Python. This
|
|
should minimize the learning curve significantly.
|
|
|
|
[$images/python.png]
|
|
|
|
[section Basic Interface]
|
|
|
|
Class [^object] wraps [^PyObject*]. All the intricacies of dealing with
|
|
[^PyObject]s such as managing reference counting are handled by the
|
|
[^object] class. C++ object interoperability is seamless. Boost.Python C++
|
|
[^object]s can in fact be explicitly constructed from any C++ object.
|
|
|
|
To illustrate, this Python code snippet:
|
|
|
|
def f(x, y):
|
|
if (y == 'foo'):
|
|
x[3:7] = 'bar'
|
|
else:
|
|
x.items += y(3, x)
|
|
return x
|
|
|
|
def getfunc():
|
|
return f;
|
|
|
|
Can be rewritten in C++ using Boost.Python facilities this way:
|
|
|
|
object f(object x, object y) {
|
|
if (y == "foo")
|
|
x.slice(3,7) = "bar";
|
|
else
|
|
x.attr("items") += y(3, x);
|
|
return x;
|
|
}
|
|
object getfunc() {
|
|
return object(f);
|
|
}
|
|
|
|
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.
|
|
|
|
[endsect]
|
|
[section Derived Object types]
|
|
|
|
Boost.Python comes with a set of derived [^object] types corresponding to
|
|
that of Python's:
|
|
|
|
* list
|
|
* dict
|
|
* tuple
|
|
* str
|
|
* long_
|
|
* enum
|
|
|
|
These derived [^object] types act like real Python types. For instance:
|
|
|
|
str(1) ==> "1"
|
|
|
|
Wherever appropriate, a particular derived [^object] has corresponding
|
|
Python type's methods. For instance, [^dict] has a [^keys()] method:
|
|
|
|
d.keys()
|
|
|
|
[^make_tuple] is provided for declaring ['tuple literals]. Example:
|
|
|
|
make_tuple(123, 'D', "Hello, World", 0.0);
|
|
|
|
In C++, when Boost.Python [^object]s are used as arguments to functions,
|
|
subtype matching is required. For example, when a function [^f], as
|
|
declared below, is wrapped, it will only accept instances of Python's
|
|
[^str] type and subtypes.
|
|
|
|
void f(str name)
|
|
{
|
|
object n2 = name.attr("upper")(); // NAME = name.upper()
|
|
str NAME = name.upper(); // better
|
|
object msg = "%s is bigger than %s" % make_tuple(NAME,name);
|
|
}
|
|
|
|
In finer detail:
|
|
|
|
str NAME = name.upper();
|
|
|
|
Illustrates that we provide versions of the str type's methods as C++
|
|
member functions.
|
|
|
|
object msg = "%s is bigger than %s" % make_tuple(NAME,name);
|
|
|
|
Demonstrates that you can write the C++ equivalent of [^"format" % x,y,z]
|
|
in Python, which is useful since there's no easy way to do that in std C++.
|
|
|
|
__alert__ [*Beware] the common pitfall of forgetting that the constructors
|
|
of most of Python's mutable types make copies, just as in Python.
|
|
|
|
Python:
|
|
|
|
>>> d = dict(x.__dict__) # copies x.__dict__
|
|
>>> d['whatever'] # modifies the copy
|
|
|
|
C++:
|
|
|
|
dict d(x.attr("__dict__")); # copies x.__dict__
|
|
d['whatever'] = 3; # modifies the copy
|
|
|
|
[h2 class_<T> as objects]
|
|
|
|
Due to the dynamic nature of Boost.Python objects, any [^class_<T>] may
|
|
also be one of these types! The following code snippet wraps the class
|
|
(type) object.
|
|
|
|
We can use this to create wrapped instances. Example:
|
|
|
|
object vec345 = (
|
|
class_<Vec2>("Vec2", init<double, double>())
|
|
.def_readonly("length", &Point::length)
|
|
.def_readonly("angle", &Point::angle)
|
|
)(3.0, 4.0);
|
|
|
|
assert(vec345.attr("length") == 5.0);
|
|
|
|
[endsect]
|
|
[section Extracting C++ objects]
|
|
|
|
At some point, we will need to get C++ values out of object instances. This
|
|
can be achieved with the [^extract<T>] function. Consider the following:
|
|
|
|
double x = o.attr("length"); // compile error
|
|
|
|
In the code above, we got a compiler error because Boost.Python
|
|
[^object] can't be implicitly converted to [^double]s. Instead, what
|
|
we wanted to do above can be achieved by writing:
|
|
|
|
double l = extract<double>(o.attr("length"));
|
|
Vec2& v = extract<Vec2&>(o);
|
|
assert(l == v.length());
|
|
|
|
The first line attempts to extract the "length" attribute of the
|
|
Boost.Python [^object] [^o]. The second line attempts to ['extract] the
|
|
[^Vec2] object from held by the Boost.Python [^object] [^o].
|
|
|
|
Take note that we said "attempt to" above. What if the Boost.Python
|
|
[^object] [^o] does not really hold a [^Vec2] type? This is certainly
|
|
a possibility considering the dynamic nature of Python [^object]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:
|
|
|
|
extract<Vec2&> x(o);
|
|
if (x.check()) {
|
|
Vec2& v = x(); ...
|
|
|
|
__tip__ The astute reader might have noticed that the [^extract<T>]
|
|
facility in fact solves the mutable copying problem:
|
|
|
|
dict d = extract<dict>(x.attr("__dict__"));
|
|
d['whatever'] = 3; # modifies x.__dict__ !
|
|
|
|
|
|
[endsect]
|
|
[section Enums]
|
|
|
|
Boost.Python has a nifty facility to capture and wrap C++ enums. While
|
|
Python has no [^enum] type, we'll often want to expose our C++ enums to
|
|
Python as an [^int]. 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:
|
|
|
|
enum choice { red, blue };
|
|
|
|
the construct:
|
|
|
|
enum_<choice>("choice")
|
|
.value("red", red)
|
|
.value("blue", blue)
|
|
;
|
|
|
|
can be used to expose to Python. The new enum type is created in the
|
|
current [^scope()], which is usually the current module. The snippet above
|
|
creates a Python class derived from Python's [^int] type which is
|
|
associated with the C++ type passed as its first parameter.
|
|
|
|
[blurb __note__ [*what is a scope?]\n\n 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 [@../../../v2/scope.html here].]
|
|
|
|
You can access those values in Python as
|
|
|
|
>>> my_module.choice.red
|
|
my_module.choice.red
|
|
|
|
where my_module is the module where the enum is declared. You can also
|
|
create a new scope around a class:
|
|
|
|
scope in_X = class_<X>("X")
|
|
.def( ... )
|
|
.def( ... )
|
|
;
|
|
|
|
// Expose X::nested as X.nested
|
|
enum_<X::nested>("nested")
|
|
.value("red", red)
|
|
.value("blue", blue)
|
|
;
|
|
|
|
[def Py_Initialize [@http://www.python.org/doc/current/api/initialization.html#l2h-652 Py_Initialize]]
|
|
[def Py_Finalize [@http://www.python.org/doc/current/api/initialization.html#l2h-656 Py_Finalize]]
|
|
[def PyRun_String [@http://www.python.org/doc/current/api/veryhigh.html#l2h-55 PyRun_String]]
|
|
[def PyRun_File [@http://www.python.org/doc/current/api/veryhigh.html#l2h-56 PyRun_File]]
|
|
[def Py_eval_input [@http://www.python.org/doc/current/api/veryhigh.html#l2h-58 Py_eval_input]]
|
|
[def Py_file_input [@http://www.python.org/doc/current/api/veryhigh.html#l2h-59 Py_file_input]]
|
|
[def Py_single_input [@http://www.python.org/doc/current/api/veryhigh.html#l2h-60 Py_single_input]]
|
|
[def Py_XINCREF [@http://www.python.org/doc/current/api/countingRefs.html#l2h-65 Py_XINCREF]]
|
|
[def Py_XDECREF [@http://www.python.org/doc/current/api/countingRefs.html#l2h-67 Py_XDECREF]]
|
|
[def PyImport_AppendInittab [@http://www.python.org/doc/current/api/importing.html#l2h-137 PyImport_AppendInittab]]
|
|
[def PyImport_AddModule [@http://www.python.org/doc/current/api/importing.html#l2h-125 PyImport_AddModule]]
|
|
[def PyModule_New [@http://www.python.org/doc/current/api/moduleObjects.html#l2h-591 PyModule_New]]
|
|
[def PyModule_GetDict [@http://www.python.org/doc/current/api/moduleObjects.html#l2h-594 PyModule_GetDict]]
|
|
|
|
[endsect]
|
|
[endsect] [/ Object Interface]
|
|
|
|
[section Embedding]
|
|
|
|
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 ['embed] the Python interpreter
|
|
into your C++ program.
|
|
|
|
Currently, Boost.Python does not directly support everything you'll need
|
|
when embedding. Therefore you'll need to use the
|
|
[@http://www.python.org/doc/current/api/api.html Python/C API] 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... :-)
|
|
|
|
[h2 Building embedded programs]
|
|
|
|
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.
|
|
|
|
Boost.Python's static link library comes in two variants. Both are located
|
|
in Boost's [^/libs/python/build/bin-stage] subdirectory. On Windows, the
|
|
variants are called [^boost_python.lib] (for release builds) and
|
|
[^boost_python_debug.lib] (for debugging). If you can't find the libraries,
|
|
you probably haven't built Boost.Python yet. See
|
|
[@../../../building.html Building and Testing] on how to do this.
|
|
|
|
Python's static link library can be found in the [^/libs] subdirectory of
|
|
your Python directory. On Windows it is called pythonXY.lib where X.Y is
|
|
your major Python version number.
|
|
|
|
Additionally, Python's [^/include] subdirectory has to be added to your
|
|
include path.
|
|
|
|
In a Jamfile, all the above boils down to:
|
|
|
|
[pre
|
|
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) ;
|
|
]
|
|
|
|
[h2 Getting started]
|
|
|
|
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:
|
|
|
|
# '''#include''' [^<boost/python.hpp>]\n\n
|
|
|
|
# Call Py_Initialize() to start the interpreter and create the [^__main__] module.\n\n
|
|
|
|
# Call other Python C API routines to use the interpreter.\n\n
|
|
|
|
# Call Py_Finalize() to stop the interpreter and release its resources.
|
|
|
|
(Of course, there can be other C++ code between all of these steps.)
|
|
|
|
[:['[*Now that we can embed the interpreter in our programs, lets see how to put it to use...]]]
|
|
|
|
[section Using the interpreter]
|
|
|
|
As you probably already know, objects in Python are reference-counted.
|
|
Naturally, the [^PyObject]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
|
|
[@http://www.python.org/doc/current/api/refcounts.html by hand]. This is
|
|
messy and especially hard to get right in the presence of C++ exceptions.
|
|
Fortunately Boost.Python provides the [@../../../v2/handle.html handle] and
|
|
[@../../../v2/object.html object] class templates to automate the process.
|
|
|
|
[h2 Reference-counting handles and objects]
|
|
|
|
There are two ways in which a function in the Python/C API can return a
|
|
[^PyObject*]: as a ['borrowed reference] or as a ['new reference]. 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.
|
|
|
|
For a function returning a ['borrowed reference] we'll have to tell the
|
|
[^handle] that the [^PyObject*] is borrowed with the aptly named
|
|
[@../../../v2/handle.html#borrowed-spec borrowed] function. Two functions
|
|
returning borrowed references are PyImport_AddModule and PyModule_GetDict.
|
|
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 [^__main__] module:
|
|
|
|
object main_module((
|
|
handle<>(borrowed(PyImport_AddModule("__main__")))));
|
|
|
|
object main_namespace = main_module.attr("__dict__");
|
|
|
|
For a function returning a ['new reference] we can just create a [^handle]
|
|
out of the raw [^PyObject*] without wrapping it in a call to borrowed. One
|
|
such function that returns a new reference is PyRun_String which we'll
|
|
discuss in the next section.
|
|
|
|
[blurb __note__ [*Handle is a class ['template], so why haven't we been using any template parameters?]\n
|
|
\n
|
|
[^handle] has a single template parameter specifying the type of the managed object. This type is [^PyObject] 99% of the time, so the parameter was defaulted to [^PyObject] for convenience. Therefore we can use the shorthand [^handle<>] instead of the longer, but equivalent, [^handle<PyObject>].
|
|
]
|
|
|
|
[h2 Running Python code]
|
|
|
|
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 [@http://www.python.org/doc/current/api/veryhigh.html here]. They
|
|
all work similarly so we will look at only one of them, namely:
|
|
|
|
PyObject* PyRun_String(char *str, int start, PyObject *globals, PyObject *locals)
|
|
|
|
PyRun_String takes the code to execute as a null-terminated (C-style)
|
|
string in its [^str] parameter. The function returns a new reference to a
|
|
Python object. Which object is returned depends on the [^start] paramater.
|
|
|
|
The [^start] parameter is the start symbol from the Python grammar to use
|
|
for interpreting the code. The possible values are:
|
|
|
|
[table Start symbols
|
|
|
|
[[Py_eval_input] [for interpreting isolated expressions]]
|
|
[[Py_file_input] [for interpreting sequences of statements]]
|
|
[[Py_single_input] [for interpreting a single statement]]
|
|
]
|
|
|
|
When using Py_eval_input, the input string must contain a single expression
|
|
and its result is returned. When using Py_file_input, the string can
|
|
contain an abitrary number of statements and None is returned.
|
|
Py_single_input works in the same way as Py_file_input but only accepts a
|
|
single statement.
|
|
|
|
Lastly, the [^globals] and [^locals] 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
|
|
[^__main__] module for both parameters.
|
|
|
|
We have already seen how to get the [^__main__] module's namespace so let's
|
|
run some Python code in it:
|
|
|
|
object main_module((
|
|
handle<>(borrowed(PyImport_AddModule("__main__")))));
|
|
|
|
object main_namespace = main_module.attr("__dict__");
|
|
|
|
handle<> ignored((PyRun_String(
|
|
|
|
"hello = file('hello.txt', 'w')\n"
|
|
"hello.write('Hello world!')\n"
|
|
"hello.close()"
|
|
|
|
, Py_file_input
|
|
, main_namespace.ptr()
|
|
, main_namespace.ptr())
|
|
));
|
|
|
|
Because the Python/C API doesn't know anything about [^object]s, we used
|
|
the object's [^ptr] member function to retrieve the [^PyObject*].
|
|
|
|
This should create a file called 'hello.txt' in the current directory
|
|
containing a phrase that is well-known in programming circles.
|
|
|
|
__note__ [*Note] that we wrap the return value of PyRun_String in a
|
|
(nameless) [^handle] 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 [^PyObject*]s in [^handle]s.
|
|
|
|
[h2 Beyond handles]
|
|
|
|
It's nice that [^handle] 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 [@object.html previous section]: the aptly
|
|
named [^object] class and it's derivatives. We've already seen that they
|
|
can be constructed from a [^handle]. The following examples should further
|
|
illustrate this fact:
|
|
|
|
object main_module((
|
|
handle<>(borrowed(PyImport_AddModule("__main__")))));
|
|
|
|
object main_namespace = main_module.attr("__dict__");
|
|
|
|
handle<> ignored((PyRun_String(
|
|
|
|
"result = 5 ** 2"
|
|
|
|
, Py_file_input
|
|
, main_namespace.ptr()
|
|
, main_namespace.ptr())
|
|
));
|
|
|
|
int five_squared = extract<int>(main_namespace["result"]);
|
|
|
|
Here we create a dictionary object for the [^__main__] 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
|
|
PyRun_String return the result directly with Py_eval_input:
|
|
|
|
object result((handle<>(
|
|
PyRun_String("5 ** 2"
|
|
, Py_eval_input
|
|
, main_namespace.ptr()
|
|
, main_namespace.ptr()))
|
|
));
|
|
|
|
int five_squared = extract<int>(result);
|
|
|
|
__note__ [*Note] that [^object]'s member function to return the wrapped
|
|
[^PyObject*] is called [^ptr] instead of [^get]. This makes sense if you
|
|
take into account the different functions that [^object] and [^handle]
|
|
perform.
|
|
|
|
[h2 Exception handling]
|
|
|
|
If an exception occurs in the execution of some Python code, the PyRun_String
|
|
function returns a null pointer. Constructing a [^handle] out of this null
|
|
pointer throws [@../../../v2/errors.html#error_already_set-spec error_already_set],
|
|
so basically, the Python exception is automatically translated into a
|
|
C++ exception when using [^handle]:
|
|
|
|
try
|
|
{
|
|
object result((handle<>(PyRun_String(
|
|
"5/0"
|
|
, Py_eval_input
|
|
, main_namespace.ptr()
|
|
, main_namespace.ptr()))
|
|
));
|
|
|
|
// execution will never get here:
|
|
int five_divided_by_zero = extract<int>(result);
|
|
}
|
|
catch(error_already_set)
|
|
{
|
|
// handle the exception in some way
|
|
}
|
|
|
|
The [^error_already_set] exception class doesn't carry any information in itself.
|
|
To find out more about the Python exception that occurred, you need to use the
|
|
[@http://www.python.org/doc/api/exceptionHandling.html exception handling functions]
|
|
of the Python/C API in your catch-statement. This can be as simple as calling
|
|
[@http://www.python.org/doc/api/exceptionHandling.html#l2h-70 PyErr_Print()] to
|
|
print the exception's traceback to the console, or comparing the type of the
|
|
exception with those of the [@http://www.python.org/doc/api/standardExceptions.html
|
|
standard exceptions]:
|
|
|
|
catch(error_already_set)
|
|
{
|
|
if (PyErr_ExceptionMatches(PyExc_ZeroDivisionError))
|
|
{
|
|
// handle ZeroDivisionError specially
|
|
}
|
|
else
|
|
{
|
|
// print all other errors to stderr
|
|
PyErr_Print();
|
|
}
|
|
}
|
|
|
|
(To retrieve even more information from the exception you can use some of the other
|
|
exception handling functions listed [@http://www.python.org/doc/api/exceptionHandling.html here].)
|
|
|
|
If you'd rather not have [^handle] throw a C++ exception when it is constructed, you
|
|
can use the [@../../../v2/handle.html#allow_null-spec allow_null] function in the same
|
|
way you'd use borrowed:
|
|
|
|
handle<> result((allow_null(PyRun_String(
|
|
"5/0"
|
|
, Py_eval_input
|
|
, main_namespace.ptr()
|
|
, main_namespace.ptr()))));
|
|
|
|
if (!result)
|
|
// Python exception occurred
|
|
else
|
|
// everything went okay, it's safe to use the result
|
|
|
|
[endsect]
|
|
[endsect] [/ Embedding]
|
|
|
|
[section Iterators]
|
|
|
|
In C++, and STL in particular, we see iterators everywhere. Python also has
|
|
iterators, but these are two very different beasts.
|
|
|
|
[*C++ iterators:]
|
|
|
|
* C++ has 5 type categories (random-access, bidirectional, forward, input, output)
|
|
* There are 2 Operation categories: reposition, access
|
|
* A pair of iterators is needed to represent a (first/last) range.
|
|
|
|
[*Python Iterators:]
|
|
|
|
* 1 category (forward)
|
|
* 1 operation category (next())
|
|
* Raises StopIteration exception at end
|
|
|
|
The typical Python iteration protocol: [^[*for y in x...]] is as follows:
|
|
|
|
iter = x.__iter__() # get iterator
|
|
try:
|
|
while 1:
|
|
y = iter.next() # get each item
|
|
... # process y
|
|
except StopIteration: pass # iterator exhausted
|
|
|
|
Boost.Python provides some mechanisms to make C++ iterators play along
|
|
nicely as Python iterators. What we need to do is to produce
|
|
appropriate __iter__ function from C++ iterators that is compatible
|
|
with the Python iteration protocol. For example:
|
|
|
|
object get_iterator = iterator<vector<int> >();
|
|
object iter = get_iterator(v);
|
|
object first = iter.next();
|
|
|
|
Or for use in class_<>:
|
|
|
|
.def("__iter__", iterator<vector<int> >())
|
|
|
|
[*range]
|
|
|
|
We can create a Python savvy iterator using the range function:
|
|
|
|
* range(start, finish)
|
|
* range<Policies,Target>(start, finish)
|
|
|
|
Here, start/finish may be one of:
|
|
|
|
* member data pointers
|
|
* member function pointers
|
|
* adaptable function object (use Target parameter)
|
|
|
|
[*iterator]
|
|
|
|
* iterator<T, Policies>()
|
|
|
|
Given a container [^T], iterator is a shortcut that simply calls [^range]
|
|
with &T::begin, &T::end.
|
|
|
|
Let's put this into action... Here's an example from some hypothetical
|
|
bogon Particle accelerator code:
|
|
|
|
f = Field()
|
|
for x in f.pions:
|
|
smash(x)
|
|
for y in f.bogons:
|
|
count(y)
|
|
|
|
Now, our C++ Wrapper:
|
|
|
|
class_<F>("Field")
|
|
.property("pions", range(&F::p_begin, &F::p_end))
|
|
.property("bogons", range(&F::b_begin, &F::b_end));
|
|
|
|
[endsect]
|
|
[section:exception Exception Translation]
|
|
|
|
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:
|
|
|
|
raise RuntimeError, 'unidentifiable C++ Exception'
|
|
|
|
Users may provide custom translation. Here's an example:
|
|
|
|
struct PodBayDoorException;
|
|
void translator(PodBayDoorException const& x) {
|
|
PyErr_SetString(PyExc_UserWarning, "I'm sorry Dave...");
|
|
}
|
|
BOOST_PYTHON_MODULE(kubrick) {
|
|
register_exception_translator<
|
|
PodBayDoorException>(translator);
|
|
...
|
|
|
|
[endsect]
|
|
[section:techniques General Techniques]
|
|
|
|
Here are presented some useful techniques that you can use while wrapping code with Boost.Python.
|
|
|
|
[section Creating Packages]
|
|
|
|
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
|
|
[@http://www.python.org/doc/current/tut/node8.html Python Tutorial].
|
|
|
|
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.
|
|
|
|
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)
|
|
[^sounds]. Our library already has a neat C++ namespace hierarchy, like so:
|
|
|
|
sounds::core
|
|
sounds::io
|
|
sounds::filters
|
|
|
|
We would like to present this same hierarchy to the Python user, allowing him
|
|
to write code like this:
|
|
|
|
import sounds.filters
|
|
sounds.filters.echo(...) # echo is a C++ function
|
|
|
|
The first step is to write the wrapping code. We have to export each module
|
|
separately with Boost.Python, like this:
|
|
|
|
/* file core.cpp */
|
|
BOOST_PYTHON_MODULE(core)
|
|
{
|
|
/* export everything in the sounds::core namespace */
|
|
...
|
|
}
|
|
|
|
/* file io.cpp */
|
|
BOOST_PYTHON_MODULE(io)
|
|
{
|
|
/* export everything in the sounds::io namespace */
|
|
...
|
|
}
|
|
|
|
/* file filters.cpp */
|
|
BOOST_PYTHON_MODULE(filters)
|
|
{
|
|
/* export everything in the sounds::filters namespace */
|
|
...
|
|
}
|
|
|
|
Compiling these files will generate the following Python extensions:
|
|
[^core.pyd], [^io.pyd] and [^filters.pyd].
|
|
|
|
[blurb __note__ The extension [^.pyd] is used for python extension modules, which
|
|
are just shared libraries. Using the default for your system, like [^.so] for
|
|
Unix and [^.dll] for Windows, works just as well.]
|
|
|
|
Now, we create this directory structure for our Python package:
|
|
|
|
[pre
|
|
sounds/
|
|
__init__.py
|
|
core.pyd
|
|
filters.pyd
|
|
io.pyd
|
|
]
|
|
|
|
The file [^__init__.py] is what tells Python that the directory [^sounds/] is
|
|
actually a Python package. It can be a empty file, but can also perform some
|
|
magic, that will be shown later.
|
|
|
|
Now our package is ready. All the user has to do is put [^sounds] into his
|
|
[@http://www.python.org/doc/current/tut/node8.html#SECTION008110000000000000000 PYTHONPATH]
|
|
and fire up the interpreter:
|
|
|
|
>>> import sounds.io
|
|
>>> import sounds.filters
|
|
>>> sound = sounds.io.open('file.mp3')
|
|
>>> new_sound = sounds.filters.echo(sound, 1.0)
|
|
|
|
Nice heh?
|
|
|
|
This is the simplest way to create hierarchies of packages, but it is not very
|
|
flexible. What if we want to add a ['pure] 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.
|
|
|
|
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:
|
|
|
|
/* file core.cpp */
|
|
BOOST_PYTHON_MODULE(_core)
|
|
{
|
|
...
|
|
/* export everything in the sounds::core namespace */
|
|
}
|
|
|
|
Note that we added an underscore to the module name. The filename will have to
|
|
be changed to [^_core.pyd] as well, and we do the same to the other extension modules.
|
|
Now, we change our package hierarchy like so:
|
|
|
|
[pre
|
|
sounds/
|
|
__init__.py
|
|
core/
|
|
__init__.py
|
|
_core.pyd
|
|
filters/
|
|
__init__.py
|
|
_filters.pyd
|
|
io/
|
|
__init__.py
|
|
_io.pyd
|
|
]
|
|
|
|
Note that we created a directory for each extension module, and added a
|
|
__init__.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:
|
|
|
|
>>> import sounds.core._core
|
|
>>> sounds.core._core.foo(...)
|
|
|
|
which is not what we want. But here enters the [^__init__.py] magic: everything
|
|
that is brought to the [^__init__.py] namespace can be accessed directly by the
|
|
user. So, all we have to do is bring the entire namespace from [^_core.pyd]
|
|
to [^core/__init__.py]. So add this line of code to [^sounds/core/__init__.py]:
|
|
|
|
from _core import *
|
|
|
|
We do the same for the other packages. Now the user accesses the functions and
|
|
classes in the extension modules like before:
|
|
|
|
>>> import sounds.filters
|
|
>>> sounds.filters.echo(...)
|
|
|
|
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 ['pure] Python function,
|
|
[^echo_noise], to the [^filters] package. This function applies both the
|
|
[^echo] and [^noise] filters in sequence in the given [^sound] object. We
|
|
create a file named [^sounds/filters/echo_noise.py] and code our function:
|
|
|
|
import _filters
|
|
def echo_noise(sound):
|
|
s = _filters.echo(sound)
|
|
s = _filters.noise(sound)
|
|
return s
|
|
|
|
Next, we add this line to [^sounds/filters/__init__.py]:
|
|
|
|
from echo_noise import echo_noise
|
|
|
|
And that's it. The user now accesses this function like any other function
|
|
from the [^filters] package:
|
|
|
|
>>> import sounds.filters
|
|
>>> sounds.filters.echo_noise(...)
|
|
|
|
[endsect]
|
|
[section Extending Wrapped Objects in Python]
|
|
|
|
Thanks to Python's flexibility, you can easily add new methods to a class,
|
|
even after it was already created:
|
|
|
|
>>> class C(object): pass
|
|
>>>
|
|
>>> # a regular function
|
|
>>> def C_str(self): return 'A C instance!'
|
|
>>>
|
|
>>> # now we turn it in a member function
|
|
>>> C.__str__ = C_str
|
|
>>>
|
|
>>> c = C()
|
|
>>> print c
|
|
A C instance!
|
|
>>> C_str(c)
|
|
A C instance!
|
|
|
|
Yes, Python rox. :-)
|
|
|
|
We can do the same with classes that were wrapped with Boost.Python. Suppose
|
|
we have a class [^point] in C++:
|
|
|
|
class point {...};
|
|
|
|
BOOST_PYTHON_MODULE(_geom)
|
|
{
|
|
class_<point>("point")...;
|
|
}
|
|
|
|
If we are using the technique from the previous session,
|
|
[link python.creating_packages Creating Packages], we can code directly
|
|
into [^geom/__init__.py]:
|
|
|
|
from _geom import *
|
|
|
|
# a regular function
|
|
def point_str(self):
|
|
return str((self.x, self.y))
|
|
|
|
# now we turn it into a member function
|
|
point.__str__ = point_str
|
|
|
|
[*All] point instances created from C++ will also have this member function!
|
|
This technique has several advantages:
|
|
|
|
* Cut down compile times to zero for these additional functions
|
|
* Reduce the memory footprint to virtually zero
|
|
* Minimize the need to recompile
|
|
* Rapid prototyping (you can move the code to C++ if required without changing the interface)
|
|
|
|
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.
|
|
|
|
# The one Boost.Python uses for all wrapped classes.
|
|
# You can use here any class exported by Boost instead of "point"
|
|
BoostPythonMetaclass = point.__class__
|
|
|
|
class injector(object):
|
|
class __metaclass__(BoostPythonMetaclass):
|
|
def __init__(self, name, bases, dict):
|
|
for b in bases:
|
|
if type(b) not in (self, type):
|
|
for k,v in dict.items():
|
|
setattr(b,k,v)
|
|
return type.__init__(self, name, bases, dict)
|
|
|
|
# inject some methods in the point foo
|
|
class more_point(injector, point):
|
|
def __repr__(self):
|
|
return 'Point(x=%s, y=%s)' % (self.x, self.y)
|
|
def foo(self):
|
|
print 'foo!'
|
|
|
|
Now let's see how it got:
|
|
|
|
>>> print point()
|
|
Point(x=10, y=10)
|
|
>>> point().foo()
|
|
foo!
|
|
|
|
Another useful idea is to replace constructors with factory functions:
|
|
|
|
_point = point
|
|
|
|
def point(x=0, y=0):
|
|
return _point(x, y)
|
|
|
|
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.
|
|
|
|
[endsect]
|
|
[section Reducing Compiling Time]
|
|
|
|
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:
|
|
|
|
/* file point.cpp */
|
|
#include <point.h>
|
|
#include <boost/python.hpp>
|
|
|
|
void export_point()
|
|
{
|
|
class_<point>("point")...;
|
|
}
|
|
|
|
/* file triangle.cpp */
|
|
#include <triangle.h>
|
|
#include <boost/python.hpp>
|
|
|
|
void export_triangle()
|
|
{
|
|
class_<triangle>("triangle")...;
|
|
}
|
|
|
|
Now you create a file [^main.cpp], which contains the [^BOOST_PYTHON_MODULE]
|
|
macro, and call the various export functions inside it.
|
|
|
|
void export_point();
|
|
void export_triangle();
|
|
|
|
BOOST_PYTHON_MODULE(_geom)
|
|
{
|
|
export_point();
|
|
export_triangle();
|
|
}
|
|
|
|
Compiling and linking together all this files produces the same result as the
|
|
usual approach:
|
|
|
|
#include <boost/python.hpp>
|
|
#include <point.h>
|
|
#include <triangle.h>
|
|
|
|
BOOST_PYTHON_MODULE(_geom)
|
|
{
|
|
class_<point>("point")...;
|
|
class_<triangle>("triangle")...;
|
|
}
|
|
|
|
but the memory is kept under control.
|
|
|
|
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.
|
|
|
|
[blurb __note__ If you're exporting your classes with [@../../../../pyste/index.html Pyste],
|
|
take a look at the [^--multiple] option, that generates the wrappers in
|
|
various files as demonstrated here.]
|
|
|
|
[blurb __note__ This method is useful too if you are getting the error message
|
|
['"fatal error C1204:Compiler limit:internal structure overflow"] when compiling
|
|
a large source file, as explained in the [@../../../v2/faq.html#c1204 FAQ].]
|
|
|
|
[endsect]
|
|
[endsect] [/ General Techniques]
|
|
|
|
|