2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-20 16:52:15 +00:00
Files
python/doc/tutorial/doc/quickstart.txt
Joel de Guzman ff9f262fac tutorial update
[SVN r17512]
2003-02-18 18:44:16 +00:00

1403 lines
43 KiB
Plaintext
Raw Blame History

[doc Boost Python Tutorial]
[def __note__ [$theme/note.gif]]
[def __alert__ [$theme/alert.gif]]
[def __detail__ [$theme/lens.gif]]
[def __tip__ [$theme/bulb.gif]]
[def :-) [$theme/smiley.gif]]
[page 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...]]]
[page 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 __detail__ [*Building without bjam][br][br]
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].
]
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. For example, a pre-built Microsoft Windows bjam executable can
be downloaded [@http://boost.sourceforge.net/jam-executables/bin.ntx86/bjam.zip here].
The complete list of bjam pre-built
executables can be found [@../../../../../tools/build/index.html#Jam here].
[h2 Lets Jam!]
[$theme/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. __note__ 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!]]
[page 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'
[page:1 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.
[page:1 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
]
[page:1 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)
[page:1 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 [@call_policies.html call policies] later.
// Tell Python to take ownership of factory's result
def("factory", factory,
return_value_policy<manage_new_object>());
[page:1 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 int f() = 0;
};
Since [^f] is a pure virtual function, [^Base] is now an abstract
class. Given an instance of our class, the free function [^call_f]
calls some implementation of this virtual function in a concrete
derived class:
int call_f(Base& b) { return b.f(); }
To allow this function to be implemented in a Python derived class, we
need to create a class wrapper:
struct BaseWrap : Base
{
BaseWrap(PyObject* self_)
: self(self_) {}
int f() { return call_method<int>(self, "f"); }
PyObject* self;
};
[blurb __detail__ [*member function and methods][br][br] Python, like
many object oriented languages uses the term [*methods]. Methods
correspond roughly to C++'s [*member functions]]
Our class wrapper [^BaseWrap] is derived from [^Base]. Its overridden
virtual member function [^f] in effect calls the corresponding method
of the Python object [^self], which is a pointer back to the Python
[^Base] object holding our [^BaseWrap] instance.
[blurb __note__ [*Why do we need BaseWrap?][br][br]
['You may ask], "Why do we need the [^BaseWrap] derived class? This could
have been designed so that everything gets done right inside of
Base."[br][br]
One of the goals of Boost.Python is to be minimally intrusive on an
existing C++ design. In principle, it should be possible to expose the
interface for a 3rd party library without changing it. To unintrusively
hook into the virtual functions so that a Python override may be called, we
must use a derived class.[br][br]
Note however that you don't need to do this to get methods overridden
in Python to behave virtually when called ['from] [*Python]. The only
time you need to do the [^BaseWrap] dance is when you have a virtual
function that's going to be overridden in Python and called
polymorphically ['from] [*C++].]
Wrapping [^Base] and the free function [^call_f]:
class_<Base, BaseWrap, boost::noncopyable>("Base", no_init)
;
def("call_f", call_f);
Notice that we parameterized the [^class_] template with [^BaseWrap] as the
second parameter. What is [^noncopyable]? Without it, the library will try
to create code for converting Base return values of wrapped functions to
Python. To do that, it needs Base's copy constructor... which isn't
available, since Base is an abstract class.
In Python, let us try to instantiate our [^Base] class:
>>> base = Base()
RuntimeError: This class cannot be instantiated from Python
Why is it an error? [^Base] is an abstract class. As such it is advisable
to define the Python wrapper with [^no_init] as we have done above. Doing
so will disallow abstract base classes such as [^Base] to be instantiated.
[h2 Deriving a Python class]
Now, at last, we can even derive from our base class Base in Python. Before
we can do that, we have to set up our class_ wrapper as:
class_<Base, BaseWrap, boost::noncopyable>("Base")
;
Otherwise, we have to suppress the Base class' [^no_init] by adding an
[^__init__()] method to all our derived classes. [^no_init] actually adds
an [^__init__] method that raises a Python RuntimeError exception.
>>> class Derived(Base):
... def f(self):
... return 42
...
Cool eh? A Python class deriving from a C++ class!
Let's now make an instance of our Python class [^Derived]:
>>> derived = Derived()
Calling [^derived.f()]:
>>> derived.f()
42
Will yield the expected result. Finally, calling calling the free function
[^call_f] with [^derived] as argument:
>>> call_f(derived)
42
Will also yield the expected result.
Here's what's happening:
# [^call_f(derived)] is called in Python
# This corresponds to [^def("call_f", call_f);]. Boost.Python dispatches this call.
# [^int call_f(Base& b) { return b.f(); }] accepts the call.
# The overridden virtual function [^f] of [^BaseWrap] is called.
# [^call_method<int>(self, "f");] dispatches the call back to Python.
# [^def f(self): return 42] is finally called.
Rewind back to our [^Base] class, if its member function [^f] was not
declared as pure virtual:
struct Base
{
virtual int f() { return 0; }
};
And instead is implemented to return [^0], as shown above.
struct BaseWrap : Base
{
BaseWrap(PyObject* self_)
: self(self_) {}
int f() { return call_method<int>(self, "f"); }
static int default_f(Base* b) { return b->Base::f(); } // <<=== added
PyObject* self;
};
then, our Boost.Python wrapper:
class_<Base, BaseWrap>("Base")
.def("f", &BaseWrap::default_f)
;
Note that we are allowing [^Base] objects to be instantiated this time,
unlike before where we specifically defined the [^class_<Base>] with
[^no_init].
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
Calling [^call_f], passing in a [^base] object:
>>> call_f(base)
0
Calling [^call_f], passing in a [^derived] object:
>>> call_f(derived)
42
[page:1 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 __detail__ 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)).]
[page 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!
]
[page:1 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][br] Ties lifetimes of the arguments
* [*with_custodian_and_ward_postcall][br] Ties lifetimes of the arguments and results
* [*return_internal_reference][br] Ties lifetime of one argument to that of result
* [*return_value_policy<T> with T one of:][br]
* [*reference_existing_object][br]na<6E>ve (dangerous) approach
* [*copy_const_reference][br]Boost.Python v1 approach
* [*copy_non_const_reference][br]
* [*manage_new_object][br] Adopt a pointer and hold the instance
[blurb :-) [*Remember the Zen, Luke:][br][br]
"Explicit is better than implicit"[br]
"In the face of ambiguity, refuse the temptation to guess"[br]]
[page:1 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 in earlier versions of
Boost.Python, we had to resort to 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_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).
[page:1 Overloading]
It was mentioned in passing in the previous section that
[^BOOST_PYTHON_FUNCTION_OVERLOADS] and [^BOOST_PYTHON_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.
The following illustrates an alternate scheme for manually wrapping an
overloaded member function instead of
[^BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS]. Obviously, the same technique
can be applied to wrapping overloaded non- member functions.
We have here our C++ classes:
struct X
{
bool f(int a, double b = 0, char c = 'x')
{
return true;
}
int f(int a, int b, int c)
{
return a + b + c;
};
};
Notice that class X has two overloaded functions with different signatures.
The types of the arguments, and the return are totally different, unlike
above where we have a common sequence of initial arguments.
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;
The first three member function pointers take care of the first X::f
overload. The one with default arguments. The last member function pointer
takes care of the second X::f overload.
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)
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]. Since the first overload
has default arguments, we can use [^BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS]
to automatically wrap the first three of the [^def]s above 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)
[page 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.
[$theme/python.png]
[page:1 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.
[page:1 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);
[page:1 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__ !
[page:1 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 __detail__ [*what is a scope?][br][br] 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)
;
[page 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));
[page 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);
...