From 78949bfc229627def78c5f328884f5a42ddbf404 Mon Sep 17 00:00:00 2001 From: Dirk Gerrits Date: Thu, 5 Dec 2002 22:22:00 +0000 Subject: [PATCH] Initial checkin: Copied original tutorial from Boost 1.29.0 [SVN r748] --- doc/tutorial/doc/quickstart.txt | 1247 +++++++++++++++++++++ doc/tutorial/doc/theme/alert.gif | Bin 0 -> 500 bytes doc/tutorial/doc/theme/arrow.gif | Bin 0 -> 70 bytes doc/tutorial/doc/theme/bkd.gif | Bin 0 -> 1037 bytes doc/tutorial/doc/theme/bkd2.gif | Bin 0 -> 843 bytes doc/tutorial/doc/theme/bulb.gif | Bin 0 -> 918 bytes doc/tutorial/doc/theme/bullet.gif | Bin 0 -> 152 bytes doc/tutorial/doc/theme/c++boost.gif | Bin 0 -> 1875 bytes doc/tutorial/doc/theme/jam.png | 1 + doc/tutorial/doc/theme/l_arr.gif | Bin 0 -> 146 bytes doc/tutorial/doc/theme/l_arr_disabled.gif | Bin 0 -> 91 bytes doc/tutorial/doc/theme/lens.gif | Bin 0 -> 875 bytes doc/tutorial/doc/theme/note.gif | Bin 0 -> 151 bytes doc/tutorial/doc/theme/python.png | 1 + doc/tutorial/doc/theme/r_arr.gif | Bin 0 -> 147 bytes doc/tutorial/doc/theme/r_arr_disabled.gif | Bin 0 -> 91 bytes doc/tutorial/doc/theme/smiley.gif | Bin 0 -> 186 bytes doc/tutorial/doc/theme/style.css | 170 +++ doc/tutorial/doc/theme/u_arr.gif | Bin 0 -> 170 bytes doc/tutorial/index.html | 126 +++ 20 files changed, 1545 insertions(+) create mode 100644 doc/tutorial/doc/quickstart.txt create mode 100644 doc/tutorial/doc/theme/alert.gif create mode 100644 doc/tutorial/doc/theme/arrow.gif create mode 100644 doc/tutorial/doc/theme/bkd.gif create mode 100644 doc/tutorial/doc/theme/bkd2.gif create mode 100644 doc/tutorial/doc/theme/bulb.gif create mode 100644 doc/tutorial/doc/theme/bullet.gif create mode 100644 doc/tutorial/doc/theme/c++boost.gif create mode 100644 doc/tutorial/doc/theme/jam.png create mode 100644 doc/tutorial/doc/theme/l_arr.gif create mode 100644 doc/tutorial/doc/theme/l_arr_disabled.gif create mode 100644 doc/tutorial/doc/theme/lens.gif create mode 100644 doc/tutorial/doc/theme/note.gif create mode 100644 doc/tutorial/doc/theme/python.png create mode 100644 doc/tutorial/doc/theme/r_arr.gif create mode 100644 doc/tutorial/doc/theme/r_arr_disabled.gif create mode 100644 doc/tutorial/doc/theme/smiley.gif create mode 100644 doc/tutorial/doc/theme/style.css create mode 100644 doc/tutorial/doc/theme/u_arr.gif create mode 100644 doc/tutorial/index.html diff --git a/doc/tutorial/doc/quickstart.txt b/doc/tutorial/doc/quickstart.txt new file mode 100644 index 00000000..2c1116cb --- /dev/null +++ b/doc/tutorial/doc/quickstart.txt @@ -0,0 +1,1247 @@ +[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 + 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 + ../../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 + ../../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 + using namespace boost::python; + + BOOST_PYTHON_MODULE(hello) + { + class_("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_] about the constructor we want to +expose instead. + + #include + using namespace boost::python; + + BOOST_PYTHON_MODULE(hello) + { + class_("World", init()) + .def("greet", &World::greet) + .def("set", &World::set) + ; + } + +[^init()] 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", init()) + .def(init()) + .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", 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", init()) + .def_readonly("name", &Var::name) + .def_readwrite("value", &Var::value); + +Then, in Python: + + >>> x = 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 "", 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") + .add_property("rovalue", &Var::get) + .add_property("value", &Var::get, &Var::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", &Var::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") + /*...*/ + ; + +Now we can inform Boost.Python of the inheritance relationship between +[^Derived] and its base class [^Base]. Thus: + + class_ >("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] 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()); + +[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(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", 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() + AttributeError: ... + +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: + + >>> 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(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(self, "f"); } + static int default_f(Base* b) { return b->Base::f(); } // <<=== added + PyObject* self; + }; + +then, our Boost.Python wrapper: + + class_("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_] 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") + .def(self + int()) // __add__ + .def(int() + self) // __radd__ + .def(self - self) // __sub__ + .def(self - int()) // __rsub__ + .def(self += int()) // __iadd__ + .def(self -= other()) + .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()] 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_() + .def(float_(self)) // __float__ + .def(pow(self, other)) // __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 > > + +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 with T one of:][br] +* [*reference_existing_object][br]naď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 + +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()); + +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 >()) + +Notice the use of [^init<...>] and [^optional<...>] to signify the default +(optional arguments). + +[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_ as objects] + +Due to the dynamic nature of Boost.Python objects, any [^class_] 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", init()) + .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] 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(o.attr("length")); + Vec2& v = extract(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 x(o); + if (x.check()) { + Vec2& v = x(); ... + +__tip__ The astute reader might have noticed that the [^extract] +facility in fact solves the mutable copying problem: + + dict d = extract(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") + .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") + .def( ... ) + .def( ... ) + ); + + // Expose X::nested as X.nested + enum_("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 >(); + object iter = get_iterator(v); + object first = iter.next(); + +Or for use in class_<>: + + .def("__iter__", iterator >()) + +[*range] + +We can create a Python savvy iterator using the range function: + +* range(start, finish) +* range(start, finish) + +Here, start/finish may be one of: + +* member data pointers +* member function pointers +* adaptable function object (use Target parameter) + +[*iterator] + +* iterator() + +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_("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& x) { + PyErr_SetString(PyExc_UserWarning, "I'm sorry Dave..."); + } + BOOST_PYTHON_MODULE(kubrick) { + register_exception_translator< + PodBayDoorException>(translator); + ... + diff --git a/doc/tutorial/doc/theme/alert.gif b/doc/tutorial/doc/theme/alert.gif new file mode 100644 index 0000000000000000000000000000000000000000..3c46e71264394d00e903f0df8ecc9b374d6e3604 GIT binary patch literal 500 zcmZ?wbhEHb6krfwc*el+^5x4HFJ3%)^yuNkhj;GW`TvCB_N{CGA2IyD%W(C|x&OBr z{@-G_a_Q{m__~8F~vDYEl>qqZpDSjq@WJa>5z1Lm6Vd z86rHnLYx__^cb|27{rAb*jO1D7#RNl{|}<5jTL{gu!=AwGw6T}2E_>j`@DwarsiZ7 zvzCqy8~4s$Mnes@-VRGi5tqr$%yz7-+I%yUbrd6_%=Konm?$SD`KoeGb{17nO!DDy e>t(PKkWWZ*<cQ3wG1?7FxB literal 0 HcmV?d00001 diff --git a/doc/tutorial/doc/theme/arrow.gif b/doc/tutorial/doc/theme/arrow.gif new file mode 100644 index 0000000000000000000000000000000000000000..e33db0fb4dd85e0fe527343d95a3f28a0ebb74f9 GIT binary patch literal 70 zcmZ?wbhEHb6lLIGn8?8J9}E~6W->4^DE?&OC-XFJFH7^5yH-udiOcdjJ0Y=g*&CzkdDw`}cS6-hKG+ zVdct|pFVy1_U+rhfB)XRdGq$|+aEuEeEj(F|NsB1R;~K`_wVY}tN;A@vu4ej6)RT! z{{8#cuU~7|u3fio-TL+GfByWrapT4f8#Zj(v}yC^&D*wZ+q!k@mMvR$@7{gjz=7@C zx9{A!bMM~0yLRo`vuDr#{rh+9*s*WlzC(u&9X)#V)TvV^Po6w- zyLay%Jb3Wz*|X1|Kfizf{+Tmpu3fwK^y$-^H*a3Qe*M|AXXnnHJAeNCg$oysA3uKe z>eWk^E+4<0lBLU*@T^?5dd(`K^&2*BTFEI`2zd3>o;z)v)s9R@BSUeM~|O8Wn_5q^405CHt*hl`1r~B%hzw;zdQW;{pasr zF9v2V8Hs|fgUuYmej5*MSa`TSY=y@hkBv&AY7)j-cRDt)dE zU9$D{^$pSTGkte&%f01a^!nae>+L=F4>WVj`|WA_`1r(RZF9M$J3l|aF#GrnzrDM@ zzP^$C;>NkXyT8BJIMglgzi&_FC%sFnlY$n!TsEid J)yw4z+5l}YIl%w` literal 0 HcmV?d00001 diff --git a/doc/tutorial/doc/theme/bkd2.gif b/doc/tutorial/doc/theme/bkd2.gif new file mode 100644 index 0000000000000000000000000000000000000000..e67a40eaf2b41797365f83dc84293ac7577fb6b2 GIT binary patch literal 843 zcmZ?wbhEHbWZ+<8_|5Qs zOXn|KICuX1*>mU4oIQK`%$bv?P8~mS;_#6p2M--OaPZ*2|Ns8}`}_OP@1MVZe*f|P z>$k6;zkL4q>Ertk?_a-p{qohz=P#Z=efIS6lgAGqJ-mDG?(I9bZ{E6j{l@jH*REc< zdgbz!%NH+QJb&T**>h)4pE-T<)X5VkPaHpS{OGZxhmRaScdEzI5s0g$w7;ojZHx%;{67PM$b%{MfOhM~)mmbm-u}zkmPy{{8Fc z&mZ5vfBX9N%jeIZK7Rc8;lqdb@87+B`{woQS1(__c>es^)2C0LJbCi?@#9Azd+y!6 zd*}A;TQ_gsxPJZG)vH%7U%qtl;>8OWE}TDq?(Eq!r%#_cdGf^Z(|epKfinT?$xVT&!0cPfB*i?n>VjryLRc)rPHTRA3uKl*s)_rj~+dA=+MD~ z2mk;74@!aqhZTRaFfuS)WzYdTfrE*Gf#W{|D~F87h6M+kIfS)hPHb3sxSew)=K?q1 Xo*A7Y+G%$@bUlvuDjn(&P-y}Hi)TZ< literal 0 HcmV?d00001 diff --git a/doc/tutorial/doc/theme/bulb.gif b/doc/tutorial/doc/theme/bulb.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b6c354c7fc3537fc07a686f1fa10f596cbfb34f GIT binary patch literal 918 zcmZ?wbhEHbU2JX=yWO8qb`WHgo38Gifu=q@6jF_W#UGhBGsb&&*6a zGjrydnP<+-{D0;Q!i=IrIPje+GvC495Q% z(*85d1W7Xd|8LCj-`M!SaoT_5nIN^s|No~k{7*CfpO*GNZ6-)-+W-GE8UD{S{y#JA z|IC>nlV<+^e}>`z8RP$8cYq8A8~y)3Nb~>yAnE^L0P?^n9t{C$ zm!W3VRz=HX0SO5P1_s5SEQ|~c3=BFT0%V2*i>ts1&-4h1-QU}6JQPJ#9Az#qP{_IL z>m8xcQ#>ccK&B>uvC-M7@I$1?63NFG4AfGLn>kr6)<~>7uC&oZb>E%VNA7Gc3=Gx) DZ>u*X literal 0 HcmV?d00001 diff --git a/doc/tutorial/doc/theme/c++boost.gif b/doc/tutorial/doc/theme/c++boost.gif new file mode 100644 index 0000000000000000000000000000000000000000..8a5a50cabe2274e6b6ce9fd8e3b29fc6e0b5ae59 GIT binary patch literal 1875 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 XYH+E(J~iH@ao+ literal 0 HcmV?d00001 diff --git a/doc/tutorial/doc/theme/note.gif b/doc/tutorial/doc/theme/note.gif new file mode 100644 index 0000000000000000000000000000000000000000..24f9ae75c1f9df935115ae536a5fc80ad839621c GIT binary patch literal 151 zcmZ?wbhEHb6krfw*vtR|#>U2JX=yW!8D`F$dFITS|7mIeXEOYsnf8C?%>QSM|DT!p z|3Ab3|7ZS#f#OdVMg|6c1|5)2kQodtE+0;Mrh1v)&NktibFtDU2JX=yWO&OCGG%>OfK|If_)4`TfP|DWOif8+oE)BgXT z`Tzf!|NsBLfB#&gTE>G6muSe93zS-%fd>Q)&EUbx3v}%NcRk;>%V0Cvz}Z0{|MoM2G+Y literal 0 HcmV?d00001 diff --git a/doc/tutorial/doc/theme/r_arr_disabled.gif b/doc/tutorial/doc/theme/r_arr_disabled.gif new file mode 100644 index 0000000000000000000000000000000000000000..2100f78bf35a47e09ca76ffcc6e80c5a2b11e359 GIT binary patch literal 91 zcmZ?wbhEHb6k!l%n8?5|bLPzZ_wWDz|DS<@LGdRGs|W)VgAM}&0|Q8&fk|gd|H|rv v@(cz3o}Svfg4O3$GVLgQHgm3)*?|1&XIvGFi0itr1TiFoeP&p1C|3gi5M|Dy8)Dq>pVFYMU2JX=yWO&OCGG%>OfK|If_)4`TfP|DWOif8+oE)BgXT z`Tzf!|NsBLfB#pCE@bTiY5O-A{7vV42g%c7%l^E8u0x + + +Boost Python Tutorial + + + + + + + + + +
+ + Boost Python Tutorial +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table of contents
+ QuickStart +
+ Building Hello World +
+ Exposing Classes +
+ Constructors +
+ Class Data Members +
+ Class Properties +
+ Inheritance +
+ Class Virtual Functions +
+ Class Operators/Special Functions +
+ Functions +
+ Call Policies +
+ Default Arguments +
+ Object Interface +
+ Basic Interface +
+ Derived Object types +
+ Extracting C++ objects +
+ Enums +
+ Iterators +
+ Exception Translation +
+
+
+ +