diff --git a/doc/tutorial/doc/basic_interface.html b/doc/tutorial/doc/basic_interface.html new file mode 100644 index 00000000..cb4a28f4 --- /dev/null +++ b/doc/tutorial/doc/basic_interface.html @@ -0,0 +1,77 @@ + + + +Basic Interface + + + + + + + + + + +
+ + Basic Interface +
+
+ + + + + + +
+

+Class object wraps PyObject*. All the intricacies of dealing with +PyObjects such as managing reference counting are handled by the +object class. C++ object interoperability is seamless. Boost.Python C++ +objects can in fact be explicitly constructed from any C++ object.

+

+To illustrate, this Python code snippet:

+
+    def f(x, f):
+         if (y == 'foo'):
+             x[3:7] = 'bar'
+         else:
+             x.items += f(3, x)
+         return x
+
+    def getfunc():
+       return f;
+
+

+Can be rewritten in C++ using Boost.Python facilities this way:

+
+    object f(object x, object f) {
+         if (f == "foo")
+             x.slice(3,7) = "bar";
+         else
+             x.attr("items") += f(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.

+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/building_an_extension_module.html b/doc/tutorial/doc/building_an_extension_module.html new file mode 100644 index 00000000..ffa176b7 --- /dev/null +++ b/doc/tutorial/doc/building_an_extension_module.html @@ -0,0 +1,186 @@ + + + +Building an Extension Module + + + + + + + + + + +
+ Building + an Extension Module
+
+ + + + + + +
+

Building Boost.Python

+

Every Boost.Python extension module must be linked with the boost_python shared + library. To build boost_python, use Boost.Build + in the usual way from the libs/python/build subdirectory of your boost + installation (if you have already built boost from the top level this may have + no effect, since the work is already done).

+

Configuration

+

You may need to configure the following variables to point Boost.Build at your + Python installation:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Variable NameSemanticsDefaultNotes
PYTHON_ROOT The root directory of your Python installationWindows:
+ c:/tools/python
+ Unix: /usr/local
On Unix, this is the --with-prefix= directory used to configure + Python
PYTHON_VERSION The The 2-part python Major.Minor version numberWindows: 2.1 Unix: 1.5Be sure not to include a third number, e.g. not "2.2.1", even + if that's the version you have.
PYTHON_INCLUDES path to Python #include directoriesAutoconfigured from
+ PYTHON_ROOT
 
PYTHON_LIB_PATHpath to Python library object.Autoconfigured from
+ PYTHON_ROOT
 
PYTHON_STDLIB_PATHpath to Python standard library modulesAutoconfigured from
+ PYTHON_ROOT
 
CYGWIN_ROOT path to the user's Cygwin installationAutoconfigured from
+ PYTHON_ROOT
Cygwin only. This and the following + two settings are useful when building with multiple toolsets on Windows, + since Cygwin requires a different build of Python.
GCC_PYTHON_ROOTpath to the user's Cygwin Python installation$(CYGWIN_ROOT)
+ /usr/local
Cygwin only
GCC_DEBUG_PYTHON_ROOT path to the user's Cygwin pydebug + build$(CYGWIN_ROOT)
+ /usr/local/pydebug
Cygwin only
+

Results

+

The build process will create a libs/python/build/bin-stage subdirectory + of the boost root (or of $(ALL_LOCATE_TARGET), if you have set that + variable), containing the built libraries. The libraries are actually built + to unique directories for each toolset and variant elsewhere in the filesystem, + and copied to the bin-stage directory as a convenience, so if you build with + multiple toolsets at once, the product of later toolsets will overwrite that + of earlier toolsets in bin-stage.

+

Testing

+

To build and test Boost.Python from within the libs/python/build directory, + invoke

+
    bjam -sTOOLS=toolset test
+

This will update all of the Boost.Python v1 test and example targets. The tests + are relatively quiet by default. To get more-verbose output, you might try

+
    bjam -sTOOLS=toolset -sPYTHON_TEST_ARGS=-v test
+

which will print each test's Python code with the expected output as it passes.

+

Building your Extension Module

+

Though there are other approaches, the easiest way to build an extension module + using Boost.Python is with Boost.Build. Until Boost.Build v2 is released, cross-project + build dependencies are not supported, so it works most smoothly if you add a + new subproject to your boost installation. The libs/python/example + subdirectory of your boost installation contains a minimal example (along with + many extra sources). To copy the example subproject:

+
    +
  1. Create a new subdirectory in, libs/python, say libs/python/my_project.
  2. +
  3. Copy libs/python/example/Jamfile + to your new directory.
  4. +
  5. Edit the Jamfile as appropriate for your project. You'll want to change + the subproject rule invocation at the top, and the names of some + of the source files and/or targets.
  6. +
+

If you can't modify or copy your boost installation, the alternative is to + create your own Boost.Build project. A similar example you can use as a starting + point is available in this archive. You'll + need to edit the Jamfile and Jamrules files, depending on the relative location + of your Boost installation and the new project. Note that automatic testing + of extension modules is not available in this configuration.

+

Build Variants

+

Three variant configurations of all python-related targets are supported, and + can be selected by setting the BUILD variable:

+

* release (optimization, -DNDEBUG)
+ * debug (no optimization -D_DEBUG)
+ * debug-python (no optimization, -D_DEBUG -DBOOST_DEBUG_PYTHON)

+

The first two variants of the boost_python library are built by default, and + are compatible with the default Python distribution. The debug-python variant + corresponds to a specially-built debugging version of Python. On Unix platforms, + this python is built by adding --with-pydebug when configuring the + Python build. On Windows, the debugging version of Python is generated by the + "Win32 Debug" target of the PCBuild.dsw Visual C++ 6.0 project in + the PCBuild subdirectory of your Python distribution. Extension modules built + with Python debugging enabled are not link-compatible with a non-debug build + of Python. Since few people actually have a debug build of Python (it doesn't + come with the standard distribution), the normal debug variant builds modules + which are compatible with ordinary Python.

+

On many windows compilers, when extension modules are built with -D_DEBUG, + Python defaults to force linking with a special debugging version of the Python + DLL. Since this debug DLL isn't supplied with the default Python installation + for Windows, Boost.Python uses boost/python/detail/wrap_python.hpp + to temporarily undefine _DEBUG when Python.h is #included + - unless BOOST_DEBUG_PYTHON is defined.

+

If you want the extra runtime checks available with the debugging version of + the library, #define BOOST_DEBUG_PYTHON + to re-enable python debuggin, and link with the debug-python variant of boost_python.

+

If you do not #define BOOST_DEBUG_PYTHON, + be sure that any source files in your extension module #include + <boost/python/detail/wrap_python.hpp> instead of the usual Python.h, + or you will have link incompatibilities.

+ + + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/building_hello_world.html b/doc/tutorial/doc/building_hello_world.html new file mode 100644 index 00000000..8595ea56 --- /dev/null +++ b/doc/tutorial/doc/building_hello_world.html @@ -0,0 +1,178 @@ + + + +Building Hello World + + + + + + + + + + +
+ + Building Hello World +
+
+ + + + + + +
+

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.

+

+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. +After this brief bjam tutorial, we should have built two DLLs:

+

+if you are on Windows, and

+

+if you are on Unix.

+

+The tutorial example can be found in the directory: +libs/python/example/tutorial. There, you can find:

+

+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. Pre-built Boost.Jam executables are available for most +platforms. For example, a pre-built Microsoft Windows bjam executable can +be downloaded +here. +The complete list of bjam pre-built executables can be found +here.

+

Lets Jam!

+

+

+Here is our minimalist Jamfile:

+
+    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:

+
+    subproject libs/python/example/tutorial ;
+

+Then we will include the definitions needed by Python modules:

+
+    SEARCH on python.jam = $(BOOST_BUILD_PATH) ;
+    include python.jam ;
+

+Finally we declare our hello extension:

+
+    extension hello                     # Declare a Python extension called hello
+    :   hello.cpp                       # source
+        <dll>../../build/boost_python   # dependencies
+        ;
+

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. 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 + +Building Boost Libraries for +further details.

+

+It should be building now:

+
+    cd C:\dev\boost\libs\python\example\tutorial
+    bjam -sTOOLS=msvc
+    ...patience...
+    ...found 1703 targets...
+    ...updating 40 targets...
+

+And so on... Finally:

+
+    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:

+

+if you are on Windows, and

+

+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!

+ + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/call_policies.html b/doc/tutorial/doc/call_policies.html new file mode 100644 index 00000000..194097df --- /dev/null +++ b/doc/tutorial/doc/call_policies.html @@ -0,0 +1,169 @@ + + + +Call Policies + + + + + + + + + + +
+ + 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:

+
  1. f is called passing in a reference to y and a pointer to z
  2. A reference to y.x is returned
  3. y is deleted. x is a dangling reference
  4. x.some_method() is called
  5. 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:

+
  1. f is called passing in a reference to y and a pointer to z
  2. A pointer to z is held by y
  3. A reference to y.x is returned
  4. z is deleted. y.z is a dangling pointer
  5. y.z_value() is called
  6. z->value() is called
  7. BOOM!

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: Z* z).

+

+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 +here.

+ + + + +
+ Remember the Zen, Luke:

+"Explicit is better than implicit"
+"In the face of ambiguity, refuse the temptation to guess"
+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/class_data_members.html b/doc/tutorial/doc/class_data_members.html new file mode 100644 index 00000000..39847ba8 --- /dev/null +++ b/doc/tutorial/doc/class_data_members.html @@ -0,0 +1,77 @@ + + + +Class Data Members + + + + + + + + + + +
+ + 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:

+
+    >>> 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.

+
+    >>> x.name = 'e' # can't change name
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in ?
+    AttributeError: can't set attribute
+
+ + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/class_operators_special_functions.html b/doc/tutorial/doc/class_operators_special_functions.html new file mode 100644 index 00000000..74a952a2 --- /dev/null +++ b/doc/tutorial/doc/class_operators_special_functions.html @@ -0,0 +1,101 @@ + + + +Class Operators/Special Functions + + + + + + + + + + +
+ + Class Operators/Special Functions +
+
+ + + + + + +
+

Python Operators

+C is well known for the abundance of oparators. 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())          // __rsub__
+        .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".

+

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))     // __pow__
+        .def(abs(self))     // __abs__
+        .def(str(self))     // __str__
+        ;
+
+

+Need we say more?

+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/class_properties.html b/doc/tutorial/doc/class_properties.html new file mode 100644 index 00000000..cd2267d5 --- /dev/null +++ b/doc/tutorial/doc/class_properties.html @@ -0,0 +1,81 @@ + + + +Class Properties + + + + + + + + + + +
+ + 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", &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)
+
+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/class_virtual_functions.html b/doc/tutorial/doc/class_virtual_functions.html new file mode 100644 index 00000000..72c3fe3d --- /dev/null +++ b/doc/tutorial/doc/class_virtual_functions.html @@ -0,0 +1,227 @@ + + + +Class Virtual Functions + + + + + + + + + + +
+ + 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;
+    };
+
+ + + + +
+ member function and methods

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.

+ + + + +
+ Why do we need BaseWrap?

+ +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."

+ +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.

+ +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()
+    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.

+

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:

+
  1. call_f(derived()) is called in Python
  2. This corresponds to def("call_f", call_f);. Boost.Python dispatches this call.
  3. int call_f(Base& b) { return b.f(); } accepts the call.
  4. The overridden virtual function f of BaseWrap is called.
  5. call_method<int>(self, "f"); dispatches the call back to Python.
  6. 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
+
+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/constructors.html b/doc/tutorial/doc/constructors.html new file mode 100644 index 00000000..79237526 --- /dev/null +++ b/doc/tutorial/doc/constructors.html @@ -0,0 +1,102 @@ + + + +Constructors + + + + + + + + + + +
+ + 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.

+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/default_arguments.html b/doc/tutorial/doc/default_arguments.html new file mode 100644 index 00000000..bfb3ce3b --- /dev/null +++ b/doc/tutorial/doc/default_arguments.html @@ -0,0 +1,118 @@ + + + +Default Arguments + + + + + + + + + + +
+ + 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:

+

+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<int, optional<char, std::string, double> >())
+
+

+Notice the use of init<...> and optional<...> to signify the default +(optional arguments).

+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/derived_object_types.html b/doc/tutorial/doc/derived_object_types.html new file mode 100644 index 00000000..303774f7 --- /dev/null +++ b/doc/tutorial/doc/derived_object_types.html @@ -0,0 +1,117 @@ + + + +Derived Object types + + + + + + + + + + +
+ + Derived Object types +
+
+ + + + + + +
+

+Boost.Python comes with a set of derived object types corresponding to +that of Python's:

+

+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 objects 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++.

+

+ 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
+
+

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);
+
+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/exception_translation.html b/doc/tutorial/doc/exception_translation.html new file mode 100644 index 00000000..9ec6bc86 --- /dev/null +++ b/doc/tutorial/doc/exception_translation.html @@ -0,0 +1,60 @@ + + + +Exception Translation + + + + + + + + + +
+ + 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/exposing_classes.html b/doc/tutorial/doc/exposing_classes.html new file mode 100644 index 00000000..7a989346 --- /dev/null +++ b/doc/tutorial/doc/exposing_classes.html @@ -0,0 +1,79 @@ + + + +Exposing Classes + + + + + + + + + + +
+ + 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'
+
+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/extracting_c___objects.html b/doc/tutorial/doc/extracting_c___objects.html new file mode 100644 index 00000000..6000821b --- /dev/null +++ b/doc/tutorial/doc/extracting_c___objects.html @@ -0,0 +1,79 @@ + + + +Extracting C++ objects + + + + + + + + + + +
+ + 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 doubles. 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 objects. 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(); ...
+
+

+ The astute reader might have noticed that the extract<T> +facility in fact solves mutable copying problem:

+
+    dict d = extract<dict>(x.attr("__dict__"));
+    d['whatever'] = 3;          #modifies x.__dict__ !
+
+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/functions.html b/doc/tutorial/doc/functions.html new file mode 100644 index 00000000..036242e2 --- /dev/null +++ b/doc/tutorial/doc/functions.html @@ -0,0 +1,73 @@ + + + +Functions + + + + + + + + + + +
+ + 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.

+
+    >>> 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!
+
+ + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/inheritance.html b/doc/tutorial/doc/inheritance.html new file mode 100644 index 00000000..0f9fe33f --- /dev/null +++ b/doc/tutorial/doc/inheritance.html @@ -0,0 +1,98 @@ + + + +Inheritance + + + + + + + + + + +
+ + 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:

+
  1. Derived automatically inherits all of Base's Python methods (wrapped C++ member functions)
  2. 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 later.

+
+    // Tell Python to take ownership of factory's result
+    def("factory", factory,
+        return_value_policy<manage_new_object>());
+
+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/iterators.html b/doc/tutorial/doc/iterators.html new file mode 100644 index 00000000..7e4282cb --- /dev/null +++ b/doc/tutorial/doc/iterators.html @@ -0,0 +1,101 @@ + + + +Iterators + + + + + + + + + + +
+ + Iterators +
+
+ + + + + + +
+

+In C++, and STL in particular, we see iterators everywhere. Python also has +iterators, but these are two very different beasts.

+

+C++ iterators:

+

+Python Iterators:

+

+The typical Python iteration protocol: for y in x... is as follows:

+
+    iter 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:

+

+Here, start/finish may be one of:

+

+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_<F>("Field")
+        .property("pions", range(&F::p_begin, &F::p_end))
+        .property("bogons", range(&F::b_begin, &F::b_end));
+
+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/object_interface.html b/doc/tutorial/doc/object_interface.html new file mode 100644 index 00000000..2279a60c --- /dev/null +++ b/doc/tutorial/doc/object_interface.html @@ -0,0 +1,54 @@ + + + +Object Interface + + + + + + + + + + +
+ + Object Interface +
+
+ + + + + + +
+

+Python is dynamically typed, unlike C++ which is statically typed. Python +variables may hold an integers, 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++ objects are as close as possible to Python. This +should minimize the learning curve significantly.

+

+

+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/quickstart.html b/doc/tutorial/doc/quickstart.html new file mode 100644 index 00000000..ec1a89ac --- /dev/null +++ b/doc/tutorial/doc/quickstart.html @@ -0,0 +1,79 @@ + + + +QuickStart + + + + + + + + + +
+ + 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).

+

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...

+ + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/quickstart.txt b/doc/tutorial/doc/quickstart.txt new file mode 100644 index 00000000..a8176ec1 --- /dev/null +++ b/doc/tutorial/doc/quickstart.txt @@ -0,0 +1,1185 @@ +[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]. + +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. 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 oparators. 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)) // __pow__ + .def(abs(self)) // __abs__ + .def(str(self)) // __str__ + ; + +Need we say more? + +[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: [^Z* z]). + +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 integers, 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, f): + if (y == 'foo'): + x[3:7] = 'bar' + else: + x.items += f(3, x) + return x + + def getfunc(): + return f; + +Can be rewritten in C++ using Boost.Python facilities this way: + + object f(object x, object f) { + if (f == "foo") + x.slice(3,7) = "bar"; + else + x.attr("items") += f(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_ + +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 mutable copying problem: + + dict d = extract(x.attr("__dict__")); + d['whatever'] = 3; # modifies x.__dict__ ! + +[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 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 00000000..270764cc Binary files /dev/null and b/doc/tutorial/doc/theme/alert.gif differ diff --git a/doc/tutorial/doc/theme/arrow.gif b/doc/tutorial/doc/theme/arrow.gif new file mode 100644 index 00000000..e33db0fb Binary files /dev/null and b/doc/tutorial/doc/theme/arrow.gif differ diff --git a/doc/tutorial/doc/theme/bkd.gif b/doc/tutorial/doc/theme/bkd.gif new file mode 100644 index 00000000..dcabcb80 Binary files /dev/null and b/doc/tutorial/doc/theme/bkd.gif differ diff --git a/doc/tutorial/doc/theme/bkd2.gif b/doc/tutorial/doc/theme/bkd2.gif new file mode 100644 index 00000000..b03d9ba9 Binary files /dev/null and b/doc/tutorial/doc/theme/bkd2.gif differ diff --git a/doc/tutorial/doc/theme/bulb.gif b/doc/tutorial/doc/theme/bulb.gif new file mode 100644 index 00000000..74f3baac Binary files /dev/null and b/doc/tutorial/doc/theme/bulb.gif differ diff --git a/doc/tutorial/doc/theme/bullet.gif b/doc/tutorial/doc/theme/bullet.gif new file mode 100644 index 00000000..da787e2e Binary files /dev/null and b/doc/tutorial/doc/theme/bullet.gif differ diff --git a/doc/tutorial/doc/theme/c++boost.gif b/doc/tutorial/doc/theme/c++boost.gif new file mode 100644 index 00000000..58be431a Binary files /dev/null and b/doc/tutorial/doc/theme/c++boost.gif differ diff --git a/doc/tutorial/doc/theme/jam.png b/doc/tutorial/doc/theme/jam.png new file mode 100644 index 00000000..224ed791 Binary files /dev/null and b/doc/tutorial/doc/theme/jam.png differ diff --git a/doc/tutorial/doc/theme/l_arr.gif b/doc/tutorial/doc/theme/l_arr.gif new file mode 100644 index 00000000..5b3cb1cb Binary files /dev/null and b/doc/tutorial/doc/theme/l_arr.gif differ diff --git a/doc/tutorial/doc/theme/l_arr_disabled.gif b/doc/tutorial/doc/theme/l_arr_disabled.gif new file mode 100644 index 00000000..ed58a605 Binary files /dev/null and b/doc/tutorial/doc/theme/l_arr_disabled.gif differ diff --git a/doc/tutorial/doc/theme/lens.gif b/doc/tutorial/doc/theme/lens.gif new file mode 100644 index 00000000..c7901819 Binary files /dev/null and b/doc/tutorial/doc/theme/lens.gif differ diff --git a/doc/tutorial/doc/theme/note.gif b/doc/tutorial/doc/theme/note.gif new file mode 100644 index 00000000..bd92f075 Binary files /dev/null and b/doc/tutorial/doc/theme/note.gif differ diff --git a/doc/tutorial/doc/theme/python.png b/doc/tutorial/doc/theme/python.png new file mode 100644 index 00000000..cc2ff1d5 Binary files /dev/null and b/doc/tutorial/doc/theme/python.png differ diff --git a/doc/tutorial/doc/theme/r_arr.gif b/doc/tutorial/doc/theme/r_arr.gif new file mode 100644 index 00000000..2dcdad11 Binary files /dev/null and b/doc/tutorial/doc/theme/r_arr.gif differ 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 00000000..2100f78b Binary files /dev/null and b/doc/tutorial/doc/theme/r_arr_disabled.gif differ diff --git a/doc/tutorial/doc/theme/smiley.gif b/doc/tutorial/doc/theme/smiley.gif new file mode 100644 index 00000000..4c848f8f Binary files /dev/null and b/doc/tutorial/doc/theme/smiley.gif differ diff --git a/doc/tutorial/doc/theme/style.css b/doc/tutorial/doc/theme/style.css new file mode 100644 index 00000000..53a6205e --- /dev/null +++ b/doc/tutorial/doc/theme/style.css @@ -0,0 +1,170 @@ +body +{ + background-image: url(bkd.gif); + background-color: #FFFFFF; + margin: 1em 2em 1em 2em; +} + +h1 { font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold; text-align: left; } +h2 { font: 140% sans-serif; font-weight: bold; text-align: left; } +h3 { font: 120% sans-serif; font-weight: bold; text-align: left; } +h4 { font: bold 100% sans-serif; font-weight: bold; text-align: left; } +h5 { font: italic 100% sans-serif; font-weight: bold; text-align: left; } +h6 { font: small-caps 100% sans-serif; font-weight: bold; text-align: left; } + +pre +{ + border-top: gray 1pt solid; + border-right: gray 1pt solid; + border-left: gray 1pt solid; + border-bottom: gray 1pt solid; + + padding-top: 2pt; + padding-right: 2pt; + padding-left: 2pt; + padding-bottom: 2pt; + + display: block; + font-family: "courier new", courier, mono; + background-color: #eeeeee; font-size: small +} + +code +{ + font-family: "Courier New", Courier, mono; + font-size: small +} + +tt +{ + display: inline; + font-family: "Courier New", Courier, mono; + color: #000099; + font-size: small +} + +p +{ + text-align: justify; + font-family: Georgia, "Times New Roman", Times, serif +} + +ul +{ + list-style-image: url(bullet.gif); + font-family: Georgia, "Times New Roman", Times, serif +} + +ol +{ + font-family: Georgia, "Times New Roman", Times, serif +} + +a +{ + font-weight: bold; + color: #003366; + text-decoration: none; +} + +a:hover { color: #8080FF; } + +.literal { color: #666666; font-style: italic} +.keyword { color: #000099} +.identifier {} +.comment { font-style: italic; color: #990000} +.special { color: #800040} +.preprocessor { color: #FF0000} +.string { font-style: italic; color: #666666} +.copyright { color: #666666; font-size: small} +.white_bkd { background-color: #FFFFFF} +.dk_grey_bkd { background-color: #999999} +.quotes { color: #666666; font-style: italic; font-weight: bold} + +.note_box +{ + display: block; + + border-top: gray 1pt solid; + border-right: gray 1pt solid; + border-left: gray 1pt solid; + border-bottom: gray 1pt solid; + + padding-right: 12pt; + padding-left: 12pt; + padding-bottom: 12pt; + padding-top: 12pt; + + font-family: Arial, Helvetica, sans-serif; + background-color: #E2E9EF; + font-size: small; text-align: justify +} + +.table_title +{ + background-color: #648CCA; + + font-family: Verdana, Arial, Helvetica, sans-serif; color: #FFFFFF; + font-weight: bold +; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 4px +} + +.table_cells +{ + background-color: #E2E9EF; + + font-family: Geneva, Arial, Helvetica, san-serif; + font-size: small +; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 4px +} + +.toc +{ + DISPLAY: block; + background-color: #E2E9EF + font-family: Arial, Helvetica, sans-serif; + + border-top: gray 1pt solid; + border-left: gray 1pt solid; + border-bottom: gray 1pt solid; + border-right: gray 1pt solid; + + padding-top: 24pt; + padding-right: 24pt; + padding-left: 24pt; + padding-bottom: 24pt; +} + +.toc_title +{ + background-color: #648CCA; + padding-top: 4px; + padding-right: 4px; + padding-bottom: 4px; + padding-left: 4px; + font-family: Geneva, Arial, Helvetica, san-serif; + color: #FFFFFF; + font-weight: bold +} + +.toc_cells +{ + background-color: #E2E9EF; + padding-top: 4px; + padding-right: 4px; + padding-bottom: 4px; + padding-left: 4px; + font-family: Geneva, Arial, Helvetica, san-serif; + font-size: small +} + +div.logo +{ + float: right; +} + +.toc_cells_L0 { background-color: #E2E9EF; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 4px; font-family: Geneva, Arial, Helvetica, san-serif; font-size: small } +.toc_cells_L1 { background-color: #E2E9EF; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 44px; font-family: Geneva, Arial, Helvetica, san-serif; font-size: small } +.toc_cells_L2 { background-color: #E2E9EF; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 88px; font-family: Geneva, Arial, Helvetica, san-serif; font-size: small } +.toc_cells_L3 { background-color: #E2E9EF; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 122px; font-family: Geneva, Arial, Helvetica, san-serif; font-size: small } +.toc_cells_L4 { background-color: #E2E9EF; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 166px; font-family: Geneva, Arial, Helvetica, san-serif; font-size: small } diff --git a/doc/tutorial/doc/theme/u_arr.gif b/doc/tutorial/doc/theme/u_arr.gif new file mode 100644 index 00000000..ada3d6e0 Binary files /dev/null and b/doc/tutorial/doc/theme/u_arr.gif differ diff --git a/doc/tutorial/index.html b/doc/tutorial/index.html new file mode 100644 index 00000000..14554d97 --- /dev/null +++ b/doc/tutorial/index.html @@ -0,0 +1,121 @@ + + + +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 +
+ Iterators +
+ Exception Translation +
+
+
+ + diff --git a/doc/v2/python.html b/doc/v2/python.html new file mode 100644 index 00000000..78706681 --- /dev/null +++ b/doc/v2/python.html @@ -0,0 +1,108 @@ + + + + + + + + + Boost.Python - <boost/python.hpp> + + + + + + + + + +
+

C++ Boost

+
+

Boost.Python

+ +

Header <boost/python.hpp>

+
+
+ +

Contents

+ +
+
Introduction
+
+
+ +

Introduction

+ +

This is a convenience header which #includes all of the public + interface headers that are part of the Boost.Python library

+
+# include <args.hpp>
+# include <args_fwd.hpp>
+# include <back_reference.hpp>
+# include <bases.hpp>
+# include <borrowed.hpp>
+# include <call.hpp>
+# include <call_method.hpp>
+# include <class.hpp>
+# include <copy_const_reference.hpp>
+# include <copy_non_const_reference.hpp>
+# include <data_members.hpp>
+# include <def.hpp>
+# include <default_call_policies.hpp>
+# include <dict.hpp>
+# include <enum.hpp>
+# include <errors.hpp>
+# include <exception_translator.hpp>
+# include <extract.hpp>
+# include <handle.hpp>
+# include <has_back_reference.hpp>
+# include <implicit.hpp>
+# include <init.hpp>
+# include <instance_holder.hpp>
+# include <iterator.hpp>
+# include <list.hpp>
+# include <long.hpp>
+# include <lvalue_from_pytype.hpp>
+# include <make_function.hpp>
+# include <manage_new_object.hpp>
+# include <module.hpp>
+# include <numeric.hpp>
+# include <object.hpp>
+# include <object_protocol.hpp>
+# include <object_protocol_core.hpp>
+# include <operators.hpp>
+# include <other.hpp>
+# include <overloads.hpp>
+# include <pointee.hpp>
+# include <ptr.hpp>
+# include <reference_existing_object.hpp>
+# include <return_internal_reference.hpp>
+# include <return_value_policy.hpp>
+# include <scope.hpp>
+# include <self.hpp>
+# include <slice_nil.hpp>
+# include <str.hpp>
+# include <to_python_converter.hpp>
+# include <to_python_indirect.hpp>
+# include <to_python_value.hpp>
+# include <tuple.hpp>
+# include <type_id.hpp>
+# include <with_custodian_and_ward.hpp>
+
+ +

Revised + + 08 October, 2002 + +

+ +

© Copyright Dave Abrahams 2002. All Rights + Reserved.

+ + + diff --git a/example/tutorial/Jamfile b/example/tutorial/Jamfile new file mode 100644 index 00000000..c05463c1 --- /dev/null +++ b/example/tutorial/Jamfile @@ -0,0 +1,15 @@ +# Hello World Example from the tutorial +# [Joel de Guzman 10/9/2002] + +# Specify our location in the boost project hierarchy +subproject libs/python/example/tutorial ; + +# Include definitions needed for Python modules +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 + ; + diff --git a/example/tutorial/hello.cpp b/example/tutorial/hello.cpp new file mode 100644 index 00000000..01ed60c3 --- /dev/null +++ b/example/tutorial/hello.cpp @@ -0,0 +1,17 @@ +// Hello World Example from the tutorial +// [Joel de Guzman 10/9/2002] + +char const* greet() +{ + return "hello, world"; +} + +#include +#include +using namespace boost::python; + +BOOST_PYTHON_MODULE(hello) +{ + def("greet", greet); +} + diff --git a/include/boost/python.hpp b/include/boost/python.hpp new file mode 100644 index 00000000..ca81cce2 --- /dev/null +++ b/include/boost/python.hpp @@ -0,0 +1,62 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#ifndef PYTHON_DWA2002810_HPP +# define PYTHON_DWA2002810_HPP + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +#endif PYTHON_DWA2002810_HPP