diff --git a/doc/comparisons.html b/doc/comparisons.html new file mode 100644 index 00000000..57cec744 --- /dev/null +++ b/doc/comparisons.html @@ -0,0 +1,231 @@ + +

+ Like Boost.Python, CXX attempts to + provide a C++-oriented interface to Python. In most cases, as with the + boost library, it relieves the user from worrying about + reference-counts. Both libraries automatically convert thrown C++ + exceptions into Python exceptions. As far as I can tell, CXX has no + support for subclassing C++ extension types in Python. An even + more significant difference is that a user's C++ code is still basically + ``dealing with Python objects'', though they are wrapped in + C++ classes. This means such jobs as argument parsing and conversion are + still left to be done explicitly by the user. + +
+ CXX claims to interoperate well with the C++ Standard Library + (a.k.a. STL) by providing iterators into Python Lists and Dictionaries, + but the claim is unfortunately unsupportable. The problem is that in + general, access to Python sequence and mapping elements through + iterators requires the use of proxy objects as the return value of + iterator dereference operations. This usage conflicts with the basic + ForwardIterator requirements in + section 24.1.3 of the standard (dereferencing must produce a + reference). Although you may be able to use these iterators with some + operations in some standard library implementations, it is neither + guaranteed to work nor portable. + +
+ As far as I can tell, CXX enables one to write what is essentially + idiomatic Python code in C++, manipulating Python objects through the + same fully-generic interfaces we use in Python. While you're hardly + programming directly to the ``bare metal'' with CXX, it basically + presents a ``C++-ized'' version of the Python 'C' API. Some fraction of + that capability is available in Boost.Python through boost/python/objects.hpp, + which provides C++ objects corresponding to Python lists, tuples, + strings, and dictionaries, and through boost/python/callback.hpp, + which allows you to call back into python with C++ arguments. + +
+ Paul F. Dubois, the original + author of CXX, has told me that what I've described is only half of the + picture with CXX, but I never understood his explanation well-enough to + fill in the other half. Here is his response to the commentary above: + +
+``My intention with CXX was not to do what you are doing. It was to enable a +person to write an extension directly in C++ rather than C. I figured others had +the wrapping business covered. I thought maybe CXX would provide an easier +target language for those making wrappers, but I never explored +that.''+ +
-Paul Dubois +
+ SWIG is an impressively mature tool + for exporting an existing ANSI 'C' interface into various scripting + languages. Swig relies on a parser to read your source code and produce + additional source code files which can be compiled into a Python (or + Perl or Tcl) extension module. It has been successfully used to create + many Python extension modules. Like Boost.Python, SWIG is trying to allow an + existing interface to be wrapped with little or no change to the + existing code. The documentation says ``SWIG parses a form of ANSI C + syntax that has been extended with a number of special directives. As a + result, interfaces are usually built by grabbing a header file and + tweaking it a little bit.'' For C++ interfaces, the tweaking has often + proven to amount to more than just a little bit. One user + writes: + +
``The problem with swig (when I used it) is that it + couldnt handle templates, didnt do func overloading properly etc. For + ANSI C libraries this was fine. But for usual C++ code this was a + problem. Simple things work. But for anything very complicated (or + realistic), one had to write code by hand. I believe Boost.Python doesn't have + this problem[sic]... IMHO overloaded functions are very important to + wrap correctly.''+ +
-Prabhu Ramachandran +
+ By contrast, Boost.Python doesn't attempt to parse C++ - the problem is simply + too complex to do correctly. Technically, one does + write code by hand to use Boost.Python. The goal, however, has been to make + that code nearly as simple as listing the names of the classes and + member functions you want to expose in Python. + +
+ SIP + is a system similar to SWIG, though seemingly more + C++-oriented. The author says that like Boost.Python, SIP supports overriding + extension class member functions in Python subclasses. It appears to + have been designed specifically to directly support some features of + PyQt/PyKDE, which is its primary client. Documentation is almost + entirely missing at the time of this writing, so a detailed comparison + is difficult. + +
+ ILU + is a very ambitious project which tries to describe a module's interface + (types and functions) in terms of an Interface + Specification Language (ISL) so that it can be uniformly interfaced + to a wide range of computer languages, including Common Lisp, C++, C, + Modula-3, and Python. ILU can parse the ISL to generate a C++ language + header file describing the interface, of which the user is expected to + provide an implementation. Unlike Boost.Python, this means that the system + imposes implementation details on your C++ code at the deepest level. It + is worth noting that some of the C++ names generated by ILU are supposed + to be reserved to the C++ implementation. It is unclear from the + documentation whether ILU supports overriding C++ virtual functions in Python. + +
+ GRAD + is another very ambitious project aimed at generating Python wrappers for + interfaces written in ``legacy languages'', among which C++ is the first one + implemented. Like SWIG, it aims to parse source code and automatically + generate wrappers, though it appears to take a more sophisticated approach + to parsing in general and C++ in particular, so it should do a much better + job with C++. It appears to support function overloading. The + documentation is missing a lot of information I'd like to see, so it is + difficult to give an accurate and fair assessment. I am left with the + following questions: +
+ Anyone in the possession of the answers to these questions will earn my
+ gratitude for a write-up ;-)
+
+
+ + ExtensionClasses in Zope use the same underlying mechanism as Boost.Python + to support subclassing of extension types in Python, including + multiple-inheritance. Both systems support pickling/unpickling of + extension class instances in very similar ways. Both systems rely on the + same ``Don + Beaudry Hack'' that also inspired Don's MESS System. +
+ The major differences are: +
BaseClass.method''.
+ + Next: A Simple Example Using Boost.Python + Previous: A Brief Introduction to writing Python Extension Modules + Up: Top +
+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided ``as is'' without + express or implied warranty, and with no claim as to its suitability + for any purpose. +
+ Updated: Mar 6, 2001 +
+
++At first sight this might seem natural and straightforward. However, it +is a fairly complex problem to establish cross-extension-module +dependencies while maintaining the same ease of use Boost.Python +provides for classes that are wrapped in the same extension module. To +a large extent this complexity can be hidden from the author of a +Boost.Python extension module, but not entirely. + +
+import std_vector +v = std_vector.double([1, 2, 3, 4]) +v.push_back(5) +v.size() ++ +Suppose the std_vector module is done well and reflects all +C++ functions that are useful at the Python level, for all C++ built-in +data types (std_vector.int, std_vector.long, etc.). + +
+Suppose further that there is statistic module with a C++ class that +has constructors or member functions that use or return a +std::vector. For example: + +
+class xy {
+ public:
+ xy(const std::vector<double>& x, const std::vector<double>& y) : m_x(x), m_y(y) {}
+ const std::vector<double>& x() const { return m_x; }
+ const std::vector<double>& y() const { return m_y; }
+ double correlation();
+ private:
+ std::vector<double> m_x;
+ std::vector<double> m_y;
+}
+
+
+What is more natural than reusing the std_vector extension
+module to expose these constructors or functions to Python?
+
++Unfortunately, what seems natural needs a little work in both the +std_vector and the statistics module. + +
+In the std_vector extension module, +std::vector<double> is exposed to Python in the usual +way with the class_builder<> template. To also enable the +automatic conversion of std::vector<double> function +arguments or return values in other Boost.Python C++ modules, the +converters that convert a std::vector<double> C++ object +to a Python object and vice versa (i.e. the to_python() and +from_python() template functions) have to be exported. For +example: + +
+ #include <boost/python/cross_module.hpp> + //... + class_builder<std::vector<double> > v_double(std_vector_module, "double"); + export_converters(v_double); ++ +In the extension module that wraps class xy we can now import +these converters with the import_converters<> template. +For example: + +
+ #include <boost/python/cross_module.hpp>
+ //...
+ import_converters<std::vector<double> > v_double_converters("std_vector", "double");
+
+
+That is all. All the attributes that are defined for
+std_vector.double in the std_vector Boost.Python
+module will be available for the returned objects of xy.x()
+and xy.y(). Similarly, the constructor for xy will
+accept objects that were created by the std_vectormodule.
+
++class_builder<store> py_store(your_module, "store"); +export_converters_noncopyable(py_store); ++ +The corresponding import_converters<> statement does not +need any special attention: + +
+import_converters<store> py_store("noncopyable_export", "store");
+
+
++import std_vector +import statistics +x = std_vector.double([1, 2, 3, 4]) +y = std_vector.double([2, 4, 6, 8]) +xy = statistics.xy(x, y) +xy.correlation() ++ +In this example it is clear that Python has to be able to find both the +std_vector and the statistics extension module. In +other words, both extension modules need to be in the Python module +search path (sys.path). + +
+The situation is not always this obvious. Suppose the +statistics module has a random() function that +returns a vector of random numbers with a given length: + +
+import statistics +x = statistics.random(5) +y = statistics.random(5) +xy = statistics.xy(x, y) +xy.correlation() ++ +A naive user will not easily anticipate that the std_vector +module is used to pass the x and y vectors around. If +the std_vector module is in the Python module search path, +this form of ignorance is of no harm. On the contrary, we are glad +that we do not have to bother the user with details like this. + +
+If the std_vector module is not in the Python module search +path, a Python exception will be raised: + +
+Traceback (innermost last): + File "foo.py", line 2, in ? + x = statistics.random(5) +ImportError: No module named std_vector ++ +As is the case with any system of a non-trivial complexity, it is +important that the setup is consistent and complete. + +
+Suppose there is a module ivect that implements vectors of +integers, and a similar module dvect that implements vectors +of doubles. We want to be able do convert an integer vector to a double +vector and vice versa. For example: + +
+import ivect +iv = ivect.ivect((1,2,3,4,5)) +dv = iv.as_dvect() ++ +The last expression will implicitly import the dvect module in +order to enable the conversion of the C++ representation of +dvect to a Python object. The analogous is possible for a +dvect: + +
+import dvect +dv = dvect.dvect((1,2,3,4,5)) +iv = dv.as_ivect() ++ +Now the ivect module is imported implicitly. + +
+Note that the two-way dependencies are possible because the +dependencies are resolved only when needed. This is, the initialization +of the ivect module does not rely on the dvect +module, and vice versa. Only if as_dvect() or +as_ivect() is actually invoked will the corresponding module +be implicitly imported. This also means that, for example, the +dvect module does not have to be available at all if +as_dvect() is never used. + +
+If a library is wrapped that consists of both header files and compiled +components (e.g. libdvect.a, dvect.lib, etc.), both +the Boost.Python extension module with the +export_converters() statement and the module with the +import_converters<> statement need to be linked against +the object library. Ideally one would build a shared library (e.g. +libdvect.so, dvect.dll, etc.). However, this +introduces the issue of having to configure the search path for the +dynamic loading correctly. For small libraries it is therefore often +more convenient to ignore the fact that the object files are loaded +into memory more than once. + +
+Another motivation for the cross-module support is that two extension +modules that wrap the same class cannot both be imported into Python. +For example, if there are two modules A and B that +both wrap a given class X, this will work: + +
+import A +x = A.X() ++ +This will also work: + +
+import B +x = B.X() ++ +However, this will fail: + +
+import A +import B +python: /net/cci/rwgk/boost/boost/python/detail/extension_class.hpp:866: +static void boost::python::detail::class_registry<X>::register_class(boost::python::detail::extension_class_base *): +Assertion `static_class_object == 0' failed. +Abort ++ +A good solution is to wrap class X only once. Depending on the +situation, this could be done by module A or B, or an +additional small extension module that only wraps and exports +class X. + +
+Finally, there can be important psychological or political reasons for +using the cross-module support. If a group of classes is lumped +together with many others in a huge module, the authors will have +difficulties in being identified with their work. The situation is +much more transparent if the work is represented by a module with a +recognizable name. This is not just a question of strong egos, but also +of getting credit and funding. + +
+Updated: April 2001 + +

Because there is in general no way to deduce that a value of arbitrary type T
+is an enumeration constant, the Boost Python Library cannot automatically
+convert enum values to and from Python. To handle this case, you need to decide
+how you want the enum to show up in Python (since Python doesn't have
+enums). Once you have done that, you can write some simple
+from_python() and to_python() functions.
+
+
If you are satisfied with a Python int as a way to represent your enum
+values, we provide a shorthand for these functions. You just need to cause
+boost::python::enum_as_int_converters<EnumType> to be
+instantiated, where
+EnumType is your enumerated type. There are two convenient ways to do this:
+
+
+ +Some buggy C++ implementations require a class to be instantiated in the same +namespace in which it is defined. In that case, the simple incantation above becomes: + ++ template class boost::python::enum_as_int_converters<my_enum>; +
+
+ ...
+} // close my_namespace
+
+// drop into namespace python and explicitly instantiate
+namespace boost { namespace python {
+ template class enum_as_int_converters<my_enum_type>;
+}} // namespace boost::python
+
+namespace my_namespace { // re-open my_namespace
+ ...
+
+
+
+
+
+// instantiate as base class in any namespace
+struct EnumTypeConverters
+ : boost::python::enum_as_int_converters<EnumType>
+{
+};
+
+Either of the above is equivalent to the following declarations: +
+BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // this is a gcc 2.95.2 bug workaround
+
+ MyEnumType from_python(PyObject* x, boost::python::type<MyEnumType>)
+ {
+ return static_cast<MyEnum>(
+ from_python(x, boost::python::type<long>()));
+ }
+
+ MyEnumType from_python(PyObject* x, boost::python::type<const MyEnumType&>)
+ {
+ return static_cast<MyEnum>(
+ from_python(x, boost::python::type<long>()));
+ }
+
+ PyObject* to_python(MyEnumType x)
+ {
+ return to_python(static_cast<long>(x));
+ }
+BOOST_PYTHON_END_CONVERSION_NAMESPACE
+
+
+This technique defines the conversions of
+MyEnumType in terms of the conversions for the built-in
+ long type.
+
+You may also want to add a bunch of lines like this to your module
+initialization. These bind the corresponding enum values to the appropriate
+names so they can be used from Python:
+
+
+ +You can also add these to an extension class definition, if your enum happens to +be local to a class and you want the analogous interface in Python: + ++mymodule.add(boost::python::make_ref(enum_value_1), "enum_value_1"); +mymodule.add(boost::python::make_ref(enum_value_2), "enum_value_2"); +... +
++my_class_builder.add(boost::python::to_python(enum_value_1), "enum_value_1"); +my_class_builder.add(boost::python::to_python(enum_value_2), "enum_value_2"); +... +
+ Next: Pointers and Smart Pointers + Previous: Building an Extension Module + Up: Top +
+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided ``as + is'' without express or implied warranty, and with no claim as to + its suitability for any purpose. +
+ Updated: Mar 6, 2001 +
+ + Suppose we have the following C++ API which we want to expose in + Python: +
+
+#include <string>
+
+namespace { // Avoid cluttering the global namespace.
+
+ // A couple of simple C++ functions that we want to expose to Python.
+ std::string greet() { return "hello, world"; }
+ int square(int number) { return number * number; }
+}
+
+
+
+ + Here is the C++ code for a python module called getting_started1 + which exposes the API. +
+
+#include <boost/python/class_builder.hpp>
+namespace python = boost::python;
+
+BOOST_PYTHON_MODULE_INIT(getting_started1)
+{
+ // Create an object representing this extension module.
+ python::module_builder this_module("getting_started1");
+
+ // Add regular functions to the module.
+ this_module.def(greet, "greet");
+ this_module.def(square, "square");
+}
+
+
+
+ That's it! If we build this shared library and put it on our
+ PYTHONPATH we can now access our C++ functions from
+ Python.
+
++>>> import getting_started1 +>>> print getting_started1.greet() +hello, world +>>> number = 11 +>>> print number, '*', number, '=', getting_started1.square(number) +11 * 11 = 121 +++ Next: Exporting Classes + Previous: Comparisons with other systems Up: + Top +
+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +
+ Updated: Mar 6, 2000 +
+ + Now let's expose a C++ class to Python: + +
+#include <iostream>
+#include <string>
+
+namespace { // Avoid cluttering the global namespace.
+
+ // A friendly class.
+ class hello
+ {
+ public:
+ hello(const std::string& country) { this->country = country; }
+ std::string greet() const { return "Hello from " + country; }
+ private:
+ std::string country;
+ };
+
+ // A function taking a hello object as an argument.
+ std::string invite(const hello& w) {
+ return w.greet() + "! Please come soon!";
+ }
+}
+
+ + To expose the class, we use a class_builder in addition to the + module_builder from the previous example. Class member functions + are exposed by using the def() member function on the + class_builder: +
+#include <boost/python/class_builder.hpp>
+namespace python = boost::python;
+
+BOOST_PYTHON_MODULE_INIT(getting_started2)
+{
+ // Create an object representing this extension module.
+ python::module_builder this_module("getting_started2");
+
+ // Create the Python type object for our extension class.
+ python::class_builder<hello> hello_class(this_module, "hello");
+
+ // Add the __init__ function.
+ hello_class.def(python::constructor<std::string>());
+ // Add a regular member function.
+ hello_class.def(&hello::greet, "greet");
+
+ // Add invite() as a regular function to the module.
+ this_module.def(invite, "invite");
+
+ // Even better, invite() can also be made a member of hello_class!!!
+ hello_class.def(invite, "invite");
+}
+
+ +Now we can use the class normally from Python: + +
+>>> from getting_started2 import *
+>>> hi = hello('California')
+>>> hi.greet()
+'Hello from California'
+>>> invite(hi)
+'Hello from California! Please come soon!'
+>>> hi.invite()
+'Hello from California! Please come soon!'
+
+
+Notes:
+ We can even make a subclass of hello.world:
+
+
+>>> class wordy(hello):
+... def greet(self):
+... return hello.greet(self) + ', where the weather is fine'
+...
+>>> hi2 = wordy('Florida')
+>>> hi2.greet()
+'Hello from Florida, where the weather is fine'
+>>> invite(hi2)
+'Hello from Florida! Please come soon!'
+
+ + Pretty cool! You can't do that with an ordinary Python extension type! + + Of course, you may now have a slightly empty feeling in the pit of + your little pythonic stomach. Perhaps you wanted to see the following + wordy invitation: + +
+ + After all, invite calls hello::greet(), and you + reimplemented that in your Python subclass, wordy. If so, read on... + ++'Hello from Florida, where the weather is fine! Please come soon!' +
+ Next: Overridable virtual functions + Previous: A Simple Example Up: + Top +
+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +
+ Updated: Mar 6, 2001 +
+ + Interfacing any language to Python involves building a module which can + be loaded by the Python interpreter, but which isn't written in Python. + This is known as an extension module. Many of the built-in Python + libraries are constructed in 'C' this way; Python even supplies its + fundamental + types using the same mechanism. An extension module can be statically + linked with the Python interpreter, but it more commonly resides in a + shared library or DLL. +
+ As you can see from The Python Extending + and Embedding Tutorial, writing an extension module normally means + worrying about +
+ Another obstacle that most people run into eventually when extending + Python is that there's no way to make a true Python class in an extension + module. The typical solution is to create a new Python type in the + extension module, and then write an additional module in 100% Python. The + Python module defines a Python class which dispatches to an instance of + the extension type, which it contains. This allows users to write + subclasses of the class in the Python module, almost as though they were + sublcassing the extension type. Aside from being tedious, it's not really + the same as having a true class, because there's no way for the user to + override a method of the extension type which is called from the + extension module. Boost.Python solves this problem by taking advantage of Python's metaclass + feature to provide objects which look, walk, and hiss almost exactly + like regular Python classes. Boost.Python classes are actually cleaner than + Python classes in some subtle ways; a more detailed discussion will + follow (someday).
+Next: Comparisons with Other Systems Up: Top
++ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability for + any purpose.
+ diff --git a/doc/inheritance.html b/doc/inheritance.html new file mode 100644 index 00000000..56e96872 --- /dev/null +++ b/doc/inheritance.html @@ -0,0 +1,166 @@ + +
Inheritance
+ + Boost.Python extension classes support single and multiple-inheritance in + Python, just like regular Python classes. You can arbitrarily mix + built-in Python classes with extension classes in a derived class' + tuple of bases. Whenever a Boost.Python extension class is among the bases for a + new class in Python, the result is an extension class: +
++ ++>>> class MyPythonClass: +... def f(): return 'MyPythonClass.f()' +... +>>> import my_extension_module +>>> class Derived(my_extension_module.MyExtensionClass, MyPythonClass): +... '''This is an extension class''' +... pass +... +>>> x = Derived() +>>> x.f() +'MyPythonClass.f()' +>>> x.g() +'MyExtensionClass.g()' ++
+ Boost.Python also allows us to represent C++ inheritance relationships so that
+ wrapped derived classes may be passed where values, pointers, or
+ references to a base class are expected as arguments. The
+ declare_base member function of
+ class_builder<> is used to establish the relationship
+ between base and derived classes:
+
+
+
+#include <memory> // for std::auto_ptr<>
+
+struct Base {
+ virtual ~Base() {}
+ virtual const char* name() const { return "Base"; }
+};
+
+struct Derived : Base {
+ Derived() : x(-1) {}
+ virtual const char* name() const { return "Derived"; }
+ int x;
+};
+
+std::auto_ptr<Base> derived_as_base() {
+ return std::auto_ptr<Base>(new Derived);
+}
+
+const char* get_name(const Base& b) {
+ return b.name();
+}
+
+int get_derived_x(const Derived& d) {
+ return d.x;
+}
+
+#include <boost/python/class_builder.hpp>
+
+// namespace alias for code brevity
+namespace python = boost::python;
+
+BOOST_PYTHON_MODULE_INIT(my_module)
+{
+ python::module_builder my_module("my_module");
+
+ python::class_builder<Base> base_class(my_module, "Base");
+ base_class.def(python::constructor<>());
+
+ python::class_builder<Derived> derived_class(my_module, "Derived");
+ derived_class.def(python::constructor<>());
+ // Establish the inheritance relationship between Base and Derived
+ derived_class.declare_base(base_class);
+
+ my_module.def(derived_as_base, "derived_as_base");
+ my_module.def(get_name, "get_name");
+ my_module.def(get_derived_x, "get_derived_x");
+}
+
+
+
++ Then, in Python: +
++objects of wrapped class Derived may be passed where Base is expected ++>>> from my_module import * +>>> base = Base() +>>> derived = Derived() +>>> get_name(base) +'Base' ++
++objects of wrapped class Derived can be passed where Derived is +expected but where type information has been lost. ++>>> get_name(derived) +'Derived' ++
++ ++>>> get_derived_x(derived_as_base()) +-1 ++
+ If for some reason your base class has no virtual functions but you still want
+ to represent the inheritance relationship between base and derived classes,
+ pass the special symbol boost::python::without_downcast as the 2nd parameter
+ to declare_base:
+
+
+
+struct Base2 {};
+struct Derived2 { int f(); };
+
+ ...
+ python::class_builder<Base> base2_class(my_module, "Base2");
+ base2_class.def(python::constructor<>());
+
+ python::class_builder<Derived2> derived2_class(my_module, "Derived2");
+ derived2_class.def(python::constructor<>());
+ derived_class.declare_base(base_class, python::without_downcast);
+
+
+
+This approach will allow Derived2 objects to be passed where
+ Base2 is expected, but does not attempt to implicitly convert (downcast)
+ smart-pointers to Base2 into Derived2 pointers,
+ references, or values.
+
+
+ Next: Special Method and Operator Support + Previous: Function Overloading + Up: Top +
+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +
+ Updated: Nov 26, 2000 +
Function Overloading
+
+ To expose overloaded functions in Python, simply def() each
+ one with the same Python name:
+
+
+inline int f1() { return 3; }
+inline int f2(int x) { return x + 1; }
+
+class X {
+public:
+ X() : m_value(0) {}
+ X(int n) : m_value(n) {}
+ int value() const { return m_value; }
+ void value(int v) { m_value = v; }
+private:
+ int m_value;
+};
+ ...
+
+BOOST_PYTHON_MODULE_INIT(overload_demo)
+{
+ try
+ {
+ boost::python::module_builder overload_demo("overload_demo");
+ // Overloaded functions at module scope
+ overload_demo.def(f1, "f");
+ overload_demo.def(f2, "f");
+
+ boost::python::class_builder<X> x_class(overload_demo, "X");
+ // Overloaded constructors
+ x_class.def(boost::python::constructor<>());
+ x_class.def(boost::python::constructor<int>());
+
+ // Overloaded member functions
+ x_class.def((int (X::*)() const)&X::value, "value");
+ x_class.def((void (X::*)(int))&X::value, "value");
+ ...
+
+
+
+ + Now in Python: +
+
+>>> from overload_demo import *
+>>> x0 = X()
+>>> x1 = X(1)
+>>> x0.value()
+0
+>>> x1.value()
+1
+>>> x0.value(3)
+>>> x0.value()
+3
+>>> X('hello')
+TypeError: No overloaded functions match (X, string). Candidates are:
+void (*)()
+void (*)(int)
+>>> f()
+3
+>>> f(4)
+5
+
+
+
++ Notice that overloading in the Python module was produced three ways:
int f1()
+ and int f2(int) and exposing them as f in Python.
+ class X
+ X::value.
+ + Techniques 1. and 3. above are really alternatives. In case 3, you need + to form a pointer to each of the overloaded functions. The casting + syntax shown above is one way to do that in C++. Case 1 does not require + complicated-looking casts, but may not be viable if you can't change + your C++ interface. N.B. There's really nothing unsafe about casting an + overloaded (member) function address this way: the compiler won't let + you write it at all unless you get it right. + +
+ This approach is not neccessarily better, but may be preferable for some + people who have trouble writing out the types of (member) function + pointers or simply prefer to avoid all casts as a matter of principle: +
+
+// Forwarding functions for X::value
+inline void set_x_value(X& self, int v) { self.value(v); }
+inline int get_x_value(X& self) { return self.value(); }
+ ...
+ // Overloaded member functions
+ x_class.def(set_x_value, "value");
+ x_class.def(get_x_value, "value");
+
+
+Here we are taking advantage of the ability to expose C++ functions at +namespace scope as Python member functions. + +
+ The function overload resolution mechanism works as follows: + +
+ +
def()ed. The first function whose signature can be made to
+ match each argument passed is the one which is ultimately called.
+ This means in particular that you cannot overload the same function on
+ both ``int'' and ``float'' because Python
+ automatically converts either of the two types into the other one.
+ If the ``float'' overload is found first, it is used
+ also used for arguments of type ``int'' as well, and the
+ ``int'' version of the function is never invoked.
+ + Next: Inheritance + Previous: Overridable Virtual Functions + Up: Top +
+ © Copyright David Abrahams 2001. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided ``as + is'' without express or implied warranty, and with no claim as to + its suitability for any purpose. +
+ Updated: Mar 6, 2001 +
+
+ + In the previous example we exposed a simple + C++ class in Python and showed that we could write a subclass. We even + redefined one of the functions in our derived class. Now we will learn + how to make the function behave virtually when called from C++. + + +
In this example, it is assumed that hello::greet() is a virtual
+member function:
+
+
+class hello
+{
+ public:
+ hello(const std::string& country) { this->country = country; }
+ virtual std::string greet() const { return "Hello from " + country; }
+ virtual ~hello(); // Good practice
+ ...
+};
+
+
++ We'll need a derived class* to help us + dispatch the call to Python. In our derived class, we need the following + elements: + +
PyObject* data member (usually
+ called self) that holds a pointer to the Python object corresponding
+ to our C++ hello instance.
+
+ PyObject* argument. The initial argument should be stored in the self data
+ member described above.
+
+
+struct hello_callback : hello
+{
+ // hello constructor storing initial self_ parameter
+ hello_callback(PyObject* self_, const std::string& x) // 2
+ : hello(x), self(self_) {}
+
+ // In case hello is returned by-value from a wrapped function
+ hello_callback(PyObject* self_, const hello& x) // 3
+ : hello(x), self(self_) {}
+
+ // Override greet to call back into Python
+ std::string greet() const // 4
+ { return boost::python::callback<std::string>::call_method(self, "greet"); }
+
+ // Supplies the default implementation of greet
+ static std::string default_greet(const hello& self_) const // 5
+ { return self_.hello::greet(); }
+ private:
+ PyObject* self; // 1
+};
+
+
++ Finally, we add hello_callback to the + class_builder<> declaration in our module initialization + function, and when we define the function, we must tell Boost.Python about the default + implementation: + +
+ ++// Create the Python type object for our extension class +boost::python::class_builder<hello,hello_callback> hello_class(hello, "hello"); +// Add a virtual member function +hello_class.def(&hello::greet, "greet", &hello_callback::default_greet); +
+ Now our Python subclass of hello behaves as expected: + +
+>>> class wordy(hello):
+... def greet(self):
+... return hello.greet(self) + ', where the weather is fine'
+...
+>>> hi2 = wordy('Florida')
+>>> hi2.greet()
+'Hello from Florida, where the weather is fine'
+>>> invite(hi2)
+'Hello from Florida, where the weather is fine! Please come soon!'
+
+ + *You may ask, "Why do we need this derived + class? This could have been designed so that everything gets done right + inside of hello." 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. + +
+ A pure virtual function with no implementation is actually a lot easier to + deal with than a virtual function with a default implementation. First of + all, you obviously don't need to supply + a default implementation. Secondly, you don't need to call + def() on the extension_class<> instance + for the virtual function. In fact, you wouldn't want to: if the + corresponding attribute on the Python class stays undefined, you'll get an + AttributeError in Python when you try to call the function, + indicating that it should have been implemented. For example: +
+
+struct baz {
+ virtual int pure(int) = 0;
+ int calls_pure(int x) { return pure(x) + 1000; }
+};
+
+struct baz_callback {
+ int pure(int x) { boost::python::callback<int>::call_method(m_self, "pure", x); }
+};
+
+BOOST_PYTHON_MODULE_INIT(foobar)
+{
+ boost::python::module_builder foobar("foobar");
+ boost::python::class_builder<baz,baz_callback> baz_class("baz");
+ baz_class.def(&baz::calls_pure, "calls_pure");
+}
+
+
+ + Now in Python: +
++ ++>>> from foobar import baz +>>> x = baz() +>>> x.pure(1) +Traceback (innermost last): + File "<stdin>", line 1, in ? +AttributeError: pure +>>> x.calls_pure(1) +Traceback (innermost last): + File "<stdin>", line 1, in ? +AttributeError: pure +>>> class mumble(baz): +... def pure(self, x): return x + 1 +... +>>> y = mumble() +>>> y.pure(99) +100 +>>> y.calls_pure(99) +1100 +
This is one area where some minor intrusiveness on the wrapped library is +required. Once it has been overridden, the only way to call the base class +implementation of a private virtual function is to make the derived class a +friend of the base class. You didn't hear it from me, but most C++ +implementations will allow you to change the declaration of the base class in +this limited way without breaking binary compatibility (though it will certainly +break the ODR). + +
+ Next: Function Overloading + Previous: Exporting Classes + Up: Top +
+ © Copyright David Abrahams 2001. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability for + any purpose. +
+ Updated: Mar 21, 2001 + diff --git a/doc/pickle.html b/doc/pickle.html new file mode 100644 index 00000000..994a78ab --- /dev/null +++ b/doc/pickle.html @@ -0,0 +1,272 @@ + + +
+
++It is often necessary to save and restore the contents of an object to +a file. One approach to this problem is to write a pair of functions +that read and write data from a file in a special format. A powerful +alternative approach is to use Python's pickle module. Exploiting +Python's ability for introspection, the pickle module recursively +converts nearly arbitrary Python objects into a stream of bytes that +can be written to a file. + +
+The Boost Python Library supports the pickle module by emulating the +interface implemented by Jim Fulton's ExtensionClass module that is +included in the +ZOPE +distribution. +This interface is similar to that for regular Python classes as +described in detail in the +Python Library Reference for pickle. + +
+ If __getinitargs__ is not defined, the class constructor + will be called without arguments. + +
+
+ If __getstate__ is not defined, the instance's + __dict__ is pickled (if it is not empty). + +
+
+ If __setstate__ is not defined, the result of + __getstate__ must be a Python dictionary. The items of this + dictionary are added to the instance's __dict__. + +
+ However, most C++ classes wrapped with Boost.Python will have member + data that are not restored correctly by this procedure. To alert the + user to this problem, a safety guard is provided. If both + __getinitargs__ and __getstate__ are not defined, + Boost.Python tests if the class has an attribute + __dict_defines_state__. An exception is raised if this + attribute is not defined: + +
+ RuntimeError: Incomplete pickle support (__dict_defines_state__ not set) ++ + In the rare cases where this is not the desired behavior, the safety + guard can deliberately be disabled. The corresponding C++ code for + this is, e.g.: + +
+ class_builder<your_class> py_your_class(your_module, "your_class"); + py_your_class.dict_defines_state(); ++ + It is also possible to override the safety guard at the Python level. + E.g.: + +
+ import your_bpl_module + class your_class(your_bpl_module.your_class): + __dict_defines_state__ = 1 ++ +
+
+
+
+ + To alert the user to this highly unobvious problem, a safety guard is + provided. If __getstate__ is defined and the instance's + __dict__ is not empty, Boost.Python tests if the class has + an attribute __getstate_manages_dict__. An exception is + raised if this attribute is not defined: + +
+ RuntimeError: Incomplete pickle support (__getstate_manages_dict__ not set) ++ + To resolve this problem, it should first be established that the + __getstate__ and __setstate__ methods manage the + instances's __dict__ correctly. Note that this can be done + both at the C++ and the Python level. Finally, the safety guard + should intentionally be overridden. E.g. in C++: + +
+ class_builder<your_class> py_your_class(your_module, "your_class"); + py_your_class.getstate_manages_dict(); ++ + In Python: + +
+ import your_bpl_module + class your_class(your_bpl_module.your_class): + __getstate_manages_dict__ = 1 + def __getstate__(self): + # your code here + def __setstate__(self, state): + # your code here ++
+
+ For simplicity, the __dict__ is not included in the result + of __getstate__. This is not generally recommended, but a + valid approach if it is anticipated that the object's + __dict__ will always be empty. Note that the safety guards + will catch the cases where this assumption is violated. + +
+Updated: March 21, 2001 +
Pointers
+ +In general, raw pointers passed to or returned from functions are problematic +for Boost.Python because pointers have too many potential meanings. Is it an iterator? +A pointer to a single element? An array? When used as a return value, is the +caller expected to manage (delete) the pointed-to object or is the pointer +really just a reference? If the latter, what happens to Python references to the +referent when some C++ code deletes it? +
+There are a few cases in which pointers are converted automatically: +
const char* are interpreted as
+null-terminated 'C' strings and when passed to or returned from C++ functions are
+converted from/to Python strings.
+
+My first piece of advice to anyone with a case not covered above is
+``find a way to avoid the problem.'' For example, if you have just one
+or two functions that return a pointer to an individual const
+T, and T is a wrapped class, you may be able to write a ``thin
+converting wrapper'' over those two functions as follows:
+
+
+const Foo* f(); // original function
+const Foo& f_wrapper() { return *f(); }
+ ...
+my_module.def(f_wrapper, "f");
+
+
+Foo must have a public copy constructor for this technique to work, since Boost.Python
+converts const T& values to_python by copying the T
+value into a new extension instance.
+
+
The first step in handling the remaining cases is to figure out what the pointer +means. Several potential solutions are provided in the examples that follow: + +
If you have lots of functions returning a const T* for some
+wrapped T, you may want to provide an automatic
+to_python conversion function so you don't have to write lots of
+thin wrappers. You can do this simply as follows:
+
+
+BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // this is a gcc 2.95.2 bug workaround
+ PyObject* to_python(const Foo* p) {
+ return to_python(*p); // convert const Foo* in terms of const Foo&
+ }
+BOOST_PYTHON_END_CONVERSION_NAMESPACE
+
+
+If the wrapped type doesn't have a public copy constructor, if copying is +extremely costly (remember, we're dealing with Python here), or if the +pointer is non-const and you really need to be able to modify the referent from +Python, you can use the following dangerous trick. Why dangerous? Because python +can not control the lifetime of the referent, so it may be destroyed by your C++ +code before the last Python reference to it disappears: + +
+BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // this is a gcc 2.95.2 bug workaround
+ PyObject* to_python(Foo* p)
+ {
+ return boost::python::python_extension_class_converters<Foo>::smart_ptr_to_python(p);
+ }
+
+ PyObject* to_python(const Foo* p)
+ {
+ return to_python(const_cast<Foo*>(p));
+ }
+BOOST_PYTHON_END_CONVERSION_NAMESPACE
+
+
+This will cause the Foo* to be treated as though it were an owning smart
+pointer, even though it's not. Be sure you don't use the reference for anything
+from Python once the pointer becomes invalid, though. Don't worry too much about
+the const_cast<> above: Const-correctness is completely lost
+to Python anyway!
+
+If you have an interface that uses non-const pointers (or references) as +in/out parameters to types which in Python are immutable (e.g. int, string), +there simply is no way to get the same interface in Python. You must +resort to transforming your interface with simple thin wrappers as shown below: +
+const void f(int* in_out_x); // original function
+const int f_wrapper(int in_x) { f(in_x); return in_x; }
+ ...
+my_module.def(f_wrapper, "f");
+
+
+Of course, [in/]out parameters commonly occur only when there is already a +return value. You can handle this case by returning a Python tuple: +
+typedef unsigned ErrorCode;
+const char* f(int* in_out_x); // original function
+ ...
+#include <boost/python/objects.hpp>
+const boost::python::tuple f_wrapper(int in_x) {
+ const char* s = f(in_x);
+ return boost::python::tuple(s, in_x);
+}
+ ...
+my_module.def(f_wrapper, "f");
+
+Now, in Python: +
+ ++>>> str,out_x = f(3) +
+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +
+ Updated: Nov 26, 2000 +
+
++For more detailed information, search for "rich comparison" +here. + +
+Boost.Python supports both automatic overloading and manual overloading +of the Rich Comparison operators. The compile-time support is +independent of the Python version that is used when compiling +Boost.Python extension modules. That is, op_lt for example can +always be used, and the C++ operator< will always be bound +to the Python method __lt__. However, the run-time +behavior will depend on the Python version. + +
+With Python versions before 2.1, the Rich Comparison operators will not +be called by Python when any of the six comparison operators +(<, <=, ==, !=, +>, >=) is used in an expression. The only way +to access the corresponding methods is to call them explicitly, e.g. +a.__lt__(b). Only with Python versions 2.1 or higher will +expressions like a < b work as expected. + +
+To support Rich Comparisions, the Python C API was modified between +Python versions 2.0 and 2.1. A new slot was introduced in the +PyTypeObject structure: tp_richcompare. For backwards +compatibility, a flag (Py_TPFLAGS_HAVE_RICHCOMPARE) has to be +set to signal to the Python interpreter that Rich Comparisions are +supported by a particular type. +There is only one flag for all the six comparison operators. +When any of the six operators is wrapped automatically or +manually, Boost.Python will set this flag. Attempts to use comparison +operators at the Python level that are not defined at the C++ level +will then lead to an AttributeError when the Python 2.1 +(or higher) interpreter tries, e.g., a.__lt__(b). That +is, in general all six operators should be supplied. Automatically +wrapped operators and manually wrapped operators can be mixed. For +example:
+ boost::python::class_builder<code> py_code(this_module, "code"); + + py_code.def(boost::python::constructor<>()); + py_code.def(boost::python::constructor<int>()); + py_code.def(boost::python::operators<( boost::python::op_eq + | boost::python::op_ne)>()); + py_code.def(NotImplemented, "__lt__"); + py_code.def(NotImplemented, "__le__"); + py_code.def(NotImplemented, "__gt__"); + py_code.def(NotImplemented, "__ge__"); ++ +NotImplemented is a simple free function that (currently) has +to be provided by the user. For example:
+ boost::python::ref
+ NotImplemented(const code&, const code&) {
+ return
+ boost::python::ref(Py_NotImplemented, boost::python::ref::increment_count);
+ }
+
+
+See also:
+
+
++Updated: July 2001 + +
+ Boost.Python supports all of the standard
+ special method names supported by real Python class instances
+ except __complex__ (more on the reasons below). In addition, it can quickly and easily expose
+ suitable C++ functions and operators as Python operators. The following
+ categories of special method names are supported:
+
+ Python provides a number of special operators for basic customization of a + class. Only a brief description is provided below; more complete + documentation can be found here. + +
__init__ is defined by
+
+ my_class.def(boost::python::constructor<...>())+ + (see section "A Simple Example Using Boost.Python").
+
__del__ is always defined automatically by
+ means of the class' destructor.
+ x(arg1, arg2, ...) is a shorthand for
+x.__call__(arg1, arg2, ...).
+ Foo provides a string
+ conversion function:
+
+std::string to_string(Foo const& f)
+{
+ std::ostringstream s;
+ s << f;
+ return s.str();
+}
+
+ This function would be wrapped like this:
++ Note that Boost.Python also supports automatic wrapping of ++boost::python::class_builder<Foo> foo_class(my_module, "Foo"); +foo_class.def(&to_string, "__str__"); +
__str__ and __cmp__. This is explained in the next section and the Table of
+ Automatically Wrapped Methods.
+
+
+ Numeric operators can be exposed manually, by defing C++
+ [member] functions that support the standard Python numeric
+ protocols. This is the same basic technique used to expose
+ to_string() as __str__() above, and is covered in detail below. Boost.Python also supports
+ automatic wrapping of numeric operators whenever they have already
+ been defined in C++.
+
+
+Supose we wanted to expose a C++ class
+ BigNum which supports addition. That is, in C++ we can write:
+
++BigNum a, b, c; +... +c = a + b; +
+ To enable the same functionality in Python, we first wrap the
+ BigNum class as usual:
+
+ Then we export the addition operator like this: + ++boost::python::class_builder<BigNum> bignum_class(my_module, "BigNum"); +bignum_class.def(boost::python::constructor<>()); +... +
+ + Since BigNum also supports subtraction, multiplication, and division, we + want to export those also. This can be done in a single command by + ``or''ing the operator identifiers together (a complete list of these + identifiers and the corresponding operators can be found in the Table of Automatically Wrapped Methods): ++bignum_class.def(boost::python::operators<boost::python::op_add>()); +
+ [Note that the or-expression must be enclosed in parentheses.] + ++bignum_class.def(boost::python::operators<(boost::python::op_sub | boost::python::op_mul | boost::python::op_div)>()); +
This form of operator definition can be used to wrap unary and + homogeneous binary operators (a homogeneous operator has left and + right operands of the same type). Now suppose that our C++ library also + supports addition of BigNums and plain integers: + +
+ To wrap these heterogeneous operators, we need to specify a different type for + one of the operands. This is done using the+BigNum a, b; +int i; +... +a = b + i; +a = i + b; +
right_operand
+ and left_operand templates:
++ Boost.Python uses overloading to register several variants of the same + operation (more on this in the context of + coercion). Again, several operators can be exported at once: ++bignum_class.def(boost::python::operators<boost::python::op_add>(), boost::python::right_operand<int>()); +bignum_class.def(boost::python::operators<boost::python::op_add>(), boost::python::left_operand<int>()); +
+ The type of the operand not mentioned is taken from the class being wrapped. In + our example, the class object is+bignum_class.def(boost::python::operators<(boost::python::op_sub | boost::python::op_mul | boost::python::op_div)>(), + boost::python::right_operand<int>()); +bignum_class.def(boost::python::operators<(boost::python::op_sub | boost::python::op_mul | boost::python::op_div)>(), + boost::python::left_operand<int>()); +
bignum_class, and thus the
+ other operand's type is ``BigNum const&''. You can override
+ this default by explicitly specifying a type in the
+ operators template:
+++bignum_class.def(boost::python::operators<boost::python::op_add, BigNum>(), boost::python::right_operand<int>()); +
+ Note that automatic wrapping uses the expression
+ ``left + right'' and can be used uniformly
+ regardless of whether the C++ operators are supplied as free functions
+
+
+ + or as member functions + ++BigNum operator+(BigNum, BigNum) +
+ ++BigNum::operator+(BigNum). +
+ For the Python built-in functions pow() and
+ abs(), there is no corresponding C++ operator. Instead,
+ automatic wrapping attempts to wrap C++ functions of the same name. This
+ only works if those functions are known in namespace
+ python. On some compilers (e.g. MSVC) it might be
+ necessary to add a using declaration prior to wrapping:
+
+
+namespace boost { namespace python {
+ using my_namespace::pow;
+ using my_namespace::abs;
+}
+
+
+
+ In some cases, automatic wrapping of operators may be impossible or
+ undesirable. Suppose, for example, that the modulo operation for BigNums
+ is defined by a set of functions called mod():
+
+
+ ++BigNum mod(BigNum const& left, BigNum const& right); +BigNum mod(BigNum const& left, int right); +BigNum mod(int left, BigNum const& right); +
+ For automatic wrapping of the modulo function, operator%() would be needed.
+ Therefore, the mod()-functions must be wrapped manually. That is, we have
+ to export them explicitly with the Python special name "__mod__":
+
+
+ ++bignum_class.def((BigNum (*)(BigNum const&, BigNum const&))&mod, "__mod__"); +bignum_class.def((BigNum (*)(BigNum const&, int))&mod, "__mod__"); +
+ The third form of mod() (with int as left operand) cannot
+ be wrapped directly. We must first create a function rmod() with the
+ operands reversed:
+
+
+BigNum rmod(BigNum const& right, int left)
+{
+ return mod(left, right);
+}
+
+
+ This function must be wrapped under the name "__rmod__" (standing for "reverse mod"):
+
++ + Many of the possible operator names can be found in the Table of Automatically Wrapped Methods. Special treatment is + necessary to export the ternary pow operator. + ++bignum_class.def(&rmod, "__rmod__"); +
+ Automatic and manual wrapping can be mixed arbitrarily. Note that you
+ cannot overload the same operator for a given extension class on both
+ ``int'' and ``float'', because Python implicitly
+ converts these types into each other. Thus, the overloaded variant
+ found first (be it ``int`` or ``float'') will be
+ used for either of the two types.
+
+
+ Boost.Python can also be used to expose inplace numeric operations
+ (i.e., += and so forth). These operators must be wrapped
+ manually, as described in the previous section. For example, suppose
+ the class BigNum has an operator+=:
+
+
+ + This can be exposed by first writing a wrapper function: + ++BigNum& operator+= (BigNum const& right); +
+BigNum& iadd (BigNum& self, const BigNum& right)
+{
+ return self += right;
+}
+
+
+ and then exposing the wrapper with
+
++ + + + ++bignum_class.def(&iadd, "__iadd__"); +
+ Boost.Python solves this problem the same way that C++ does: with overloading. This technique drastically + simplifies the code neccessary to support operators: you just register + operators for all desired type combinations, and Boost.Python automatically + ensures that the correct function is called in each case; there is no + need for user-defined coercion functions. To enable operator + overloading, Boost.Python provides a standard coercion which is implicitly + registered whenever automatic operator wrapping is used. +
+ If you wrap all operator functions manually, but still want to use + operator overloading, you have to register the standard coercion + function explicitly: + +
+ + If you encounter a situation where you absolutely need a customized + coercion, you can still define the "__coerce__" operator manually. The signature + of a coercion function should look like one of the following (the first is + the safest): + ++// this is not necessary if automatic operator wrapping is used +bignum_class.def_standard_coerce(); +
+ + The resulting+boost::python::tuple custom_coerce(boost::python::reference left, boost::python::reference right); +boost::python::tuple custom_coerce(PyObject* left, PyObject* right); +PyObject* custom_coerce(PyObject* left, PyObject* right); +
tuple must contain two elements which
+ represent the values of left and right
+ converted to the same type. Such a function is wrapped as usual:
+
++ + Note that the standard coercion (defined by use of automatic + operator wrapping on a+// this must be called before any use of automatic operator +// wrapping or a call to some_class.def_standard_coerce() +some_class.def(&custom_coerce, "__coerce__"); +
class_builder or a call to
+ class_builder::def_standard_coerce()) will never be applied if
+ a custom coercion function has been registered. Therefore, in
+ your coercion function you should call
+
++ + for all cases that you don't want to handle yourself. + ++boost::python::standard_coerce(left, right); +
pow() Operator
+ In addition to the usual binary pow(x, y) operator (meaning
+ xy), Python also provides a ternary variant that implements
+ xy mod z, presumably using a more efficient algorithm than
+ concatenation of power and modulo operators. Automatic operator wrapping
+ can only be used with the binary variant. Ternary pow() must
+ always be wrapped manually. For a homgeneous ternary pow(),
+ this is done as usual:
+
+
+ + If you want to support this function with non-uniform argument + types, wrapping is a little more involved. Suppose you have to wrap: + ++BigNum power(BigNum const& first, BigNum const& second, BigNum const& modulus); +typedef BigNum (ternary_function1)(const BigNum&, const BigNum&, const BigNum&); +... +bignum_class.def((ternary_function1)&power, "__pow__"); +
+ + The first variant can be wrapped as usual: + ++BigNum power(BigNum const& first, int second, int modulus); +BigNum power(int first, BigNum const& second, int modulus); +BigNum power(int first, int second, BigNum const& modulus); +
+ + In the second variant, however,+typedef BigNum (ternary_function2)(const BigNum&, int, int); +bignum_class.def((ternary_function2)&power, "__pow__"); +
BigNum appears only as second
+ argument, and in the last one it's the third argument. These functions
+ must be presented to Boost.Python such that that the BigNum
+ argument appears in first position:
+
+
+BigNum rpower(BigNum const& second, int first, int modulus)
+{
+ return power(first, second, modulus);
+}
+
+BigNum rrpower(BigNum const& modulus, int first, int second)
+{
+ return power(first, second, modulus);
+}
+
+
+These functions must be wrapped under the names "__rpow__" and "__rrpow__" + respectively: + +
+ +Note that "__rrpow__" is an extension not present in plain Python. + ++bignum_class.def((ternary_function2)&rpower, "__rpow__"); +bignum_class.def((ternary_function2)&rrpower, "__rrpow__"); +
+ Boost.Python can automatically wrap the following + special methods: + +
+
| + Python Operator Name + | + Python Expression + | + C++ Operator Id + |
+ C++ Expression Used For Automatic Wrapping + with cpp_left = from_python(left,
+ type<Left>()),+ cpp_right = from_python(right,
+ type<Right>()),+ and cpp_oper = from_python(oper, type<Oper>())
+ |
+ __add__, __radd__
+ |
+ left + right
+ |
+ op_add
+ |
+ cpp_left + cpp_right
+ |
+ __sub__, __rsub__
+ |
+ left - right
+ |
+ op_sub
+ |
+ cpp_left - cpp_right
+ |
+ __mul__, __rmul__
+ |
+ left * right
+ |
+ op_mul
+ |
+ cpp_left * cpp_right
+ |
+ __div__, __rdiv__
+ |
+ left / right
+ |
+ op_div
+ |
+ cpp_left / cpp_right
+ |
+ __mod__, __rmod__
+ |
+ left % right
+ |
+ op_mod
+ |
+ cpp_left % cpp_right
+ |
+ __divmod__, __rdivmod__
+ |
+ (quotient, remainder)
+ |
+ op_divmod
+ |
+ cpp_left / cpp_right
+ cpp_left % cpp_right
+ |
+ __pow__, __rpow__
+ |
+ pow(left, right)+ (binary power) + |
+ op_pow
+ |
+ pow(cpp_left, cpp_right)
+ |
+ __rrpow__
+ |
+ pow(left, right, modulo)+ (ternary power modulo) + | + no automatic wrapping, special treatment + required + | |
+ __lshift__, __rlshift__
+ |
+ left << right
+ |
+ op_lshift
+ |
+ cpp_left << cpp_right
+ |
+ __rshift__, __rrshift__
+ |
+ left >> right
+ |
+ op_rshift
+ |
+ cpp_left >> cpp_right
+ |
+ __and__, __rand__
+ |
+ left & right
+ |
+ op_and
+ |
+ cpp_left & cpp_right
+ |
+ __xor__, __rxor__
+ |
+ left ^ right
+ |
+ op_xor
+ |
+ cpp_left ^ cpp_right
+ |
+ __or__, __ror__
+ |
+ left | right
+ |
+ op_or
+ |
+ cpp_left | cpp_right
+
+ |
+ __cmp__, __rcmp__
+ |
+ cmp(left, right)+ See Rich Comparisons. + |
+ op_cmp
+ |
+ cpp_left < cpp_right
+ cpp_right < cpp_left
+ |
+ __lt__
+ __le__
+ __eq__
+ __ne__
+ __gt__
+ __ge__
+ |
+ left < right
+ left <= right
+ left == right
+ left != right
+ left > right
+ left >= right
+ See Rich Comparisons + |
+ op_lt
+ op_le
+ op_eq
+ op_ne
+ op_gt
+ op_ge
+ |
+ cpp_left < cpp_right
+ cpp_left <= cpp_right
+ cpp_left == cpp_right
+ cpp_left != cpp_right
+ cpp_left > cpp_right
+ cpp_left >= cpp_right
+
+ |
+ __neg__
+ |
+ -oper (unary negation)
+ |
+ op_neg
+ |
+ -cpp_oper
+ |
+ __pos__
+ |
+ +oper (identity)
+ |
+ op_pos
+ |
+ +cpp_oper
+ |
+ __abs__
+ |
+ abs(oper) (absolute value)
+ |
+ op_abs
+ |
+ abs(cpp_oper)
+ |
+ __invert__
+ |
+ ~oper (bitwise inversion)
+ |
+ op_invert
+ |
+ ~cpp_oper
+ |
+ __int__
+ |
+ int(oper) (integer conversion)
+ |
+ op_int
+ |
+ long(cpp_oper)
+ |
+ __long__
+ |
+ long(oper) + (infinite precision integer conversion) + |
+ op_long
+ |
+ PyLong_FromLong(cpp_oper)
+ |
+ __float__
+ |
+ float(oper) (float conversion)
+ |
+ op_float
+ |
+ double(cpp_oper)
+ |
+ __str__
+ |
+ str(oper) (string conversion)
+ |
+ op_str
+ |
+ std::ostringstream s; s << oper;
+ |
+ __coerce__
+ |
+ coerce(left, right)
+ | + usually defined automatically, otherwise + special treatment required + | |
+ Sequence and mapping operators let wrapped objects behave in accordance + to Python's iteration and access protocols. These protocols differ + considerably from the ones found in C++. For example, Python's typical + iteration idiom looks like + +
+ + while in C++ one writes + ++for i in S: +
+ ++for (iterator i = S.begin(), end = S.end(); i != end; ++i) +
One could try to wrap C++ iterators in order to carry the C++ idiom into + Python. However, this does not work very well because + +
std::vector::iterator) are often implemented as plain C++
+ pointers which are problematic for any automatic
+ wrapping system.
+
+ It is a better idea to support the standard Python
+ sequence and mapping protocols for your wrapped containers. These
+ operators have to be wrapped manually because there are no corresponding
+ C++ operators that could be used for automatic wrapping. The Python
+ documentation lists the relevant
+ container operators. In particular, expose __getitem__, __setitem__
+ and remember to raise the appropriate Python exceptions
+ (PyExc_IndexError for sequences,
+ PyExc_KeyError for mappings) when the requested item is not
+ present.
+
+
+ In the following example, we expose std::map<std::size_t,std::string>:
+
+
+typedef std::map<std::size_t, std::string> StringMap;
+
+// A helper function for dealing with errors. Throw a Python exception
+// if p == m.end().
+void throw_key_error_if_end(
+ const StringMap& m,
+ StringMap::const_iterator p,
+ std::size_t key)
+{
+ if (p == m.end())
+ {
+ PyErr_SetObject(PyExc_KeyError, boost::python::converters::to_python(key));
+ boost::python::throw_error_already_set();
+ }
+}
+
+// Define some simple wrapper functions which match the Python protocol
+// for __getitem__, __setitem__, and __delitem__. Just as in Python, a
+// free function with a ``self'' first parameter makes a fine class method.
+
+const std::string& get_item(const StringMap& self, std::size_t key)
+{
+ const StringMap::const_iterator p = self.find(key);
+ throw_key_error_if_end(self, p, key);
+ return p->second;
+}
+
+// Sets the item corresponding to key in the map.
+void StringMapPythonClass::set_item(StringMap& self, std::size_t key, const std::string& value)
+{
+ self[key] = value;
+}
+
+// Deletes the item corresponding to key from the map.
+void StringMapPythonClass::del_item(StringMap& self, std::size_t key)
+{
+ const StringMap::iterator p = self.find(key);
+ throw_key_error_if_end(self, p, key);
+ self.erase(p);
+}
+
+class_builder<StringMap> string_map(my_module, "StringMap");
+string_map.def(boost::python::constructor<>());
+string_map.def(&StringMap::size, "__len__");
+string_map.def(get_item, "__getitem__");
+string_map.def(set_item, "__setitem__");
+string_map.def(del_item, "__delitem__");
+
+
+ + Then in Python: +
++ ++>>> m = StringMap() +>>> m[1] +Traceback (innermost last): + File "<stdin>", line 1, in ? +KeyError: 1 +>>> m[1] = 'hello' +>>> m[1] +'hello' +>>> del m[1] +>>> m[1] # prove that it's gone +Traceback (innermost last): + File "<stdin>", line 1, in ? +KeyError: 1 +>>> del m[2] +Traceback (innermost last): + File "<stdin>", line 1, in ? +KeyError: 2 +>>> len(m) +0 +>>> m[0] = 'zero' +>>> m[1] = 'one' +>>> m[2] = 'two' +>>> m[3] = 'three' +>>> len(m) +4 ++
+ Just like built-in Python classes, Boost.Python extension classes support special
+ the usual attribute access methods __getattr__,
+ __setattr__, and __delattr__.
+ Because writing these functions can
+ be tedious in the common case where the attributes being accessed are
+ known statically, Boost.Python checks the special names
+
+
__getattr__<name>__
+ __setattr__<name>__
+ __delattr__<name>__
+ +++>>> class Range(AnyBoost.PythonExtensionClass): +... def __init__(self, start, end): +... self.start = start +... self.end = end +... def __getattr__length__(self): +... return self.end - self.start +... +>>> x = Range(3, 9) +>>> x.length +6 ++
+ Boost.Python uses the special
+ __xxxattr__<name>__ functionality described above
+ to allow direct access to data members through the following special
+ functions on class_builder<> and
+ extension_class<>:
+
def_getter(pointer-to-member, name) //
+ read access to the member via attribute name
+ def_setter(pointer-to-member, name) //
+ write access to the member via attribute name
+ def_readonly(pointer-to-member, name)
+ // read-only access to the member via attribute name
+ def_read_write(pointer-to-member,
+ name) // read/write access to the member via attribute
+ name
+
+ Note that the first two functions, used alone, may produce surprising
+ behavior. For example, when def_getter() is used, the
+ default functionality for setattr() and
+ delattr() remains in effect, operating on items in the extension
+ instance's name-space (i.e., its __dict__). For that
+ reason, you'll usually want to stick with def_readonly and
+ def_read_write.
+
+ For example, to expose a std::pair<int,long> we
+ might write:
+
+
+typedef std::pair<int,long> Pil;
+int first(const Pil& x) { return x.first; }
+long second(const Pil& x) { return x.second; }
+ ...
+my_module.def(first, "first");
+my_module.def(second, "second");
+
+class_builder<Pil> pair_int_long(my_module, "Pair");
+pair_int_long.def(boost::python::constructor<>());
+pair_int_long.def(boost::python::constructor<int,long>());
+pair_int_long.def_read_write(&Pil::first, "first");
+pair_int_long.def_read_write(&Pil::second, "second");
+
+
+
+ Now your Python class has attributes first and
+ second which, when accessed, actually modify or reflect the
+ values of corresponding data members of the underlying C++ object. Now
+ in Python:
+
+++>>> x = Pair(3,5) +>>> x.first +3 +>>> x.second +5 +>>> x.second = 8 +>>> x.second +8 +>>> second(x) # Prove that we're not just changing the instance __dict__ +8 ++
__complex__?
+ + That, dear reader, is one problem we don't know how to solve. The + Python source contains the following fragment, indicating the + special-case code really is hardwired: +
+
+/* XXX Hack to support classes with __complex__ method */
+if (PyInstance_Check(r)) { ...
+
+
+ +Next: A Peek Under the Hood +Previous: Inheritance +Up: Top +
+ © Copyright David Abrahams and Ullrich Köthe 2000. + Permission to copy, use, modify, sell and distribute this document is + granted provided this copyright notice appears in all copies. This + document is provided ``as is'' without express or implied + warranty, and with no claim as to its suitability for any purpose. +
+ Updated: Nov 26, 2000 +
+
+ Declaring a class_builder<T> causes the instantiation
+ of an extension_class<T> to which it forwards all
+ member function calls and which is doing most of the real work.
+ extension_class<T> is a subclass of
+ PyTypeObject, the struct which Python's 'C' API uses
+ to describe a type. An instance of the
+ extension_class<> becomes the Python type object
+ corresponding to hello::world. When we add it to the module it goes into the
+ module's dictionary to be looked up under the name "world".
+
+ Boost.Python uses C++'s template argument deduction mechanism to determine the
+ types of arguments to functions (except constructors, for which we must
+ provide an argument list
+ because they can't be named in C++). Then, it calls the appropriate
+ overloaded functions PyObject*
+ to_python(S) and
+ S'from_python(PyObject*,
+ type<S>) which convert between any C++
+ type S and a PyObject*, the type which represents a
+ reference to any Python object in its 'C' API. The extension_class<T>
+ template defines a whole raft of these conversions (for T, T*,
+ T&, std::auto_ptr<T>, etc.), using the same inline
+ friend function technique employed by the boost operators
+ library.
+
+ Because the to_python and from_python functions
+ for a user-defined class are defined by
+ extension_class<T>, it is important that an instantiation of
+ extension_class<T> is visible to any code which wraps
+ a C++ function with a T, T*, const T&, etc. parameter or
+ return value. In particular, you may want to create all of the classes at
+ the top of your module's init function, then def the member
+ functions later to avoid problems with inter-class dependencies.
+
+ Next: Building a Module with Boost.Python + Previous: Special Method and Operator Support + Up: Top +
+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability for + any purpose. +
+ Updated: Nov 26, 2000
+
diff --git a/include/boost/python.hpp b/include/boost/python.hpp
deleted file mode 100644
index ca81cce2..00000000
--- a/include/boost/python.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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