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

- 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 deleted file mode 100644 index 56e96872..00000000 --- a/doc/inheritance.html +++ /dev/null @@ -1,166 +0,0 @@ - -
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 deleted file mode 100644 index 994a78ab..00000000 --- a/doc/pickle.html +++ /dev/null @@ -1,272 +0,0 @@ - - -
-
--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 -