diff --git a/develop/doc/html/HTML.manifest b/develop/doc/html/HTML.manifest new file mode 100644 index 00000000..375d3832 --- /dev/null +++ b/develop/doc/html/HTML.manifest @@ -0,0 +1,30 @@ +index.html +building.html +building/background.html +building/no_install_quickstart.html +building/installing_boost_python_on_your_.html +building/configuring_boost_build.html +building/choosing_a_boost_python_library_.html +building/include_issues.html +building/python_debugging_builds.html +building/testing_boost_python.html +building/notes_for_mingw_and_cygwin_with_.html +configuration.html +support.html +faq.html +faq/i_m_getting_the_attempt_to_retur.html +faq/is_return_internal_reference_eff.html +faq/how_can_i_wrap_functions_which_t.html +faq/fatal_error_c1204_compiler_limit.html +faq/how_do_i_debug_my_python_extensi.html +faq/why_doesn_t_my_operator_work.html +faq/does_boost_python_work_with_mac_.html +faq/how_can_i_find_the_existing_pyob.html +faq/how_can_i_wrap_a_function_which0.html +faq/compilation_takes_too_much_time_.html +faq/how_do_i_create_sub_packages_usi.html +faq/error_c2064_term_does_not_evalua.html +faq/how_can_i_automatically_convert_.html +faq/why_is_my_automatic_to_python_co.html +faq/is_boost_python_thread_aware_com.html +glossary.html diff --git a/develop/doc/html/article.html b/develop/doc/html/article.html new file mode 100644 index 00000000..6a9126dc --- /dev/null +++ b/develop/doc/html/article.html @@ -0,0 +1,929 @@ + + + +
+ + +| Author: | +David Abrahams |
|---|---|
| Contact: | +dave@boost-consulting.com |
| Organization: | +Boost Consulting |
| Date: | +2003-05-14 |
| Author: | +Ralf W. Grosse-Kunstleve |
| Copyright: | +Copyright David Abrahams and Ralf W. Grosse-Kunstleve 2003. All rights reserved |
Table of Contents
+ +Boost.Python is an open source C++ library which provides a concise +IDL-like interface for binding C++ classes and functions to +Python. Leveraging the full power of C++ compile-time introspection +and of recently developed metaprogramming techniques, this is achieved +entirely in pure C++, without introducing a new syntax. +Boost.Python's rich set of features and high-level interface make it +possible to engineer packages from the ground up as hybrid systems, +giving programmers easy and coherent access to both the efficient +compile-time polymorphism of C++ and the extremely convenient run-time +polymorphism of Python.
+Python and C++ are in many ways as different as two languages could +be: while C++ is usually compiled to machine-code, Python is +interpreted. Python's dynamic type system is often cited as the +foundation of its flexibility, while in C++ static typing is the +cornerstone of its efficiency. C++ has an intricate and difficult +compile-time meta-language, while in Python, practically everything +happens at runtime.
+Yet for many programmers, these very differences mean that Python and +C++ complement one another perfectly. Performance bottlenecks in +Python programs can be rewritten in C++ for maximal speed, and +authors of powerful C++ libraries choose Python as a middleware +language for its flexible system integration capabilities. +Furthermore, the surface differences mask some strong similarities:
+Given Python's rich 'C' interoperability API, it should in principle +be possible to expose C++ type and function interfaces to Python with +an analogous interface to their C++ counterparts. However, the +facilities provided by Python alone for integration with C++ are +relatively meager. Compared to C++ and Python, 'C' has only very +rudimentary abstraction facilities, and support for exception-handling +is completely missing. 'C' extension module writers are required to +manually manage Python reference counts, which is both annoyingly +tedious and extremely error-prone. Traditional extension modules also +tend to contain a great deal of boilerplate code repetition which +makes them difficult to maintain, especially when wrapping an evolving +API.
+These limitations have lead to the development of a variety of wrapping +systems. SWIG is probably the most popular package for the +integration of C/C++ and Python. A more recent development is SIP, +which was specifically designed for interfacing Python with the Qt +graphical user interface library. Both SWIG and SIP introduce their +own specialized languages for customizing inter-language bindings. +This has certain advantages, but having to deal with three different +languages (Python, C/C++ and the interface language) also introduces +practical and mental difficulties. The CXX package demonstrates an +interesting alternative. It shows that at least some parts of +Python's 'C' API can be wrapped and presented through a much more +user-friendly C++ interface. However, unlike SWIG and SIP, CXX does +not include support for wrapping C++ classes as new Python types.
+The features and goals of Boost.Python overlap significantly with +many of these other systems. That said, Boost.Python attempts to +maximize convenience and flexibility without introducing a separate +wrapping language. Instead, it presents the user with a high-level +C++ interface for wrapping C++ classes and functions, managing much of +the complexity behind-the-scenes with static metaprogramming. +Boost.Python also goes beyond the scope of earlier systems by +providing:
+The key insight that sparked the development of Boost.Python is that +much of the boilerplate code in traditional extension modules could be +eliminated using C++ compile-time introspection. Each argument of a +wrapped C++ function must be extracted from a Python object using a +procedure that depends on the argument type. Similarly the function's +return type determines how the return value will be converted from C++ +to Python. Of course argument and return types are part of each +function's type, and this is exactly the source from which +Boost.Python deduces most of the information required.
+This approach leads to user guided wrapping: as much information is +extracted directly from the source code to be wrapped as is possible +within the framework of pure C++, and some additional information is +supplied explicitly by the user. Mostly the guidance is mechanical +and little real intervention is required. Because the interface +specification is written in the same full-featured language as the +code being exposed, the user has unprecedented power available when +she does need to take control.
+The primary goal of Boost.Python is to allow users to expose C++ +classes and functions to Python using nothing more than a C++ +compiler. In broad strokes, the user experience should be one of +directly manipulating C++ objects from Python.
+However, it's also important not to translate all interfaces too +literally: the idioms of each language must be respected. For +example, though C++ and Python both have an iterator concept, they are +expressed very differently. Boost.Python has to be able to bridge the +interface gap.
+It must be possible to insulate Python users from crashes resulting +from trivial misuses of C++ interfaces, such as accessing +already-deleted objects. By the same token the library should +insulate C++ users from low-level Python 'C' API, replacing +error-prone 'C' interfaces like manual reference-count management and +raw PyObject pointers with more-robust alternatives.
+Support for component-based development is crucial, so that C++ types +exposed in one extension module can be passed to functions exposed in +another without loss of crucial information like C++ inheritance +relationships.
+Finally, all wrapping must be non-intrusive, without modifying or +even seeing the original C++ source code. Existing C++ libraries have +to be wrappable by third parties who only have access to header files +and binaries.
+And now for a preview of Boost.Python, and how it improves on the raw +facilities offered by Python. Here's a function we might want to +expose:
+
+char const* greet(unsigned x)
+{
+ static char const* const msgs[] = { "hello", "Boost.Python", "world!" };
+
+ if (x > 2)
+ throw std::range_error("greet: index out of range");
+
+ return msgs[x];
+}
+
+To wrap this function in standard C++ using the Python 'C' API, we'd +need something like this:
+
+extern "C" // all Python interactions use 'C' linkage and calling convention
+{
+ // Wrapper to handle argument/result conversion and checking
+ PyObject* greet_wrap(PyObject* args, PyObject * keywords)
+ {
+ int x;
+ if (PyArg_ParseTuple(args, "i", &x)) // extract/check arguments
+ {
+ char const* result = greet(x); // invoke wrapped function
+ return PyString_FromString(result); // convert result to Python
+ }
+ return 0; // error occurred
+ }
+
+ // Table of wrapped functions to be exposed by the module
+ static PyMethodDef methods[] = {
+ { "greet", greet_wrap, METH_VARARGS, "return one of 3 parts of a greeting" }
+ , { NULL, NULL, 0, NULL } // sentinel
+ };
+
+ // module initialization function
+ DL_EXPORT init_hello()
+ {
+ (void) Py_InitModule("hello", methods); // add the methods to the module
+ }
+}
+
+Now here's the wrapping code we'd use to expose it with Boost.Python:
+
+#include <boost/python.hpp>
+using namespace boost::python;
+BOOST_PYTHON_MODULE(hello)
+{
+ def("greet", greet, "return one of 3 parts of a greeting");
+}
+
+and here it is in action:
++>>> import hello +>>> for x in range(3): +... print hello.greet(x) +... +hello +Boost.Python +world! ++
Aside from the fact that the 'C' API version is much more verbose, +it's worth noting a few things that it doesn't handle correctly:
+This section outlines some of the library's major features. Except as +neccessary to avoid confusion, details of library implementation are +omitted.
+C++ classes and structs are exposed with a similarly-terse interface. +Given:
+
+struct World
+{
+ void set(std::string msg) { this->msg = msg; }
+ std::string greet() { return msg; }
+ std::string msg;
+};
+
+The following code will expose it in our extension module:
+
+#include <boost/python.hpp>
+BOOST_PYTHON_MODULE(hello)
+{
+ class_<World>("World")
+ .def("greet", &World::greet)
+ .def("set", &World::set)
+ ;
+}
+
+Although this code has a certain pythonic familiarity, people +sometimes find the syntax bit confusing because it doesn't look like +most of the C++ code they're used to. All the same, this is just +standard C++. Because of their flexible syntax and operator +overloading, C++ and Python are great for defining domain-specific +(sub)languages +(DSLs), and that's what we've done in Boost.Python. To break it down:
+
+class_<World>("World")
+
+constructs an unnamed object of type class_<World> and passes +"World" to its constructor. This creates a new-style Python class +called World in the extension module, and associates it with the +C++ type World in the Boost.Python type conversion registry. We +might have also written:
+
+class_<World> w("World");
+
+but that would've been more verbose, since we'd have to name w +again to invoke its def() member function:
+
+w.def("greet", &World::greet)
+
+There's nothing special about the location of the dot for member +access in the original example: C++ allows any amount of whitespace on +either side of a token, and placing the dot at the beginning of each +line allows us to chain as many successive calls to member functions +as we like with a uniform syntax. The other key fact that allows +chaining is that class_<> member functions all return a reference +to *this.
+So the example is equivalent to:
+
+class_<World> w("World");
+w.def("greet", &World::greet);
+w.def("set", &World::set);
+
+It's occasionally useful to be able to break down the components of a +Boost.Python class wrapper in this way, but the rest of this article +will stick to the terse syntax.
+For completeness, here's the wrapped class in use:
+
+>>> import hello
+>>> planet = hello.World()
+>>> planet.set('howdy')
+>>> planet.greet()
+'howdy'
+
+Since our World class is just a plain struct, it has an +implicit no-argument (nullary) constructor. Boost.Python exposes the +nullary constructor by default, which is why we were able to write:
++>>> planet = hello.World() ++
However, well-designed classes in any language may require constructor +arguments in order to establish their invariants. Unlike Python, +where __init__ is just a specially-named method, In C++ +constructors cannot be handled like ordinary member functions. In +particular, we can't take their address: &World::World is an +error. The library provides a different interface for specifying +constructors. Given:
+
+struct World
+{
+ World(std::string msg); // added constructor
+ ...
+
+we can modify our wrapping code as follows:
+
+class_<World>("World", init<std::string>())
+ ...
+
+of course, a C++ class may have additional constructors, and we can +expose those as well by passing more instances of init<...> to +def():
+
+class_<World>("World", init<std::string>())
+ .def(init<double, double>())
+ ...
+
+Boost.Python allows wrapped functions, member functions, and +constructors to be overloaded to mirror C++ overloading.
+Any publicly-accessible data members in a C++ class can be easily +exposed as either readonly or readwrite attributes:
+
+class_<World>("World", init<std::string>())
+ .def_readonly("msg", &World::msg)
+ ...
+
+and can be used directly in Python:
+
+>>> planet = hello.World('howdy')
+>>> planet.msg
+'howdy'
+
+This does not result in adding attributes to the World instance +__dict__, which can result in substantial memory savings when +wrapping large data structures. In fact, no instance __dict__ +will be created at all unless attributes are explicitly added from +Python. Boost.Python owes this capability to the new Python 2.2 type +system, in particular the descriptor interface and property type.
+In C++, publicly-accessible data members are considered a sign of poor +design because they break encapsulation, and style guides usually +dictate the use of "getter" and "setter" functions instead. In +Python, however, __getattr__, __setattr__, and since 2.2, +property mean that attribute access is just one more +well-encapsulated syntactic tool at the programmer's disposal. +Boost.Python bridges this idiomatic gap by making Python property +creation directly available to users. If msg were private, we +could still expose it as attribute in Python as follows:
+
+class_<World>("World", init<std::string>())
+ .add_property("msg", &World::greet, &World::set)
+ ...
+
+The example above mirrors the familiar usage of properties in Python +2.2+:
++>>> class World(object): +... __init__(self, msg): +... self.__msg = msg +... def greet(self): +... return self.__msg +... def set(self, msg): +... self.__msg = msg +... msg = property(greet, set) ++
The ability to write arithmetic operators for user-defined types has +been a major factor in the success of both languages for numerical +computation, and the success of packages like NumPy attests to the +power of exposing operators in extension modules. Boost.Python +provides a concise mechanism for wrapping operator overloads. The +example below shows a fragment from a wrapper for the Boost rational +number library:
+
+class_<rational<int> >("rational_int")
+ .def(init<int, int>()) // constructor, e.g. rational_int(3,4)
+ .def("numerator", &rational<int>::numerator)
+ .def("denominator", &rational<int>::denominator)
+ .def(-self) // __neg__ (unary minus)
+ .def(self + self) // __add__ (homogeneous)
+ .def(self * self) // __mul__
+ .def(self + int()) // __add__ (heterogenous)
+ .def(int() + self) // __radd__
+ ...
+
+The magic is performed using a simplified application of "expression +templates" [VELD1995], a technique originally developed for +optimization of high-performance matrix algebra expressions. The +essence is that instead of performing the computation immediately, +operators are overloaded to construct a type representing the +computation. In matrix algebra, dramatic optimizations are often +available when the structure of an entire expression can be taken into +account, rather than evaluating each operation "greedily". +Boost.Python uses the same technique to build an appropriate Python +method object based on expressions involving self.
+C++ inheritance relationships can be represented to Boost.Python by adding +an optional bases<...> argument to the class_<...> template +parameter list as follows:
+
+class_<Derived, bases<Base1,Base2> >("Derived")
+ ...
+
+This has two effects:
+Of course it's possible to derive new Python classes from wrapped C++ +class instances. Because Boost.Python uses the new-style class +system, that works very much as for the Python built-in types. There +is one significant detail in which it differs: the built-in types +generally establish their invariants in their __new__ function, so +that derived classes do not need to call __init__ on the base +class before invoking its methods :
++>>> class L(list): +... def __init__(self): +... pass +... +>>> L().reverse() +>>> ++
Because C++ object construction is a one-step operation, C++ instance +data cannot be constructed until the arguments are available, in the +__init__ function:
++>>> class D(SomeBoostPythonClass): +... def __init__(self): +... pass +... +>>> D().some_boost_python_method() +Traceback (most recent call last): + File "<stdin>", line 1, in ? +TypeError: bad argument type for built-in operation ++
This happened because Boost.Python couldn't find instance data of type +SomeBoostPythonClass within the D instance; D's __init__ +function masked construction of the base class. It could be corrected +by either removing D's __init__ function or having it call +SomeBoostPythonClass.__init__(...) explicitly.
+Deriving new types in Python from extension classes is not very +interesting unless they can be used polymorphically from C++. In +other words, Python method implementations should appear to override +the implementation of C++ virtual functions when called through base +class pointers/references from C++. Since the only way to alter the +behavior of a virtual function is to override it in a derived class, +the user must build a special derived class to dispatch a polymorphic +class' virtual functions:
+
+//
+// interface to wrap:
+//
+class Base
+{
+ public:
+ virtual int f(std::string x) { return 42; }
+ virtual ~Base();
+};
+
+int calls_f(Base const& b, std::string x) { return b.f(x); }
+
+//
+// Wrapping Code
+//
+
+// Dispatcher class
+struct BaseWrap : Base
+{
+ // Store a pointer to the Python object
+ BaseWrap(PyObject* self_) : self(self_) {}
+ PyObject* self;
+
+ // Default implementation, for when f is not overridden
+ int f_default(std::string x) { return this->Base::f(x); }
+ // Dispatch implementation
+ int f(std::string x) { return call_method<int>(self, "f", x); }
+};
+
+...
+ def("calls_f", calls_f);
+ class_<Base, BaseWrap>("Base")
+ .def("f", &Base::f, &BaseWrap::f_default)
+ ;
+
+Now here's some Python code which demonstrates:
++>>> class Derived(Base): +... def f(self, s): +... return len(s) +... +>>> calls_f(Base(), 'foo') +42 +>>> calls_f(Derived(), 'forty-two') +9 ++
Things to notice about the dispatcher class:
+Admittedly, this formula is tedious to repeat, especially on a project +with many polymorphic classes. That it is neccessary reflects some +limitations in C++'s compile-time introspection capabilities: there's +no way to enumerate the members of a class and find out which are +virtual functions. At least one very promising project has been +started to write a front-end which can generate these dispatchers (and +other wrapping code) automatically from C++ headers.
+Pyste is being developed by Bruno da Silva de Oliveira. It builds on +GCC_XML, which generates an XML version of GCC's internal program +representation. Since GCC is a highly-conformant C++ compiler, this +ensures correct handling of the most-sophisticated template code and +full access to the underlying type system. In keeping with the +Boost.Python philosophy, a Pyste interface description is neither +intrusive on the code being wrapped, nor expressed in some unfamiliar +language: instead it is a 100% pure Python script. If Pyste is +successful it will mark a move away from wrapping everything directly +in C++ for many of our users. It will also allow us the choice to +shift some of the metaprogram code from C++ to Python. We expect that +soon, not only our users but the Boost.Python developers themselves +will be "thinking hybrid" about their own code.
+Serialization is the process of converting objects in memory to a +form that can be stored on disk or sent over a network connection. The +serialized object (most often a plain string) can be retrieved and +converted back to the original object. A good serialization system will +automatically convert entire object hierarchies. Python's standard +pickle module is just such a system. It leverages the language's strong +runtime introspection facilities for serializing practically arbitrary +user-defined objects. With a few simple and unintrusive provisions this +powerful machinery can be extended to also work for wrapped C++ objects. +Here is an example:
+
+#include <string>
+
+struct World
+{
+ World(std::string a_msg) : msg(a_msg) {}
+ std::string greet() const { return msg; }
+ std::string msg;
+};
+
+#include <boost/python.hpp>
+using namespace boost::python;
+
+struct World_picklers : pickle_suite
+{
+ static tuple
+ getinitargs(World const& w) { return make_tuple(w.greet()); }
+};
+
+BOOST_PYTHON_MODULE(hello)
+{
+ class_<World>("World", init<std::string>())
+ .def("greet", &World::greet)
+ .def_pickle(World_picklers())
+ ;
+}
+
+Now let's create a World object and put it to rest on disk:
+
+>>> import hello
+>>> import pickle
+>>> a_world = hello.World("howdy")
+>>> pickle.dump(a_world, open("my_world", "w"))
+
+In a potentially different script on a potentially different +computer with a potentially different operating system:
+
+>>> import pickle
+>>> resurrected_world = pickle.load(open("my_world", "r"))
+>>> resurrected_world.greet()
+'howdy'
+
+Of course the cPickle module can also be used for faster +processing.
+Boost.Python's pickle_suite fully supports the pickle protocol +defined in the standard Python documentation. Like a __getinitargs__ +function in Python, the pickle_suite's getinitargs() is responsible for +creating the argument tuple that will be use to reconstruct the pickled +object. The other elements of the Python pickling protocol, +__getstate__ and __setstate__ can be optionally provided via C++ +getstate and setstate functions. C++'s static type system allows the +library to ensure at compile-time that nonsensical combinations of +functions (e.g. getstate without setstate) are not used.
+Enabling serialization of more complex C++ objects requires a little +more work than is shown in the example above. Fortunately the +object interface (see next section) greatly helps in keeping the +code manageable.
+Experienced 'C' language extension module authors will be familiar +with the ubiquitous PyObject*, manual reference-counting, and the +need to remember which API calls return "new" (owned) references or +"borrowed" (raw) references. These constraints are not just +cumbersome but also a major source of errors, especially in the +presence of exceptions.
+Boost.Python provides a class object which automates reference +counting and provides conversion to Python from C++ objects of +arbitrary type. This significantly reduces the learning effort for +prospective extension module writers.
+Creating an object from any other type is extremely simple:
+
+object s("hello, world"); // s manages a Python string
+
+object has templated interactions with all other types, with +automatic to-python conversions. It happens so naturally that it's +easily overlooked:
++object ten_Os = 10 * s[4]; // -> "oooooooooo" ++
In the example above, 4 and 10 are converted to Python objects +before the indexing and multiplication operations are invoked.
+The extract<T> class template can be used to convert Python objects +to C++ types:
++double x = extract<double>(o); ++
If a conversion in either direction cannot be performed, an +appropriate exception is thrown at runtime.
+The object type is accompanied by a set of derived types +that mirror the Python built-in types such as list, dict, +tuple, etc. as much as possible. This enables convenient +manipulation of these high-level types from C++:
++dict d; +d["some"] = "thing"; +d["lucky_number"] = 13; +list l = d.keys(); ++
This almost looks and works like regular Python code, but it is pure +C++. Of course we can wrap C++ functions which accept or return +object instances.
+Because of the practical and mental difficulties of combining +programming languages, it is common to settle a single language at the +outset of any development effort. For many applications, performance +considerations dictate the use of a compiled language for the core +algorithms. Unfortunately, due to the complexity of the static type +system, the price we pay for runtime performance is often a +significant increase in development time. Experience shows that +writing maintainable C++ code usually takes longer and requires far +more hard-earned working experience than developing comparable Python +code. Even when developers are comfortable working exclusively in +compiled languages, they often augment their systems by some type of +ad hoc scripting layer for the benefit of their users without ever +availing themselves of the same advantages.
+Boost.Python enables us to think hybrid. Python can be used for +rapidly prototyping a new application; its ease of use and the large +pool of standard libraries give us a head start on the way to a +working system. If necessary, the working code can be used to +discover rate-limiting hotspots. To maximize performance these can +be reimplemented in C++, together with the Boost.Python bindings +needed to tie them back into the existing higher-level procedure.
+Of course, this top-down approach is less attractive if it is clear +from the start that many algorithms will eventually have to be +implemented in C++. Fortunately Boost.Python also enables us to +pursue a bottom-up approach. We have used this approach very +successfully in the development of a toolbox for scientific +applications. The toolbox started out mainly as a library of C++ +classes with Boost.Python bindings, and for a while the growth was +mainly concentrated on the C++ parts. However, as the toolbox is +becoming more complete, more and more newly added functionality can be +implemented in Python.
+
+This figure shows the estimated ratio of newly added C++ and Python +code over time as new algorithms are implemented. We expect this +ratio to level out near 70% Python. Being able to solve new problems +mostly in Python rather than a more difficult statically typed +language is the return on our investment in Boost.Python. The ability +to access all of our code from Python allows a broader group of +developers to use it in the rapid development of new applications.
+The first version of Boost.Python was developed in 2000 by Dave +Abrahams at Dragon Systems, where he was privileged to have Tim Peters +as a guide to "The Zen of Python". One of Dave's jobs was to develop +a Python-based natural language processing system. Since it was +eventually going to be targeting embedded hardware, it was always +assumed that the compute-intensive core would be rewritten in C++ to +optimize speed and memory footprint1. The project also wanted to +test all of its C++ code using Python test scripts2. The only +tool we knew of for binding C++ and Python was SWIG, and at the time +its handling of C++ was weak. It would be false to claim any deep +insight into the possible advantages of Boost.Python's approach at +this point. Dave's interest and expertise in fancy C++ template +tricks had just reached the point where he could do some real damage, +and Boost.Python emerged as it did because it filled a need and +because it seemed like a cool thing to try.
+This early version was aimed at many of the same basic goals we've +described in this paper, differing most-noticeably by having a +slightly more cumbersome syntax and by lack of special support for +operator overloading, pickling, and component-based development. +These last three features were quickly added by Ullrich Koethe and +Ralf Grosse-Kunstleve3, and other enthusiastic contributors arrived +on the scene to contribute enhancements like support for nested +modules and static member functions.
+By early 2001 development had stabilized and few new features were +being added, however a disturbing new fact came to light: Ralf had +begun testing Boost.Python on pre-release versions of a compiler using +the EDG front-end, and the mechanism at the core of Boost.Python +responsible for handling conversions between Python and C++ types was +failing to compile. As it turned out, we had been exploiting a very +common bug in the implementation of all the C++ compilers we had +tested. We knew that as C++ compilers rapidly became more +standards-compliant, the library would begin failing on more +platforms. Unfortunately, because the mechanism was so central to the +functioning of the library, fixing the problem looked very difficult.
+Fortunately, later that year Lawrence Berkeley and later Lawrence +Livermore National labs contracted with Boost Consulting for support +and development of Boost.Python, and there was a new opportunity to +address fundamental issues and ensure a future for the library. A +redesign effort began with the low level type conversion architecture, +building in standards-compliance and support for component-based +development (in contrast to version 1 where conversions had to be +explicitly imported and exported across module boundaries). A new +analysis of the relationship between the Python and C++ objects was +done, resulting in more intuitive handling for C++ lvalues and +rvalues.
+The emergence of a powerful new type system in Python 2.2 made the +choice of whether to maintain compatibility with Python 1.5.2 easy: +the opportunity to throw away a great deal of elaborate code for +emulating classic Python classes alone was too good to pass up. In +addition, Python iterators and descriptors provided crucial and +elegant tools for representing similar C++ constructs. The +development of the generalized object interface allowed us to +further shield C++ programmers from the dangers and syntactic burdens +of the Python 'C' API. A great number of other features including C++ +exception translation, improved support for overloaded functions, and +most significantly, CallPolicies for handling pointers and +references, were added during this period.
+In October 2002, version 2 of Boost.Python was released. Development +since then has concentrated on improved support for C++ runtime +polymorphism and smart pointers. Peter Dimov's ingenious +boost::shared_ptr design in particular has allowed us to give the +hybrid developer a consistent interface for moving objects back and +forth across the language barrier without loss of information. At +first, we were concerned that the sophistication and complexity of the +Boost.Python v2 implementation might discourage contributors, but the +emergence of Pyste and several other significant feature +contributions have laid those fears to rest. Daily questions on the +Python C++-sig and a backlog of desired improvements show that the +library is getting used. To us, the future looks bright.
+Boost.Python achieves seamless interoperability between two rich and +complimentary language environments. Because it leverages template +metaprogramming to introspect about types and functions, the user +never has to learn a third syntax: the interface definitions are +written in concise and maintainable C++. Also, the wrapping system +doesn't have to parse C++ headers or represent the type system: the +compiler does that work for us.
+Computationally intensive tasks play to the strengths of C++ and are +often impossible to implement efficiently in pure Python, while jobs +like serialization that are trivial in Python can be very difficult in +pure C++. Given the luxury of building a hybrid software system from +the ground up, we can approach design with new confidence and power.
+| [VELD1995] | T. Veldhuizen, "Expression Templates," C++ Report, +Vol. 7 No. 5 June 1995, pp. 26-31. +http://osl.iu.edu/~tveldhui/papers/Expression-Templates/exprtmpl.html |
| [1] | In retrospect, it seems that "thinking hybrid" from the +ground up might have been better for the NLP system: the +natural component boundaries defined by the pure python +prototype turned out to be inappropriate for getting the +desired performance and memory footprint out of the C++ core, +which eventually caused some redesign overhead on the Python +side when the core was moved to C++. |
| [2] | We also have some reservations about driving all C++ +testing through a Python interface, unless that's the only way +it will be ultimately used. Any transition across language +boundaries with such different object models can inevitably +mask bugs. |
| [3] | These features were expressed very differently in v1 of +Boost.Python |
![]() |
+ Boost.Python requires Python 2.2 + [1] or newer. +
+[1] + Note that although we tested earlier versions of Boost.Python with Python + 2.2, and we don't think we've done anything + to break compatibility, this release of Boost.Python may not have been + tested with versions of Python earlier than 2.4, so we're not 100% sure + that python 2.2 and 2.3 are supported. +
| + | + |
![]() |
+ There are two basic models for combining C++ and Python: +
+
+ The key distinction between extending and embedding is the location of the
+ C++ main()
+ function: in the Python interpreter executable, or in some other program,
+ respectively. Note that even when embedding Python in another program, extension
+ modules are often the best way to make C/C++ functionality accessible to
+ Python code, so the use of extension modules is really at the heart
+ of both models.
+
+ Except in rare cases, extension modules are built as dynamically-loaded libraries
+ with a single entry point, which means you can change them without rebuilding
+ either the other extension modules or the executable containing main().
+
| + | + |
![]() |
+ If—instead of letting Boost.Build construct and link with the right libraries + automatically—you choose to use a pre-built Boost.Python library, you'll + need to think about which one to link with. The Boost.Python binary comes + in both static and dynamic flavors. Take care to choose the right flavor + for your application. [3] +
++ The dynamic library is the safest and most-versatile choice: +
++ It might be appropriate to use the static Boost.Python library in any of + the following cases: +
+[3] + Information about how to identify the static and dynamic builds of Boost.Python + on Windows + / Unix + variants +
[4] + Because of the way most *nix platforms share symbols among dynamically-loaded + objects, I'm not certain that extension modules built with different + compiler toolsets will always use different copies of the Boost.Python + library when loaded into the same Python instance. Not using different + libraries could be a good thing if the compilers have compatible + ABIs, because extension modules built with the two libraries would + be interoperable. Otherwise, it could spell disaster, since an extension + module and the Boost.Python library would have different ideas of + such things as class layout. I would appreciate someone doing the + experiment to find out what happens. +
| + | + |
![]() |
+ As described in the Boost.Build
+ Reference Manual, a file called user-config.jam in
+ your home directory is used to specify the tools and libraries available
+ to the build system. You may need to create or edit user-config.jam to
+ tell Boost.Build how to invoke Python, #include
+ its headers, and link with its libraries.
+
![]() |
+Note | +
|---|---|
+ If you are using a unix-variant OS and you ran Boost's |
+ If you have one fairly “standard” python installation for your platform,
+ you might not need to do anything special to describe it. If you haven't
+ configured python in user-config.jam (and
+ you don't specify --without-python
+ on the Boost.Build command line), Boost.Build will automatically execute
+ the equivalent of
+
import toolset : using ; +using python ; ++
+ which automatically looks for Python in the most likely places. However,
+ that only happens when using the Boost.Python project file (e.g. when referred
+ to by another project as in the quickstart method). If instead you are linking
+ against separately-compiled Boost.Python binaries, you should set up a user-config.jam file
+ with at least the minimal incantation above.
+
+ If you have several versions of Python installed, or Python is installed
+ in an unusual way, you may want to supply any or all of the following optional
+ parameters to using python.
+
+
+ the version of Python to use. Should be in Major.Minor format, for
+ example, 2.3. Do not
+ include the subminor version (i.e. not
+ 2.5.1). If you have multiple Python versions
+ installed, the version will usually be the only configuration argument
+ required.
+
+ preferably, a command that invokes a Python interpreter. Alternatively, + the installation prefix for Python libraries and header files. Only + use the alternative formulation if there is no appropriate Python + executable available. +
+ the #include paths
+ for Python headers. Normally the correct path(s) will be automatically
+ deduced from version
+ and/or cmd-or-prefix.
+
+ the path to Python library binaries. On MacOS/Darwin, you can also
+ pass the path of the Python framework. Normally the correct path(s)
+ will be automatically deduced from version
+ and/or cmd-or-prefix.
+
+ if specified, should be a set of Boost.Build properties that are + matched against the build configuration when Boost.Build selects + a Python configuration to use. See examples below for details. +
+ A string to append to the name of extension modules before the true + filename extension. You almost certainly don't need to use this. + Usually this suffix is only used when targeting a Windows debug build + of Python, and will be set automatically for you based on the value + of the <python-debugging> + feature. However, at least one Linux distribution (Ubuntu Feisty + Fawn) has a specially configured <python-dbg> + package that claims to use such a suffix. +
+ Note that in the examples below, case and especially + whitespace are significant. +
+user-config.jam might contain
+using python : 2.5 ; # Make both versions of Python available +using python : 2.4 ; # To build with python 2.4, add python=2.4 + # to your command line. ++
+ The first version configured (2.5) becomes the default. To build
+ against python 2.4, add python=2.4
+ to the bjam command
+ line.
+
cmd-or-prefix
+ parameter:
+using python : : /usr/local/python-2.6-beta/bin/python ; ++
condition
+ parameter:
+using python ; # use for most toolsets + +# Use with Intel C++ toolset +using python + : # version + : c:\\Devel\\Python-2.5-IntelBuild\\PCBuild\\python # cmd-or-prefix + : # includes + : # libraries + : <toolset>intel # condition + ; ++
using python : 2.5 : C:\\src\\Python-2.5\\PCBuild\\python ; +using python : 2.5 : C:\\src\\Python-2.5\\PCBuild\\python_d + : # includes + : # libs + : <python-debugging>on ; ++
<target-os>cygwin
+ in the condition parameter
+ for the cygwin python installation:
+# windows installation +using python ; + +# cygwin installation +using python : : c:\\cygwin\\bin\\python2.5 : : : <target-os>cygwin ; ++
+ when you put target-os=cygwin in your build request, it should build + with the cygwin version of python: _ +
+bjam target-os=cygwin toolset=gcc ++
+ This is supposed to work the other way, too (targeting windows python + with a Cygwin bjam) but it + seems as though the support in Boost.Build's toolsets for building + that way is broken at the time of this writing. +
+using python : 2.5 ; # a regular windows build +using python : 2.4 : : : : <target-os>cygwin ; ++
+ building with +
+bjam target-os=cygwin ++
+ will yield an error. Instead, you'll need to write +
+bjam target-os=cygwin/python=2.4 ++
[2]
+ configure overwrites
+ the existing user-config.jam in your home directory (if any)
+ after making a backup of the old version.
+
| + | + |
![]() |
+ 1. If you should ever have occasion to #include
+ "python.h" directly in a
+ translation unit of a program using Boost.Python, use #include
+ "boost/python/detail/wrap_python.hpp"
+ instead. It handles several issues necessary for use with Boost.Python, one
+ of which is mentioned in the next section.
+
+ 2. Be sure not to #include
+ any system headers before wrap_python.hpp. This
+ restriction is actually imposed by Python, or more properly, by Python's
+ interaction with your operating system. See http://docs.python.org/ext/simpleExample.html
+ for details.
+
| + | + |
![]() |
+ Since Boost.Python is a separately-compiled (as opposed to header-only) library, its user relies on the services
+ of a Boost.Python library binary.
+
+ If you need a regular installation of the Boost.Python library binaries on
+ your system, the Boost Getting
+ Started Guide will walk you through the steps of creating one. If
+ building binaries from source, you might want to supply the --with-python
+ argument to bjam (or the
+ --with-libraries=python
+ argument to configure), so
+ only the Boost.Python binary will be built, rather than all the Boost binaries.
+
| + | + |
![]() |
+ There is no need to “install Boost” in order to get started using Boost.Python. + These instructions use Boost.Build + projects, which will build those binaries as soon as they're needed. Your + first tests may take a little longer while you wait for Boost.Python to build, + but doing things this way will save you from worrying about build intricacies + like which library binaries to use for a specific compiler configuration + and figuring out the right compiler options to use yourself. +
+![]() |
+Note | +
|---|---|
|
+ + Of course it's possible to use other build systems to build Boost.Python + and its extensions, but they are not officially supported by Boost. Moreover + 99% of all “I can't build Boost.Python” problems + come from trying to use another build system without first following + these instructions. + +
+ If you want to use another system anyway, we suggest that you follow these
+ instructions, and then invoke
+ + options to dump the build commands it executes to a file, so you can see + what your alternate build system needs to do. + + |
+ 1. Get Boost; see sections 1 and 2 of the Boost Getting + Started Guide. +
+
+ 2. Get the bjam build driver.
+ See section 5 of the Boost Getting
+ Started Guide.
+
+ 3. cd into the example/quickstart/ directory of your Boost.Python installation,
+ which contains a small example project.
+
+ 4. Invoke bjam. Replace
+ the “stage“ argument
+ from the example invocation from section 5 of the Boost Getting
+ Started Guide with “test,“
+ to build all the test targets. Also add the argument “--verbose-test” to see the output generated by
+ the tests when they are run. On Windows, your bjam
+ invocation might look something like:
+
C:\\...\\quickstart> bjam toolset=msvc --verbose-test test ++
+ and on Unix variants, perhaps, +
+.../quickstart$ bjam toolset=gcc --verbose-test test ++
![]() |
+Note | +
|---|---|
+ For the sake of concision, the rest of this guide will use unix-style + forward slashes in pathnames instead of the backslashes with which Windows + users may be more familiar. The forward slashes should work everywhere + except in Command + Prompt windows, where you should use backslashes. + |
+ If you followed this procedure successfully, you will have built an extension
+ module called extending
+ and tested it by running a Python script called test_extending.py.
+ You will also have built and run a simple application called embedding that embeds python.
+
+ If you're seeing lots of compiler and/or linker error messages, it's probably
+ because Boost.Build is having trouble finding your Python installation.
+ You might want to pass the --debug-configuration option to bjam the first few times you invoke it,
+ to make sure that Boost.Build is correctly locating all the parts of your
+ Python installation. If it isn't, consider Configuring
+ Boost.Build as detailed below.
+
+ If you're still having trouble, Someone on one of the following mailing + lists may be able to help: +
++ Rejoice! If you're new to Boost.Python, at this point it might be a good + idea to ignore build issues for a while and concentrate on learning the + library by going through the Tutorial + and perhaps some of the Reference Manual, + trying out what you've learned about the API by modifying the quickstart + project. +
+
+ If you're content to keep your extension module forever in one source file
+ called extending.cpp, inside your Boost.Python distribution,
+ and import it forever as extending,
+ then you can stop here. However, it's likely that you will want to make
+ a few changes. There are a few things you can do without having to learn
+ Boost.Build in depth.
+
+ The project you just built is specified in two files in the current directory:
+ boost-build.jam, which tells bjam
+ where it can find the interpreted code of the Boost build system, and
+ Jamroot, which describes
+ the targets you just built. These files are heavily commented, so they
+ should be easy to modify. Take care, however, to preserve whitespace. Punctuation
+ such as ; will not be recognized
+ as intended by bjam if
+ it is not surrounded by whitespace.
+
+ You'll probably want to copy this project elsewhere so you can change + it without modifying your Boost distribution. To do that, simply +
+
+ a. copy the entire example/quickstart/ directory into a new directory.
+
+ b. In the new copies of boost-build.jam
+ and Jamroot, locate the
+ relative path near the top of the file that is clearly marked by a comment,
+ and edit that path so that it refers to the same directory your Boost
+ distribution as it referred to when the file was in its original location
+ in the example/quickstart/
+ directory.
+
+ For example, if you moved the project from /home/dave/boost_1_34_0/libs/python/example/quickstart to /home/dave/my-project, you could change the first
+ path in boost-build.jam from
+
../../../../tools/build/src ++
+ to +
+/home/dave/boost_1_34_0/tools/build/src ++
+ and change the first path in Jamroot
+ from
+
../../../..
+
++ to +
+/home/dave/boost_1_34_0 ++
+ The names of additional source files involved in building your extension
+ module or embedding application can be listed in Jamroot
+ right alongside extending.cpp
+ or embedding.cpp respectively. Just be sure to leave
+ whitespace around each filename:
+
… file1.cpp file2.cpp file3.cpp … ++
+ Naturally, if you want to change the name of a source file you can tell
+ Boost.Build about it by editing the name in Jamroot.
+
+ The name of the extension module is determined by two things: +
+Jamroot
+ immediately following python-extension,
+ and
+ BOOST_PYTHON_MODULE
+ in extending.cpp.
+
+ To change the name of the extension module from extending
+ to hello, you'd edit
+ Jamroot, changing
+
python-extension extending : extending.cpp ; ++
+ to +
+python-extension hello : extending.cpp ; ++
+ and you'd edit extending.cpp, changing +
+BOOST_PYTHON_MODULE(extending) ++
+ to +
+BOOST_PYTHON_MODULE(hello) ++
| + | + |
![]() |
+ If you are using a version of Python prior to 2.4.1 with a MinGW prior to
+ 3.0.0 (with binutils-2.13.90-20030111-1), you will need to create a MinGW-compatible
+ version of the Python library; the one shipped with Python will only work
+ with a Microsoft-compatible linker. Follow the instructions in the “Non-Microsoft”
+ section of the “Building Extensions: Tips And Tricks” chapter in Installing Python Modules
+ to create libpythonXX.a, where XX
+ corresponds to the major and minor version numbers of your Python installation.
+
| + | + |
![]() |
+ Python can be built in a special “python debugging” configuration that + adds extra checks and instrumentation that can be very useful for developers + of extension modules. The data structures used by the debugging configuration + contain additional members, so a Python executable + built with python debugging enabled cannot be used with an extension module + or library compiled without it, and vice-versa. +
+
+ Since pre-built “python debugging” versions of the Python executable
+ and libraries are not supplied with most distributions of Python, [5] and we didn't want to force our users to build them, Boost.Build
+ does not automatically enable python debugging in its debug
+ build variant (which is the default). Instead there is a special build property
+ called python-debugging that, when used as a build property,
+ will define the right preprocessor symbols and select the right libraries
+ to link with.
+
+ On unix-variant platforms, the debugging versions of Python's data structures
+ will only be used if the symbol Py_DEBUG
+ is defined. On many windows compilers, when extension modules are built with
+ the preprocessor symbol _DEBUG,
+ Python defaults to force linking with a special debugging version of the
+ Python DLL. Since that symbol is very commonly used even when Python is not
+ present, Boost.Python temporarily undefines _DEBUG
+ when Python.h is #included from boost/python/detail/wrap_python.hpp -
+ unless BOOST_DEBUG_PYTHON
+ is defined. The upshot is that if you want “python debugging”and you
+ aren't using Boost.Build, you should make sure BOOST_DEBUG_PYTHON
+ is defined, or python debugging will be suppressed.
+
[5] + On Unix and similar platforms, a debugging python and associated libraries + are built by adding --with-pydebug when configuring the Python build. On + Windows, the debugging version of Python is generated by the "Win32 + Debug" target of the Visual Studio project in the PCBuild subdirectory + of a full Python source code distribution. +
| + | + |
![]() |
+ To run the full test suite for Boost.Python, invoke bjam
+ in the test subdirectory
+ of your Boost.Python distribution.
+
| + | + |
![]() |
+ Boost.Python uses several configuration
+ macros in <boost/config.hpp>, as well as configuration macros meant
+ to be supplied by the application. These macros are documented here.
+
+ These are the macros that may be defined by an application using Boost.Python. + Note that if you extend a strict interpretation of the C++ standard to + cover dynamic libraries, using different values of these macros when compiling + different libraries (including extension modules and the Boost.Python library + itself) is a violation of the ODR. However, + we know of no C++ implementations on which this particular violation is + detectable or causes any problems. +
+|
+ + Macro + + |
+
+ + Default + + |
+
+ + Meaning + + |
+
|---|---|---|
|
+ + BOOST_PYTHON_MAX_ARITY + + |
+
+ + 15 + + |
+
+ + The maximum arity of any function, member function, or constructor + to be wrapped, invocation of a Boost.Python function wich is + specified as taking arguments x1, x2,...Xn. This includes, in + particular, callback mechanisms such as object::operator()(...) + or call_method<R>(... ). + + |
+
|
+ + BOOST_PYTHON_MAX_BASES + + |
+
+ + 10 + + |
+
+
+ The maximum number of template arguments to the |
+
|
+ + BOOST_PYTHON_STATIC_MODULE + + |
+
+ + not defined + + |
+
+ + If defined, prevents your module initialization function from + being treated as an exported symbol on platforms which support + that distinction in-code + + |
+
|
+ + BOOST_PYTHON_ENABLE_CDECL + + |
+
+ + not defined + + |
+
+
+ If defined, allows functions using the |
+
|
+ + BOOST_PYTHON_ENABLE_STDCALL + + |
+
+ + not defined + + |
+
+
+ If defined, allows functions using the |
+
|
+ + BOOST_PYTHON_ENABLE_FASTCALL + + |
+
+ + not defined + + |
+
+
+ If defined, allows functions using the |
+
+ These macros are defined by Boost.Python + and are implementation details of interest only to implementors and those + porting to new platforms. +
+|
+ + Macro + + |
+
+ + Default + + |
+
+ + Meaning + + |
+
|---|---|---|
|
+ + BOOST_PYTHON_TYPE_ID_NAME + + |
+
+ + not defined + + |
+
+
+ If defined, this indicates that the type_info comparison across
+ shared library boundaries does not work on this platform. In
+ other words, if shared-lib-1 passes |
+
|
+ + BOOST_PYTHON_NO_PY_SIGNATURES + + |
+
+ + not defined + + |
+
+
+ If defined for a module no pythonic signatures are generated
+ for the docstrings of the module functions, and no python type
+ is associated with any of the converters registered by the module.
+ This also reduces the binary size of the module by about 14%
+ (gcc compiled). If defined for the boost_python runtime library,
+ the default for the |
+
|
+ + BOOST_PYTHON_SUPPORTS_PY_SIGNATURES + + |
+
+
+ defined if |
+
+ + This macro is defined to enable a smooth transition from older + Boost.Python versions which do not support pythonic signatures. + For example usage see here. + + |
+
|
+ + BOOST_PYTHON_PY_SIGNATURES_PROPER_INIT_SELF_TYPE + + |
+
+ + not defined + + |
+
+
+ If defined the python type of |
+
| + | + |
![]() |
return_internal_reference efficient?*= operator work?+ If what you're trying to do is something like this: +
+typedef boost::function<void (string s) > funcptr; + +void foo(funcptr fp) +{ + fp("hello,world!"); +} + +BOOST_PYTHON_MODULE(test) +{ + def("foo",foo); +} ++
+ And then: +
+>>> def hello(s): +... print s +... +>>> foo(hello) +hello, world! ++
+ The short answer is: "you can't". This is not a Boost.Python limitation
+ so much as a limitation of C++. The problem is that a Python function is
+ actually data, and the only way of associating data with a C++ function pointer
+ is to store it in a static variable of the function. The problem with that
+ is that you can only associate one piece of data with every C++ function,
+ and we have no way of compiling a new C++ function on-the-fly for every Python
+ function you decide to pass to foo.
+ In other words, this could work if the C++ function is always going to invoke
+ the same Python function, but you probably don't want
+ that.
+
+ If you have the luxury of changing the C++ code you're wrapping, pass it
+ an object instead and call
+ that; the overloaded function call operator will invoke the Python function
+ you pass it behind the object.
+
| + | + |
![]() |
+ Please refer to the Reducing Compiling Time
+ section in the Tutorial.
+
| + | + |
![]() |
+ It is known to work under 10.2.8 and 10.3 using Apple's gcc 3.3 compiler: +
+gcc (GCC) 3.3 20030304 (Apple Computer, Inc. build 1493)+
+ Under 10.2.8 get the August 2003 gcc update (free at http://connect.apple.com). + Under 10.3 get the Xcode Tools v1.0 (also free). +
++ Python 2.3 is required. The Python that ships with 10.3 is fine. Under 10.2.8 + use these commands to install Python as a framework: +
+./configure --enable-framework +make +make frameworkinstall+
+ The last command requires root privileges because the target directory is
+ /Library/Frameworks/Python.framework/Versions/2.3. However,
+ the installation does not interfere with the Python version that ships with
+ 10.2.8.
+
+ It is also crucial to increase the stacksize
+ before starting compilations, e.g.:
+
limit stacksize 8192k+
+ If the stacksize is too small
+ the build might crash with internal compiler errors.
+
+ Sometimes Apple's compiler exhibits a bug by printing an error like the following
+ while compiling a boost::python::class_<your_type>
+ template instantiation:
+
.../inheritance.hpp:44: error: cannot + dynamic_cast `p' (of type `struct cctbx::boost_python::<unnamed>::add_pair* + ') to type `void*' (source type is not polymorphic) ++
+ We do not know a general workaround, but if the definition of your_type can be modified the following
+ was found to work in all cases encountered so far:
+
struct your_type +{ + // before defining any member data +#if defined(__MACH__) && defined(__APPLE_CC__) && __APPLE_CC__ == 1493 + bool dummy_; +#endif + // now your member data, e.g. + double x; + int j; + // etc. +}; ++
| + | + |
![]() |
+ Niall Douglas provides these notes: +
++ If you see Microsoft Visual C++ 7.1 (MS Visual Studio .NET 2003) issue an + error message like the following it is most likely due to a bug in the compiler: +
+boost\boost\python\detail\invoke.hpp(76): +error C2064: term does not evaluate to a function taking 2 arguments" ++
+ This message is triggered by code like the following: +
+#include <boost/python.hpp> + +using namespace boost::python; + +class FXThread +{ +public: + bool setAutoDelete(bool doso) throw(); +}; + +void Export_FXThread() +{ + class_< FXThread >("FXThread") + .def("setAutoDelete", &FXThread::setAutoDelete) + ; +} ++
+ The bug is related to the throw() modifier. As a workaround cast off the
+ modifier. E.g.:
+
.def("setAutoDelete", (bool (FXThread::*)(bool)) &FXThread::setAutoDelete) ++
+ (The bug has been reported to Microsoft.) +
+| + | + |
![]() |
+ Q: I get this error message when + compiling a large source file. What can I do? +
++ A: You have two choices: +
+
+ my_module.cpp:
+
... +void more_of_my_module(); +BOOST_PYTHON_MODULE(my_module) +{ + def("foo", foo); + def("bar", bar); + ... + more_of_my_module(); +} ++
+ more_of_my_module.cpp:
+
void more_of_my_module() +{ + def("baz", baz); + ... +} ++
+ If you find that a class_<...> declaration can't fit in
+ a single source file without triggering the error, you can always pass
+ a reference to the class_
+ object to a function in another source file, and call some of its member
+ functions (e.g. .def(...)) in the auxilliary source file:
+
+ more_of_my_class.cpp:
+
void more_of_my_class(class<my_class>& x) +{ + x + .def("baz", baz) + .add_property("xx", &my_class::get_xx, &my_class::set_xx) + ; + ... +} ++
| + | + |
![]() |
+ Ralf W. Grosse-Kunstleve provides these notes: +
++ Below is a small, self-contained demo extension module that shows how to + do this. Here is the corresponding trivial test: +
+import custom_string +assert custom_string.hello() == "Hello world." +assert custom_string.size("california") == 10 ++
+ If you look at the code you will find: +
+to_python converter
+ (easy): custom_string_to_python_str
+ custom_string_from_python_str
+ + The custom converters are registered in the global Boost.Python registry + near the top of the module initialization function. Once flow control has + passed through the registration code the automatic conversions from and to + Python strings will work in any module imported in the same process. +
+#include <boost/python/module.hpp> +#include <boost/python/def.hpp> +#include <boost/python/to_python_converter.hpp> + +namespace sandbox { namespace { + +class custom_string +{ + public: + custom_string() {} + custom_string(std::string const &value) : value_(value) {} + std::string const &value() const { return value_; } + private: + std::string value_; +}; + +struct custom_string_to_python_str +{ + static PyObject* convert(custom_string const &s) + { + return boost::python::incref(boost::python::object(s.value()).ptr()); + } +}; + +struct custom_string_from_python_str +{ + custom_string_from_python_str() + { + boost::python::converter::registry::push_back( + &convertible, + &construct, + boost::python::type_id<custom_string>()); + } + + static void* convertible(PyObject* obj_ptr) + { + if (!PyString_Check(obj_ptr)) return 0; + return obj_ptr; + } + + static void construct( + PyObject* obj_ptr, + boost::python::converter::rvalue_from_python_stage1_data* data) + { + const char* value = PyString_AsString(obj_ptr); + if (value == 0) boost::python::throw_error_already_set(); + void* storage = ( + (boost::python::converter::rvalue_from_python_storage<custom_string>*) + data)->storage.bytes; + new (storage) custom_string(value); + data->convertible = storage; + } +}; + +custom_string hello() { return custom_string("Hello world."); } + +std::size_t size(custom_string const &s) { return s.value().size(); } + +void init_module() +{ + using namespace boost::python; + + boost::python::to_python_converter< + custom_string, + custom_string_to_python_str>(); + + custom_string_from_python_str(); + + def("hello", hello); + def("size", size); +} + +}} // namespace sandbox::<anonymous> + +BOOST_PYTHON_MODULE(custom_string) +{ + sandbox::init_module(); +} ++
| + | + |
![]() |
+ "I am wrapping a function that always returns a pointer to an already-held + C++ object." +
+ One way to do that is to hijack the mechanisms used for wrapping a class + with virtual functions. If you make a wrapper class with an initial PyObject* + constructor argument and store that PyObject* as "self", you can + get back to it by casting down to that wrapper type in a thin wrapper function. + For example: +
+class X { X(int); virtual ~X(); ... }; +X* f(); // known to return Xs that are managed by Python objects + + +// wrapping code + +struct X_wrap : X +{ + X_wrap(PyObject* self, int v) : self(self), X(v) {} + PyObject* self; +}; + +handle<> f_wrap() +{ + X_wrap* xw = dynamic_cast<X_wrap*>(f()); + assert(xw != 0); + return handle<>(borrowed(xw->self)); +} + +... + +def("f", f_wrap()); +class_<X,X_wrap,boost::noncopyable>("X", init<int>()) + ... + ; ++
+ Of course, if X has no virtual functions you'll have to use static_cast instead of dynamic_cast
+ with no runtime check that it's valid. This approach also only works if the
+ X object was constructed
+ from Python, because Xs constructed
+ from C++ are of course never X_wrap
+ objects.
+
+ Another approach to this requires you to change your C++ code a bit; if that's
+ an option for you it might be a better way to go. work we've been meaning
+ to get to anyway. When a shared_ptr<X>
+ is converted from Python, the shared_ptr actually manages a reference to
+ the containing Python object. When a shared_ptr<X> is converted back
+ to Python, the library checks to see if it's one of those "Python object
+ managers" and if so just returns the original Python object. So you
+ could just write object(p) to get
+ the Python object back. To exploit this you'd have to be able to change the
+ C++ code you're wrapping so that it deals with shared_ptr instead of raw
+ pointers.
+
+ There are other approaches too. The functions that receive the Python object + that you eventually want to return could be wrapped with a thin wrapper that + records the correspondence between the object address and its containing + Python object, and you could have your f_wrap function look in that mapping + to get the Python object out. +
+| + | + |
![]() |
+ Q: Part of an API that I'm wrapping goes + something like this: +
+struct A {}; struct B { void add( A* ); } +where B::add() takes ownership of the pointer passed to it. ++
+ However: +
+a = mod.A() +b = mod.B() +b.add( a ) +del a +del b +# python interpreter crashes +# later due to memory corruption. ++
+ Even binding the lifetime of a to b via with_custodian_and_ward
+ doesn't prevent the python object a from ultimately trying to delete the
+ object it's pointing to. Is there a way to accomplish a 'transfer-of-ownership'
+ of a wrapped C++ object?
+
+ --Bruce Lowery +
++ Yes: Make sure the C++ object is held by auto_ptr: +
+class_<A, std::auto_ptr<A> >("A") + ... + ; ++
+ Then make a thin wrapper function which takes an auto_ptr parameter: +
+void b_insert(B &b, std::auto_ptr<A> a) +{ + b.insert(a.get()); + a.release(); +} ++
+ Wrap that as B.add. Note that pointers returned via manage_new_object
+ will also be held by auto_ptr,
+ so this transfer-of-ownership will also work correctly.
+
| + | + |
![]() |
+ Ralf W. Grosse-Kunstleve provides these notes: +
+
+ Using the regular class_<> wrapper:
+
class_<std::vector<double> >("std_vector_double") + .def(...) + ... +; ++ This can be moved to a template so that several types (
double, int,
+ long, etc.) can be wrapped
+ with the same code. This technique is used in the file scitbx/include/scitbx/array_family/boost_python/flex_wrapper.h in the "scitbx" package.
+ The file could easily be modified for wrapping std::vector<> instantiations. This type of
+ C++/Python binding is most suitable for containers that may contain a
+ large number of elements (>10000).
+ + Using custom rvalue converters. Boost.Python "rvalue converters" + match function signatures such as: +
+void foo(std::vector<double> const &array); // pass by const-reference +void foo(std::vector<double> array); // pass by value ++ Some custom rvalue converters are implemented in the file
scitbx/include/scitbx/boost_python/container_conversions.h This code can be used to convert
+ from C++ container types such as std::vector<> or std::list<> to Python tuples and vice versa.
+ A few simple examples can be found in the file scitbx/array_family/boost_python/regression_test_module.cpp
+ Automatic C++ container <-> Python tuple conversions are most suitable
+ for containers of moderate size. These converters generate significantly
+ less object code compared to alternative 1 above.
+ + A disadvantage of using alternative 2 is that operators such as arithmetic + +,-,*,/,% are not available. It would be useful to have custom rvalue converters + that convert to a "math_array" type instead of tuples. This is + currently not implemented but is possible within the framework of Boost.Python + V2 as it will be released in the next couple of weeks. [ed.: this was posted + on 2002/03/10] +
+
+ It would also be useful to also have "custom lvalue converters"
+ such as std::vector<>
+ <-> Python list. These converters would support the modification of
+ the Python list from C++. For example:
+
+ C++: +
+void foo(std::vector<double> &array) +{ + for(std::size_t i=0;i<array.size();i++) { + array[i] *= 2; + } +} ++
+ Python: +
+>>> l = [1, 2, 3] +>>> foo(l) +>>> print l +[2, 4, 6] ++
+ Custom lvalue converters require changes to the Boost.Python core library + and are currently not available. +
++ P.S.: +
++ The "scitbx" files referenced above are available via anonymous + CVS: +
+cvs -d:pserver:anonymous@cvs.cctbx.sourceforge.net:/cvsroot/cctbx login +cvs -d:pserver:anonymous@cvs.cctbx.sourceforge.net:/cvsroot/cctbx co scitbx ++
| + | + |
![]() |
+ Please refer to the Creating Packages section in the Tutorial.
+
| + | + |
![]() |
+ Greg Burley gives the following answer for Unix GCC users: +
+++ Once you have created a boost python extension for your c++ library or + class, you may need to debug the code. Afterall this is one of the reasons + for wrapping the library in python. An expected side-effect or benefit + of using BPL is that debugging should be isolated to the c++ library that + is under test, given that python code is minimal and boost::python either + works or it doesn't. (ie. While errors can occur when the wrapping method + is invalid, most errors are caught by the compiler ;-). +
++ The basic steps required to initiate a gdb session to debug a c++ library + via python are shown here. Note, however that you should start the gdb + session in the directory that contains your BPL my_ext.so module. +
+(gdb) target exec python +(gdb) run +>>> from my_ext import * +>>> [C-c] +(gdb) break MyClass::MyBuggyFunction +(gdb) cont +>>> pyobj = MyClass() +>>> pyobj.MyBuggyFunction() +Breakpoint 1, MyClass::MyBuggyFunction ... +Current language: auto; currently c++ +(gdb) do debugging stuff ++
+ Greg's approach works even better using Emacs' "gdb" command, since + it will show you each line of source as you step through it. +
++ On Windows, my favorite debugging solution + is the debugger that comes with Microsoft Visual C++ 7. This debugger seems + to work with code generated by all versions of Microsoft and Metrowerks toolsets; + it's rock solid and "just works" without requiring any special + tricks from the user. +
++ Raoul Gough has provided the following for gdb on Windows: +
+++ gdb support for Windows DLLs has improved lately, so it is now possible + to debug Python extensions using a few tricks. Firstly, you will need an + up-to-date gdb with support for minimal symbol extraction from a DLL. Any + gdb from version 6 onwards, or Cygwin gdb-20030214-1 and onwards should + do. A suitable release will have a section in the gdb.info file under Configuration + - Native - Cygwin Native - Non-debug DLL symbols. Refer to that info section + for more details of the procedures outlined here. +
++ Secondly, it seems necessary to set a breakpoint in the Python interpreter, + rather than using ^C to break execution. A good place to set this breakpoint + is PyOS_Readline, which will stop execution immediately before reading + each interactive Python command. You have to let Python start once under + the debugger, so that it loads its own DLL, before you can set the breakpoint: +
+$ gdb python +GNU gdb 2003-09-02-cvs (cygwin-special) +[...] + +(gdb) run +Starting program: /cygdrive/c/Python22/python.exe +Python 2.2.2 (#37, Oct 14 2002, 17:02:34) [MSC 32 bit (Intel)] on win32 +Type "help", "copyright", "credits" or "license" for more information. +>>> ^Z + + +Program exited normally. +(gdb) break *&PyOS_Readline +Breakpoint 1 at 0x1e04eff0 +(gdb) run +Starting program: /cygdrive/c/Python22/python.exe +Python 2.2.2 (#37, Oct 14 2002, 17:02:34) [MSC 32 bit (Intel)] on win32 +Type "help", "copyright", "credits" or "license" for more information. + +Breakpoint 1, 0x1e04eff0 in python22!PyOS_Readline () + from /cygdrive/c/WINNT/system32/python22.dll +(gdb) cont +Continuing. +>>> from my_ext import * + +Breakpoint 1, 0x1e04eff0 in python22!PyOS_Readline () + from /cygdrive/c/WINNT/system32/python22.dll +(gdb) # my_ext now loaded (with any debugging symbols it contains) ++
+ If you are launching your extension module tests with Boost.Build
+ using the boost-python-runtest rule, you can ask it to launch
+ your debugger for you by adding "--debugger=debugger"
+ to your bjam command-line:
+
bjam -sTOOLS=vc7.1 "--debugger=devenv /debugexe" test +bjam -sTOOLS=gcc -sPYTHON_LAUNCH=gdb test ++
+ It can also be extremely useful to add the -d+2
+ option when you run your test, because Boost.Build will then show you the
+ exact commands it uses to invoke it. This will invariably involve setting
+ up PYTHONPATH and other important environment variables such as LD_LIBRARY_PATH
+ which may be needed by your debugger in order to get things to work right.
+
| + | + |
![]() |
+ That exception is protecting you from causing a nasty crash. It usually happens + in response to some code like this: +
+period const &get_floating_frequency() const +{ + return boost::python::call_method<period const &>( + m_self,"get_floating_frequency"); +} ++
+ And you get: +
+ReferenceError: Attempt to return dangling reference to object of type: +class period ++
+ In this case, the Python method invoked by call_method
+ constructs a new Python object. You're trying to return a reference to a
+ C++ object (an instance of class period) contained within and owned by that
+ Python object. Because the called method handed back a brand new object,
+ the only reference to it is held for the duration of get_floating_frequency() above. When the function returns, the Python
+ object will be destroyed, destroying the instance of class
+ period, and leaving the returned
+ reference dangling. That's already undefined behavior, and if you try to
+ do anything with that reference you're likely to cause a crash. Boost.Python
+ detects this situation at runtime and helpfully throws an exception instead
+ of letting you do that.
+
| + | + |
![]() |
+ Niall Douglas provides these notes: +
++ The quick answer to this is: no. +
+
+ The longer answer is that it can be patched to be so, but it's complex. You
+ will need to add custom lock/unlock wrapping of every time your code enters
+ Boost.Python (particularly every virtual function override) plus heavily
+ modify boost/python/detail/invoke.hpp with custom unlock/lock wrapping of
+ every time Boost.Python enters your code. You must furthermore take care
+ to not unlock/lock when Boost.Python is invoking iterator
+ changes via invoke.hpp.
+
+ There is a patched invoke.hpp posted
+ on the C++-SIG mailing list archives and you can find a real implementation
+ of all the machinery necessary to fully implement this in the TnFOX project
+ at this SourceForge
+ project location.
+
| + | + |
![]() |
+ Q: /I have an object composed of 12 doubles.
+ A const&
+ to this object is returned by a member function of another class. From the
+ viewpoint of using the returned object in Python I do not care if I get a
+ copy or a reference to the returned object. In Boost.Python I have the choice
+ of using copy_const_reference
+ or return_internal_reference.
+ Are there considerations that would lead me to prefer one over the other,
+ such as size of generated code or memory overhead?/
+
+ A: copy_const_reference
+ will make an instance with storage for one of your objects, size = base_size + 12 * sizeof(double).
+ return_internal_reference
+ will make an instance with storage for a pointer to one of your objects,
+ size =
+ base_size +
+ sizeof(void*). However,
+ it will also create a weak reference object which goes in the source object's
+ weakreflist and a special callback object to manage the lifetime of the internally-referenced
+ object. My guess? copy_const_reference
+ is your friend here, resulting in less overall memory use and less fragmentation,
+ also probably fewer total cycles.
+
| + | + |
![]() |
+ Q: I have exported my class to
+ python, with many overloaded operators. it works fine for me except the
+ *= operator. It always tells
+ me "can't multiply sequence with non int type". If I use p1.__imul__(p2)
+ instead of p1 *=
+ p2, it successfully executes my
+ code. What's wrong with me?
+
+ A: There's nothing wrong with you. This + is a bug in Python 2.2. You can see the same effect in Pure Python (you can + learn a lot about what's happening in Boost.Python by playing with new-style + classes in Pure Python). +
+>>> class X(object): +... def __imul__(self, x): +... print 'imul' +... +>>> x = X() +>>> x *= 1 ++
+ To cure this problem, all you need to do is upgrade your Python to version + 2.2.1 or later. +
+| + | + |
![]() |
+ Niall Douglas provides these notes: +
+
+ If you define custom converters similar to the ones shown above the def_readonly()
+ and def_readwrite()
+ member functions provided by boost::python::class_
+ for direct access to your member data will not work as expected. This is
+ because def_readonly("bar",&foo::bar) is equivalent to:
+
.add_property("bar", make_getter(&foo::bar, return_internal_reference())) ++
+ Similarly, def_readwrite("bar",&foo::bar)
+ is equivalent to:
+
.add_property("bar", make_getter(&foo::bar, return_internal_reference()), + make_setter(&foo::bar, return_internal_reference()) ++
+ In order to define return value policies compatible with the custom conversions
+ replace def_readonly()
+ and def_readwrite()
+ by add_property().
+ E.g.:
+
.add_property("bar", make_getter(&foo::bar, return_value_policy<return_by_value>()), + make_setter(&foo::bar, return_value_policy<return_by_value>())) ++
| + | + |
![]() |
+
+ The number of argumnts accepted by a function or member function. Unless
+ otherwise specified, the hidden this
+ argument to member functions is not counted when specifying arity.
+
+ Null-Terminated Byte String, or 'C'-string. C++ string literals are + ntbses. An ntbs + must never be null. +
+ Exceptions in Python are "raised", not "thrown",
+ as they are in C++. When this documentation says that some Python exception
+ is "raised" in the context of C++ code, it means that the corresponding
+ Python exception is set via the Python/'C'
+ API, and throw_error_already_set() is called.
+
+ A technical term from the C++ standard. Short for "Plain Ol'Data": + A POD-struct is an aggregate class that has no non-static data members + of type pointer to member, non-POD-struct, non-POD-union (or array of + such types) or reference, and has no user-defined copy assign- ment operator + and no user-defined destructor. Similarly, a POD-union is an aggregate + union that has no non-static data members of type pointer to member, + non-POD-struct, non-POD-union (or array of such types) or reference, + and has no user-defined copy assignment operator and no user-defined + destructor. A POD class is a class that is either a POD-struct or a POD-union. + An aggregate is an array or a class (clause 9) with no user-declared + constructors (12.1), no private or protected non-static data members + (clause 11), no base classes (clause 10), and no virtual functions (10.3). +
+ The "One Definition Rule", which says that any entity in a + C++ program must have the same definition in all translation units (object + files) which make up a program. +
| + | + |
![]() |
Copyright © 2002-2015 David + Abrahams, Stefan Seefeld
+ Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +
++ Welcome to Boost.Python, a C++ library which enables seamless interoperability + between C++ and the Python programming language. The library includes support + for: +
+
+ The development of these features was funded in part by grants to Boost Consulting
+ from the Lawrence Livermore National Laboratories
+ and by the Computational Crystallography Initiative
+ at Lawrence Berkeley National Laboratories.
+
+ Building Hybrid Systems With Boost Python, + by Dave Abrahams and Ralf W. Grosse-Kunstleve +
+Last revised: October 07, 2016 at 15:41:08 GMT |
++ |
![]() |
Table of Contents
++ Models of the CallPolicies concept are used to specialize the behavior + of Python callable objects generated by Boost.Python to wrapped C++ objects + like function and member function pointers, providing three behaviors: +
+precall - Python argument
+ tuple management before the wrapped object is invoked
+ result_converter -
+ C++ return value handling
+ postcall - Python argument
+ tuple and result management after the wrapped object is invoked
+ extract_return_type
+ - metafunction for extracting the return type from a given signature
+ type sequence
+
+ In order to allow the use of multiple models of CallPolicies in the same
+ callable object, Boost.Python's CallPolicies class templates provide a
+ chaining interface which allows them to be recursively composed. This interface
+ takes the form of an optional template parameter, Base,
+ which defaults to default_call_policies.
+ By convention, the precall
+ function of the Base is
+ invoked after the precall
+ function supplied by the outer
+ template, and the postcall
+ function of the Base is
+ invoked before the postcall
+ function of the outer template.
+ If a result_converter is
+ supplied by the outer template,
+ it replaces any result_converter
+ supplied by the Base. For
+ an example, see return_internal_reference.
+
|
+ + Expression + + |
+
+ + Type + + |
+
+ + Result/Semantics + + |
+
|---|---|---|
|
+
+ |
+
+
+ convertible to |
+
+
+ returns |
+
|
+
+ |
+
+
+ A model of |
+
+ + An MPL unary Metafunction Class used produce the "preliminary" + result object. + + |
+
|
+
+ |
+
+
+ convertible to |
+
+
+ |
+
|
+
+ |
+
+ + A model of Metafunction. + + |
+
+
+ An MPL unary Metafunction used extract the return type from a
+ given signature. By default it is derived from |
+
| + | + |
![]() |
+ Instances of a Dereferenceable
+ type can be used like a pointer to access an lvalue.
+
+ In the table below, T is
+ a model of Dereferenceable, and x
+ denotes an object of type T.
+ In addition, all pointers are Dereferenceable.
+
|
+ + Expression + + |
+
+ + Result + + |
+
+ + Operational Semantics + + |
+
|---|---|---|
|
+
+ |
+
+
+ convertible to |
+
+
+ |
+
| + | + |
![]() |
+ An Extractor is a class which Boost.Python can use to extract C++ objects
+ from Python objects, and is typically used by facilities that define from_python conversions for "traditional"
+ Python extension types.
+
+ In the table below, X denotes
+ a model of Extractor and
+ a denotes an instance of
+ a Python object type.
+
|
+ + Expression + + |
+
+ + Type + + |
+
+ + Semantics + + |
+
|---|---|---|
|
+
+ |
+
+ + non-void + + |
+
+ + Returns the C++ object being extracted. The execute function + must not be overloaded. + + |
+
|
+
+ |
+
+
+ |
+
+
+ Points to the |
+
+ Informally, an Extractor's execute member must be a non-overloaded static + function whose single argument is a Python object type. Acceptable Python + object types include those publicly (and unambiguously) derived from PyObject, + and POD types which are layout-compatible with PyObject. +
+| + | + |
![]() |
+ A HolderGenerator is a unary metafunction class which returns types suitable + for holding instances of its argument in a wrapped C++ class instance. +
+
+ In the table below, G denotes
+ an type which models HolderGenerator,
+ and X denotes a class type.
+
|
+ + Expression + + |
+
+ + Requirements + + |
+
|---|---|
|
+
+ |
+
+
+ A concrete subclass of |
+
| + | + |
![]() |
+ This page defines two concepts used to describe classes which manage a + Python objects, and which are intended to support usage with a Python-like + syntax. +
++ Models of the ObjectWrapper concept have object + as a publicly-accessible base class, and are used to supply special construction + behavior and/or additional convenient functionality through (often templated) + member functions. Except when the return type R is itself an TypeWrapper, + a member function invocation of the form +
+x.some_function(a1, a2,...an)+
+ always has semantics equivalent to: +
+extract<R>(x.attr("some_function")(object(a1), object(a2),...object(an)))()+
+ (see caveat below). +
+
+ TypeWrapper is a refinement of ObjectWrapper
+ which is associated with a particular Python type X.
+ For a given TypeWrapper T,
+ a valid constructor expression
+
T(a1, a2,...an)+
+ builds a new T object managing the result of invoking X with arguments + corresponding to +
+object(a1), object(a2),...object(an)+
+ . When used as arguments to wrapped C++ functions, or as the template parameter + to extract<>, + only instances of the associated Python type will be considered a match. +
+
+ The upshot of the special member function invocation rules when the return
+ type is a TypeWrapper is that it is possible for the returned object to
+ manage a Python object of an inappropriate type. This is not usually a
+ serious problem; the worst-case result is that errors will be detected
+ at runtime a little later than they might otherwise be. For an example
+ of how this can occur, note that the dict
+ member function items returns
+ an object of type list.
+ Now suppose the user defines this dict
+ subclass in Python:
+
>>> class mydict(dict): +... def items(self): +... return tuple(dict.items(self)) # return a tuple ++
+ Since an instance of mydict
+ is also an instance of dict,
+ when used as an argument to a wrapped C++ function, boost::python::dict
+ can accept objects of Python type mydict.
+ Invoking items()
+ on this object can result in an instance of boost::python::list
+ which actually holds a Python tuple.
+ Subsequent attempts to use list
+ methods (e.g. append, or
+ any other mutating operation) on this object will raise the same exception
+ that would occur if you tried to do it from Python.
+
| + | + |
![]() |
+ A ResultConverter for a type T
+ is a type whose instances can be used to convert C++ return values of type
+ T to_python.
+ A ResultConverterGenerator is an MPL unary metafunction class which, given
+ the return type of a C++ function, returns a ResultConverter for that type.
+ ResultConverters in Boost.Python generally inspect library's registry of
+ converters to find a suitable converter, but converters which don't use
+ the registry are also possible.
+
+ In the table below, C denotes
+ a ResultConverter type for a type R,
+ c denotes an object of
+ type C, and r denotes an object of type R.
+
|
+ + Expression + + |
+
+ + Type + + |
+
+ + Semantics + + |
+
|---|---|---|
|
+
+ |
++ | +
+
+ Constructs a |
+
|
+
+ |
+
+
+ convertible to |
+
+
+ |
+
|
+
+ |
+
+
+ convertible to |
+
+
+ A pointer to a Python object corresponding to |
+
|
+
+ |
+
+
+ |
+
+
+ A pointer to a Python Type object corresponding to result of
+ the conversion, or |
+
+ In the table below, G denotes
+ a ResultConverterGenerator type and R
+ denotes a possible C++ function return type.
+
|
+ + Expression + + |
+
+ + Requirements + + |
+
|---|---|
|
+
+ |
+
+
+ A ResultConverter type for |
+
| + | + |
![]() |
Table of Contents
+ ++ Exposes a mechanism for embedding the python interpreter into C++ code. +
+object eval(str expression, + object globals = object(), + object locals = object()); ++
+
+ Evaluate Python expression from expression in the context specified + by the dictionaries globals and locals. +
+ An instance of object which holds the value of the expression. +
object exec(str code, + object globals = object(), + object locals = object()); ++
+
+ Execute Python source code from code in the context specified by + the dictionaries globals and locals. +
+ An instance of object which holds the result of executing the code. +
object exec_file(str filename, + object globals = object(), + object locals = object()); ++
+
+ Execute Python source code from the file named by filename in the + context specified by the dictionaries globals and locals. +
+ An instance of object which holds the result of executing the code. +
+ The following example demonstrates the use of import and exec to define + a function in python, and later call it from within C++. +
+#include <iostream> +#include <string> + +using namespace boost::python; + +void greet() +{ + // Retrieve the main module. + object main = import("__main__"); + + // Retrieve the main module's namespace + object global(main.attr("__dict__")); + + // Define greet function in Python. + object result = exec( + "def greet(): \n" + " return 'Hello from Python!' \n", + global, global); + + // Create a reference to it. + object greet = global["greet"]; + + // Call it. + std::string message = extract<std::string>(greet()); + std::cout << message << std::endl; +} ++
+ Instead of embedding the python script into a string, we could also store + it in an a file... +
+def greet(): + return 'Hello from Python!' ++
+ ... and execute that instead. +
+// ... + // Load the greet function from a file. + object result = exec_file(script, global, global); + // ... +} ++
| + | + |
![]() |
+ Exposes a mechanism for importing python modules. +
+object import(str name);+
+
+ Imports the module named by name. +
+ An instance of object which holds a reference to the imported module. +
+ The following example demonstrates the use of import to access a function + in python, and later call it from within C++. +
+#include <iostream> +#include <string> + +using namespace boost::python; + +void print_python_version() +{ + // Load the sys module. + object sys = import("sys"); + + // Extract the python version. + std::string version = extract<std::string>(sys.attr("version")); + std::cout << version << std::endl; +} ++
| + | + |
![]() |
Table of Contents
++ Supplies a family of overloaded functions for specifying argument keywords + for wrapped C++ functions. +
++ A keyword-expression results in an object which holds a sequence of + ntbses, and whose type encodes the number + of keywords specified. The keyword-expression may contain default values + for some or all of the keywords it holds +
+arg
++ The objects of class arg are keyword-expressions holding one keyword ( + size one ) +
+namespace boost { namespace python +{ + struct arg + { + template <class T> + arg &operator = (T const &value); + explicit arg (char const *name){elements[0].name = name;} + }; + +}} ++
arg(char const* name);+
+
+ The argument must be a ntbs. +
+ Constructs an arg object holding a keyword with name name. +
template <class T> arg &operator = (T const &value);+
+
+ The argument must convertible to python. +
+ Assigns default value for the keyword. +
+ Reference to this.
+
keyword-expression operator , (keyword-expression, const arg &kw) const +keyword-expression operator , (keyword-expression, const char *name) const; ++
+
+ The argument name must be a ntbs. +
+ Extends the keyword-expression argument with one more keyword. +
+ The extended keyword-expression. +
#include <boost/python/def.hpp> +using namespace boost::python; + +int f(double x, double y, double z=0.0, double w=1.0); + +BOOST_PYTHON_MODULE(xxx) +{ + def("f", f, (arg("x"), "y", arg("z")=0.0, arg("w")=1.0)); +} ++
| + | + |
![]() |
<boost/python/call.hpp> defines the call family of overloaded function templates, used to invoke Python callable objects from C++. ++
template <class R, class A1, class A2, ... class An> +R call(PyObject* callable, A1 const&, A2 const&, ... An const&) ++
+
+ R is a pointer type, reference type, or a complete type with an accessible + copy constructor +
+ Invokes callable(a1, a2, ...an) in Python, where a1...an are the + arguments to call(), converted to Python objects. +
+ The result of the Python call, converted to the C++ type R. +
+ For a complete semantic description and rationale, see this page. +
+ The following C++ function applies a Python callable object to its two + arguments and returns the result. If a Python exception is raised or the + result can't be converted to a double, an exception is thrown. +
+double apply2(PyObject* func, double x, double y) +{ + return boost::python::call<double>(func, x, y); +} ++
| + | + |
![]() |
+ <boost/python/call_method.hpp> defines the call_method family of + overloaded function templates, used to invoke callable attributes of Python + objects from C++. +
+template <class R, class A1, class A2, ... class An> +R call_method(PyObject* self, char const* method, A1 const&, A2 const&, ... An const&) ++
+
+ R is a pointer type,
+ reference type, or a complete type with an accessible copy constructor
+
+ Invokes self.method(a1, a2, ...an) in Python, where a1...an
+ are the arguments to call_method(), converted to Python objects. For
+ a complete semantic description, see this page.
+
+ The result of the Python call, converted to the C++ type R.
+
+ call_method is critical
+ to implementing C++ virtual functions which are overridable in Python,
+ as shown by the example below.
+
+ The following C++ illustrates the use of call_method
+ in wrapping a class with a virtual function that can be overridden in Python:
+ C++ Module Definition
+
#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/utility.hpp> +#include <cstring> + +// class to be wrapped +class Base +{ + public: + virtual char const* class_name() const { return "Base"; } + virtual ~Base(); +}; + +bool is_base(Base* b) +{ + return !std::strcmp(b->class_name(), "Base"); +} + +// Wrapper code begins here +using namespace boost::python; + +// Callback class +class Base_callback : public Base +{ + public: + Base_callback(PyObject* self) : m_self(self) {} + + char const* class_name() const { return call_method<char const*>(m_self, "class_name"); } + char const* Base_name() const { return Base::class_name(); } + private: + PyObject* const m_self; +}; + +using namespace boost::python; +BOOST_PYTHON_MODULE(my_module) +{ + def("is_base", is_base); + + class_<Base,Base_callback, noncopyable>("Base") + .def("class_name", &Base_callback::Base_name) + ; + +} ++
+ Python code: +
+>>> from my_module import * +>>> class Derived(Base): +... def __init__(self): +... Base.__init__(self) +... def class_name(self): +... return self.__class__.__name__ +... +>>> is_base(Base()) # calls the class_name() method from C++ +1 +>>> is_base(Derived()) +0 ++
| + | + |
![]() |
+ make_getter()
+ and make_setter()
+ are the functions used internally by class_<>::def_readonly and class_<>::def_readwrite to produce Python
+ callable objects which wrap C++ data members.
+
template <class C, class D> +object make_getter(D C::*pm); + +template <class C, class D, class Policies> +object make_getter(D C::*pm, Policies const& policies); ++
+
+ Policies is a model of CallPolicies.
+
+ Creates a Python callable object which accepts a single argument + that can be converted from_python to C*, and returns the corresponding + member D member of the C object, converted to_python. If policies + is supplied, it will be applied to the function as described here. + Otherwise, the library attempts to determine whether D is a user-defined + class type, and if so uses return_internal_reference<> for + Policies. Note that this test may inappropriately choose return_internal_reference<> + in some cases when D is a smart pointer type. This is a known defect. +
+ An instance of object which holds the new Python callable object. +
template <class D> +object make_getter(D const& d); +template <class D, class Policies> +object make_getter(D const& d, Policies const& policies); + +template <class D> +object make_getter(D const* p); +template <class D, class Policies> +object make_getter(D const* p, Policies const& policies); ++
+
+ Policies is a model of CallPolicies. +
+ Creates a Python callable object which accepts no arguments and returns + d or *p, converted to_python on demand. If policies is supplied, + it will be applied to the function as described here. Otherwise, + the library attempts to determine whether D is a user-defined class + type, and if so uses reference_existing_object for Policies. +
+ An instance of object which holds the new Python callable object. +
template <class C, class D> +object make_setter(D C::*pm); + +template <class C, class D, class Policies> +object make_setter(D C::*pm, Policies const& policies); ++
+
+ Policies is a model of CallPolicies. +
+ Creates a Python callable object which, when called from Python, + expects two arguments which can be converted from_python to C* and + D const&, respectively, and sets the corresponding D member of + the C object. If policies is supplied, it will be applied to the + function as described here. +
+ An instance of object which holds the new Python callable object. +
template <class D> +object make_setter(D& d); +template <class D, class Policies> +object make_setter(D& d, Policies const& policies); + +template <class D> +object make_setter(D* p); +template <class D, class Policies> +object make_setter(D* p, Policies const& policies); ++
+
+ Policies is a model of CallPolicies. +
+ Creates a Python callable object which accepts one argument, which + is converted from Python to D const& and written into d or *p, + respectively. If policies is supplied, it will be applied to the + function as described here. +
+ An instance of object which holds the new Python callable object. +
+ The code below uses make_getter and make_setter to expose a data member + as functions: +
+#include <boost/python/data_members.hpp> +#include <boost/python/module.hpp> +#include <boost/python/class.hpp> + +struct X +{ + X(int x) : y(x) {} + int y; +}; + +using namespace boost::python; + +BOOST_PYTHON_MODULE_INIT(data_members_example) +{ + class_<X>("X", init<int>()) + .def("get", make_getter(&X::y)) + .def("set", make_setter(&X::y)) + ; +} ++
+ It can be used this way in Python: +
+>>> from data_members_example import * +>>> x = X(1) +>>> x.get() +1 +>>> x.set(2) +>>> x.get() +2 ++
| + | + |
![]() |
+ make_function() and make_constructor() are the functions used internally + by def() and class_<>::def() to produce Python callable objects which + wrap C++ functions and member functions. +
+template <class F> +object make_function(F f) + +template <class F, class Policies> +object make_function(F f, Policies const& policies) + +template <class F, class Policies, class KeywordsOrSignature> +object make_function(F f, Policies const& policies, KeywordsOrSignature const& ks) + +template <class F, class Policies, class Keywords, class Signature> +object make_function(F f, Policies const& policies, Keywords const& kw, Signature const& sig) ++
+
+ F is a function pointer or member function pointer type. If policies + are supplied, it must be a model of CallPolicies. If kewords are + supplied, it must be the result of a keyword-expression specifying + no more arguments than the arity of f. +
+ Creates a Python callable object which, when called from Python, + converts its arguments to C++ and calls f. If F is a pointer-to-member-function + type, the target object of the function call (*this) will be taken + from the first Python argument, and subsequent Python arguments will + be used as the arguments to f. +
++ * If policies are supplied, it will be applied to the function as + described here. * If keywords are supplied, the keywords will be + applied in order to the final arguments of the resulting function. + * If Signature is supplied, it should be an instance of an MPL front-extensible + sequence representing the function's return type followed by its + argument types. Pass a Signature when wrapping function object types + whose signatures can't be deduced, or when you wish to override the + types which will be passed to the wrapped function. +
++ An instance of object which holds the new Python callable object. +
+ An argument of pointer type may be 0 if None is passed from Python. + An argument type which is a constant reference may refer to a temporary + which was created from the Python object for just the duration of + the call to the wrapped function, for example a std::vector conjured + up by the conversion process from a Python list. Use a non-const + reference argument when a persistent lvalue is required. +
template <class F> +object make_constructor(F f) + +template <class F, class Policies> +object make_constructor(F f, Policies const& policies) + +template <class F, class Policies, class KeywordsOrSignature> +object make_constructor(F f, Policies const& policies, KeywordsOrSignature const& ks) + +template <class F, class Policies, class Keywords, class Signature> +object make_constructor(F f, Policies const& policies, Keywords const& kw, Signature const& sig) ++
+
+ F is a function pointer type. If policies are supplied, it must be + a model of CallPolicies. If kewords are supplied, it must be the + result of a keyword-expression specifying no more arguments than + the arity of f. +
+ Creates a Python callable object which, when called from Python, + converts its arguments to C++ and calls f. +
+ An instance of object which holds the new Python callable object. +
+ C++ function exposed below returns a callable object wrapping one of two + functions. +
+#include <boost/python/make_function.hpp> +#include <boost/python/module.hpp> + +char const* foo() { return "foo"; } +char const* bar() { return "bar"; } + +using namespace boost::python; +object choose_function(bool selector) +{ + if (selector) + return boost::python::make_function(foo); + else + return boost::python::make_function(bar); +} + +BOOST_PYTHON_MODULE(make_function_test) +{ + def("choose_function", choose_function); +} ++
+ It can be used this way in Python: +
+>>> from make_function_test import * +>>> f = choose_function(1) +>>> g = choose_function(0) +>>> f() +'foo' +>>> g() +'bar' ++
| + | + |
![]() |
+ Defines facilities for generating families of overloaded Python functions + and extension class methods from C++ functions and member functions with + default arguments, or from similar families of C++ overloads +
++ An overload-dispatch-expression is used to describe a family of overloaded + methods to be generated for an extension class. It has the following + properties: +
++
+ An ntbs whose value will bound to the
+ methods' __doc__
+ attribute
+
+ A keyword-expression + which will be used to name (a trailing subsequence of) the arguments + to the generated methods. +
+ An instance of some type which models CallPolicies. +
+ The minimum number of arguments to be accepted by a generated method + overload. +
+ The maximum number of arguments to be accepted by a generated method + overload. +
+ An OverloadDispatcher X is a class which has a minimum arity and a maximum + arity, and for which the following following are valid overload-dispatch-expressions, + with the same minimum and maximum arity as the OverloadDispatcher. +
+X() +X(docstring) +X(docstring, keywords) +X(keywords, docstring) +X()[policies] +X(docstring)[policies] +X(docstring, keywords)[policies] +X(keywords, docstring)[policies] ++
+ * If policies are supplied, it must be an instance of a type which models
+ CallPolicies, and will be
+ used as the result's call policies. Otherwise the result's call policies
+ will be an instance of default_call_policies. * If docstring
+ is supplied it must be an ntbs, and will be
+ used as the result's docstring. Otherwise the result has an empty docstring.
+ * If keywords is supplied it must be the result of a keyword-expression
+ whose length is no greater than X's maximum arity, and will be used as
+ the result's keywords. Otherwise the result's keywords will be empty.
+
BOOST_PYTHON_FUNCTION_OVERLOADS(name, func_id, min_args, max_args) ++
+ Expands to the definition of an OverloadDispatcher called name in the current + scope which can be used to generate the following function invocation: +
+func_id(a1, a2,...ai);+
+ for all min_args <=
+ i <=
+ max_args.
+
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(name, member_name, min_args, max_args) ++
+ Expands to the definition of an OverloadDispatcher called name in the current + scope which can be used to generate the following function invocation: +
+x.member_name(a1, a2,...ai);+
+ for all min_args <= i <= max_args, where x is a reference to an object + of class type. +
+#include <boost/python/module.hpp> +#include <boost/python/def.hpp> +#include <boost/python/args.hpp> +#include <boost/python/tuple.hpp> +#include <boost/python/class.hpp> +#include <boost/python/overloads.hpp> +#include <boost/python/return_internal_reference.hpp> + +using namespace boost::python; + +tuple f(int x = 1, double y = 4.25, char const* z = "wow") +{ + return make_tuple(x, y, z); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(f_overloads, f, 0, 3) + +struct Y {}; +struct X +{ + Y& f(int x, double y = 4.25, char const* z = "wow") + { + return inner; + } + Y inner; +}; + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(f_member_overloads, f, 1, 3) + +BOOST_PYTHON_MODULE(args_ext) +{ + def("f", f, + f_overloads( + args("x", "y", "z"), "This is f's docstring" + )); + + + class_<Y>("Y") + ; + + class_<X>("X", "This is X's docstring") + .def("f1", &X::f, + f_member_overloads( + args("x", "y", "z"), "f's docstring" + )[return_internal_reference<>()] + ) + ; +} ++
| + | + |
![]() |
+ <boost/python/ptr.hpp> defines the ptr() function template, which + allows users to specify how to convert C++ pointer values to python in + the context of implementing overridable virtual functions, invoking Python + callable objects, or explicitly converting C++ objects to Python. Normally, + when passing pointers to Python callbacks, the pointee is copied to ensure + that the Python object never holds a dangling reference. To specify that + the new Python object should merely contain a copy of a pointer p, the + user can pass ptr(p) instead of passing p directly. This interface is meant + to mirror the use of boost::ref(), which can be similarly used to prevent + copying of referents. +
+
+ ptr(p) returns an instance of pointer_wrapper<>,
+ which can be detected using the is_pointer_wrapper<>
+ metafunction; unwrap_pointer<>
+ is a metafunction which extracts the original pointer type from a pointer_wrapper<>.
+ These classes can be thought of as implementation details.
+
template <class T> +pointer_wrapper<T> ptr(T x); ++
+
+ T is a pointer type. +
+ pointer_wrapper<T>(x) +
+ nothing. +
+ A "type envelope" which is returned by ptr(), used to indicate reference semantics
+ for pointers passed to Python callbacks.
+
namespace boost { namespace python +{ + template<class Ptr> class pointer_wrapper + { + public: + typedef Ptr type; + + explicit pointer_wrapper(Ptr x); + operator Ptr() const; + Ptr get() const; + }; +}} ++
typedef Ptr type; ++
+ The type of the pointer being wrapped. +
+explicit pointer_wrapper(Ptr x); ++
+
+ Ptr is a pointer
+ type
+
+ Stores x in a the
+ pointer_wrapper<>.
+
+ nothing. +
operator Ptr() const; +Ptr get() const; ++
+
+ a copy of the stored pointer. +
+ pointer_wrapper is intended to be a stand-in for the actual pointer + type, but sometimes it's better to have an explicit way to retrieve + the pointer. +
+ A unary metafunction whose value is true iff its argument is a pointer_wrapper<>. +
+namespace boost { namespace python +{ + template<class T> class is_pointer_wrapper + { + static unspecified value = ...; + }; +}} ++
+
+ true iff T is a specialization of pointer_wrapper<>.
+ value is an integral constant convertible to bool of unspecified
+ type
+
+ A unary metafunction which extracts the wrapped pointer type from a specialization + of pointer_wrapper<>. +
+namespace boost { namespace python +{ + template<class T> class unwrap_pointer + { + typedef unspecified type; + }; +}} ++
+
+ T::type if T
+ is a specialization of pointer_wrapper<>, T
+ otherwise
+
+ This example illustrates the use of ptr() to prevent an object from being + copied: +
+#include <boost/python/call.hpp> +#include <boost/python/ptr.hpp> + +class expensive_to_copy +{ + ... +}; + +void pass_as_arg(expensive_to_copy* x, PyObject* f) +{ + // call the Python function f, passing a Python object built around + // which refers to *x by-pointer. + // + // *** Note: ensuring that *x outlives the argument to f() is *** + // *** up to the user! Failure to do so could result in a crash! *** + + boost::python::call<void>(f, ptr(x)); +} +... ++
| + | + |
![]() |
+ raw_function(...)
+ is used to convert a function taking a tuple and a dict into a Python callable object
+ which accepts a variable number of arguments and arbitrary keyword arguments.
+
template <class F> +object raw_function(F f, std::size_t min_args = 0); ++
+
+ f(tuple(), dict()) is well-formed. +
+ a callable object which requires at least min_args arguments. When + called, the actual non-keyword arguments will be passed in a tuple + as the first argument to f, and the keyword arguments will be passed + in a dict as the second argument to f. +
+ C++: +
+#include <boost/python/def.hpp> +#include <boost/python/tuple.hpp> +#include <boost/python/dict.hpp> +#include <boost/python/module.hpp> +#include <boost/python/raw_function.hpp> + +using namespace boost::python; + +tuple raw(tuple args, dict kw) +{ + return make_tuple(args, kw); +} + +BOOST_PYTHON_MODULE(raw_test) +{ + def("raw", raw_function(raw)); +} ++
+ Python: +
+>>> from raw_test import * + +>>> raw(3, 4, foo = 'bar', baz = 42) +((3, 4), {'foo': 'bar', 'baz': 42}) ++
| + | + |
![]() |
+ Boost.Python supports docstrings with automatic appending of Pythonic
+ and C++ signatures. This feature is implemented by class function_doc_signature_generator. The
+ class uses all of the overloads, supplied arg names and default values,
+ as well as the user-defined docstrings, to generate documentation for
+ a given function.
+
+ The class has only one public function which returns a list of strings + documenting the overloads of a function. +
+namespace boost { namespace python { namespace objects { + + class function_doc_signature_generator + { + public: + static list function_doc_signatures(function const *f); + }; + +}}} ++
#include <boost/python/module.hpp> +#include <boost/python/def.hpp> +#include <boost/python/args.hpp> +#include <boost/python/tuple.hpp> +#include <boost/python/class.hpp> +#include <boost/python/overloads.hpp> +#include <boost/python/raw_function.hpp> + +using namespace boost::python; + +tuple f(int x = 1, double y = 4.25, char const* z = "wow") +{ + return make_tuple(x, y, z); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(f_overloads, f, 0, 3) + + +struct X +{ + tuple f(int x = 1, double y = 4.25, char const* z = "wow") + { + return make_tuple(x, y, z); + } +}; + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(X_f_overloads, X::f, 0, 3) + +tuple raw_func(tuple args, dict kw) +{ + return make_tuple(args, kw); +} + +BOOST_PYTHON_MODULE(args_ext) +{ + def("f", f, (arg("x")=1, arg("y")=4.25, arg("z")="wow") + , "This is f's docstring" + ); + + def("raw", raw_function(raw_func)); + + def("f1", f, f_overloads("f1's docstring", args("x", "y", "z"))); + + + class_<X>("X", "This is X's docstring", init<>(args("self"))) + .def("f", &X::f + , "This is X.f's docstring" + , args("self","x", "y", "z")) + + ; + +} ++
+ Python code: +
+>>> import args_ext +>>> help(args_ext) +Help on module args_ext: + +NAME + args_ext + +FILE + args_ext.pyd + +CLASSES + Boost.Python.instance(__builtin__.object) + X + + class X(Boost.Python.instance) + | This is X's docstring + | + | Method resolution order: + | X + | Boost.Python.instance + | __builtin__.object + | + | Methods defined here: + | + | __init__(...) + | __init__( (object)self) -> None : + | C++ signature: + | void __init__(struct _object *) + | + | f(...) + | f( (X)self, (int)x, (float)y, (str)z) -> tuple : This is X.f's docstring + | C++ signature: + | class boost::python::tuple f(struct X {lvalue},int,double,char const *) + | + | ................. + | +FUNCTIONS + f(...) + f([ (int)x=1 [, (float)y=4.25 [, (str)z='wow']]]) -> tuple : This is f's docstring + C++ signature: + class boost::python::tuple f([ int=1 [,double=4.25 [,char const *='wow']]]) + + f1(...) + f1([ (int)x [, (float)y [, (str)z]]]) -> tuple : f1's docstring + C++ signature: + class boost::python::tuple f1([ int [,double [,char const *]]]) + + raw(...) + object raw(tuple args, dict kwds) : + C++ signature: + object raw(tuple args, dict kwds) ++
+ To support Pythonic signatures the converters should supply a get_pytype function returning a pointer
+ to the associated PyTypeObject.
+ See for example ResultConverter or to_python_converter. The classes
+ in this header file are meant to be used when implmenting get_pytype. There are also _direct versions of the templates of
+ class T
+ which should be used with undecorated type parameter, expected to be
+ in the conversion registry when the module loads.
+
+ This template generates a static get_pytype
+ member returning the template parameter.
+
namespace boost { namespace python { namespace converter{ + + template < PyTypeObject const *pytype > + class wrap_pytype + { + public: + static PyTypeObject const *get_pytype(){return pytype; } + }; + +}}} ++
+ This template should be used with template parameters which are (possibly
+ decorated) types exported to python using class_. The generated a static
+ get_pytype member returns
+ the corresponding python type.
+
namespace boost { namespace python { namespace converter{ + + template < class T > + class registered_pytype + { + public: + static PyTypeObject const *get_pytype(); + }; + +}}} ++
+ This template generates a static get_pytype
+ member which inspects the registered from_python
+ converters for the type T
+ and returns a matching python type.
+
namespace boost { namespace python { namespace converter{ + + template < class T > + class expected_from_python_type + { + public: + static PyTypeObject const *get_pytype(); + }; + +}}} ++
+ This template generates a static get_pytype
+ member returning the python type to which T
+ can be converted.
+
namespace boost { namespace python { namespace converter{ + + template < class T > + class to_python_target_type + { + public: + static PyTypeObject const *get_pytype(); + }; + +}}} ++
+ This example presumes that someone has implemented the standard noddy
+ example module from the Python documentation, and placed the corresponding
+ declarations in "noddy.h". Because noddy_NoddyObject
+ is the ultimate trivial extension type, the example is a bit contrived:
+ it wraps a function for which all information is contained in the type
+ of its return value.
+
+ C++ module definition: +
+#include <boost/python/reference.hpp> +#include <boost/python/module.hpp> +#include "noddy.h" + +struct tag {}; +tag make_tag() { return tag(); } + +using namespace boost::python; + +struct tag_to_noddy +#if defined BOOST_PYTHON_SUPPORTS_PY_SIGNATURES //unnecessary overhead if py signatures are not supported +: wrap_pytype<&noddy_NoddyType> //inherits get_pytype from wrap_pytype +#endif +{ + static PyObject* convert(tag const& x) + { + return PyObject_New(noddy_NoddyObject, &noddy_NoddyType); + } +}; + +BOOST_PYTHON_MODULE(to_python_converter) +{ + def("make_tag", make_tag); + to_python_converter<tag, tag_to_noddy +#if defined BOOST_PYTHON_SUPPORTS_PY_SIGNATURES //invalid if py signatures are not supported + , true +#endif + >(); //"true" because tag_to_noddy has member get_pytype +} ++
+ The following example registers to and from python converters using the + templates expected_from_python_type and to_pyhton_target_type. +
+#include <boost/python/module.hpp> +#include <boost/python/def.hpp> +#include <boost/python/extract.hpp> +#include <boost/python/to_python_converter.hpp> +#include <boost/python/class.hpp> + +using namespace boost::python; + +struct A +{ +}; + +struct B +{ + A a; + B(const A& a_):a(a_){} +}; + +// Converter from A to python int +struct BToPython +#if defined BOOST_PYTHON_SUPPORTS_PY_SIGNATURES //unnecessary overhead if py signatures are not supported + : converter::to_python_target_type<A> //inherits get_pytype +#endif +{ + static PyObject* convert(const B& b) + { + return incref(object(b.a).ptr()); + } +}; + +// Conversion from python int to A +struct BFromPython +{ + BFromPython() + { + boost::python::converter::registry::push_back + ( &convertible + , &construct + , type_id< B >() +#if defined BOOST_PYTHON_SUPPORTS_PY_SIGNATURES //invalid if py signatures are not supported + , &converter::expected_from_python_type<A>::get_pytype//convertible to A can be converted to B +#endif + ); + } + + static void* convertible(PyObject* obj_ptr) + { + extract<const A&> ex(obj_ptr); + if (!ex.check()) return 0; + return obj_ptr; + } + + static void construct( + PyObject* obj_ptr, + converter::rvalue_from_python_stage1_data* data) + { + void* storage = ( + (converter::rvalue_from_python_storage< B >*)data)-> storage.bytes; + + extract<const A&> ex(obj_ptr); + new (storage) B(ex()); + data->convertible = storage; + } +}; + + +B func(const B& b) { return b ; } + +BOOST_PYTHON_MODULE(pytype_function_ext) +{ + to_python_converter< B , BToPython +#if defined BOOST_PYTHON_SUPPORTS_PY_SIGNATURES //invalid if py signatures are not supported + ,true +#endif + >(); //has get_pytype + BFromPython(); + + class_<A>("A") ; + + def("func", &func); + +} + + + +>>> from pytype_function_ext import * +>>> print func.__doc__ +func( (A)arg1) -> A : + C++ signature: + struct B func(struct B) ++
| + | + |
![]() |
+ default_call_policies
+ is a model of CallPolicies
+ with no precall or postcall behavior and a result_converter which handles by-value
+ returns. Wrapped C++ functions and member functions use
+ default_call_policies unless
+ otherwise specified. You may find it convenient to derive new models
+ of CallPolicies
+ from default_call_policies.
+
namespace boost { namespace python +{ + struct default_call_policies + { + static bool precall(PyObject*); + static PyObject* postcall(PyObject*, PyObject* result); + typedef default_result_converter result_converter; + template <class Sig> struct extract_return_type : mpl::front<Sig>{}; + }; +}} ++
bool precall(PyObject*);+
+
+ true +
+ nothing +
PyObject* postcall(PyObject*, PyObject* result);+
+
+ result +
+ nothing +
+ default_result_converter is a model of ResultConverterGenerator which
+ can be used to wrap C++ functions returning non-pointer types, char const*, and PyObject*, by-value.
+
namespace boost { namespace python +{ + struct default_result_converter + { + template <class T> struct apply; + }; +}} ++
template <class T> struct apply+
+
+ T is not a reference type. If T is a pointer type, T is const char* + or PyObject*. +
+ typedef to_python_value<T const&> type; +
+ This example comes from the Boost.Python implementation itself. Because + the return_value_policy class template does not implement precall or + postcall behavior, its default base class is default_call_policies: +
+template <class Handler, class Base = default_call_policies> +struct return_value_policy : Base +{ + typedef Handler result_converter; +}; ++
+ return_arg and return_self instantiations are models
+ of CallPolicies
+ which return the specified argument parameter (usually *this)
+ of a wrapped (member) function.
+
|
+ + Parameter + + |
+
+ + Requirements + + |
+
+ + Description + + |
+
+ + Default + + |
+
|---|---|---|---|
|
+ + arg_pos + + |
+
+
+ A positive compile-time constant of type |
+
+ + the position of the argument to be returned. + + |
+
+ + 1 + + |
+
|
+ + Base + + |
+
+
+ A model of |
+
+
+ Used for policy composition. Any |
+
+ + default_call_policies + + |
+
namespace boost { namespace python +{ + template <size_t arg_pos=1, class Base = default_call_policies> + struct return_arg : Base + { + static PyObject* postcall(PyObject*, PyObject* result); + struct result_converter{ template <class T> struct apply; }; + template <class Sig> struct extract_return_type : mpl::at_c<Sig, arg_pos>{}; + + }; +}} ++
PyObject* postcall(PyObject* args, PyObject* result);+
+
+ PyTuple_Check(args)
+ != 0
+ and PyTuple_Size(args) != 0
+
+ PyTuple_GetItem(args,arg_pos-1) +
namespace boost { namespace python +{ + template <class Base = default_call_policies> + struct return_self + : return_arg<1,Base> + {}; +}} ++
+ C++ module definition: +
+#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/return_arg.hpp> + +struct Widget +{ + Widget() :sensitive_(true){} + bool get_sensitive() const { return sensitive_; } + void set_sensitive(bool s) { this->sensitive_ = s; } + private: + bool sensitive_; +}; + +struct Label : Widget +{ + Label() {} + + std::string get_label() const { return label_; } + void set_label(const std::string &l){ label_ = l; } + + private: + std::string label_; +}; + +using namespace boost::python; +BOOST_PYTHON_MODULE(return_self_ext) +{ + class_<widget>("Widget") + .def("sensitive", &Widget::get_sensitive) + .def("sensitive", &Widget::set_sensitive, return_self<>()) + ; + + class_<Label, bases<Widget> >("Label") + .def("label", &Label::get_label) + .def("label", &Label::set_label, return_self<>()) + ; +} ++
+ Python code: +
+>>> from return_self_ext import * +>>> l1 = Label().label("foo").sensitive(false) +>>> l2 = Label().sensitive(false).label("foo") ++
+ return_internal_reference
+ instantiations are models of CallPolicies which allow pointers
+ and references to objects held internally by a free or member function
+ argument or from the target of a member function to be returned safely
+ without making a copy of the referent. The default for its first template
+ argument handles the common case where the containing object is the target
+ (*this)
+ of a wrapped member function.
+
|
+ + Parameter + + |
+
+ + Requirements + + |
+
+ + Description + + |
+
+ + Default + + |
+
|---|---|---|---|
|
+ + owner_arg + + |
+
+
+ A positive compile-time constant of type |
+
+
+ The index of the parameter which contains the object to which
+ the reference or pointer is being returned. If used to wrap
+ a member function, parameter 1 is the target object ( |
++ | +
|
+ + Base + + |
+
+
+ A model of |
+
+
+ Used for policy composition. Any |
+
+ + default_call_policies + + |
+
namespace boost { namespace python +{ + template <std::size_t owner_arg = 1, class Base = default_call_policies> + struct return_internal_reference : Base + { + static PyObject* postcall(PyObject*, PyObject* result); + typedef reference_existing_object result_converter; + }; +}} ++
PyObject* postcall(PyObject* args, PyObject* result);+
+
+ PyTuple_Check(args)
+ != 0
+
+ with_custodian_and_ward_postcall::postcall(args, result)
+
+ C++ module definition: +
+#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/return_internal_reference.hpp> + +class Bar +{ + public: + Bar(int x) : x(x) {} + int get_x() const { return x; } + void set_x(int x) { this->x = x; } + private: + int x; +}; + +class Foo +{ + public: + Foo(int x) : b(x) {} + + // Returns an internal reference + Bar const& get_bar() const { return b; } + + private: + Bar b; +}; + +using namespace boost::python; +BOOST_PYTHON_MODULE(internal_refs) +{ + class_<Bar>("Bar", init<int>()) + .def("get_x", &Bar::get_x) + .def("set_x", &Bar::set_x) + ; + + class_<Foo>("Foo", init<int>()) + .def("get_bar", &Foo::get_bar + , return_internal_reference<>()) + ; +} ++
+ Python code: +
+>>> from internal_refs import * +>>> f = Foo(3) +>>> b1 = f.get_bar() +>>> b2 = f.get_bar() +>>> b1.get_x() +3 +>>> b2.get_x() +3 +>>> b1.set_x(42) +>>> b2.get_x() +42 ++
+ return_value_policy instantiations are simply models of CallPolicies which are composed
+ of a ResultConverterGenerator and
+ optional Base CallPolicies.
+
|
+ + Parameter + + |
+
+ + Requirements + + |
+
+ + Default + + |
+
|---|---|---|
|
+ + ResultConverterGenerator + + |
+
+
+ A model of |
++ | +
|
+ + Base + + |
+
+
+ A model of |
+
+ + default_call_policies + + |
+
namespace boost { namespace python +{ + template <class ResultConverterGenerator, class Base = default_call_policies> + struct return_value_policy : Base + { + typedef ResultConverterGenerator result_converter; + }; +}} ++
+ C++ module definition: +
+#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/copy_const_reference.hpp> +#include <boost/python/return_value_policy.hpp> + +// classes to wrap +struct Bar { int x; } + +struct Foo { + Foo(int x) : { b.x = x; } + Bar const& get_bar() const { return b; } + private: + Bar b; +}; + +// Wrapper code +using namespace boost::python; +BOOST_PYTHON_MODULE(my_module) +{ + class_<Bar>("Bar"); + + class_<Foo>("Foo", init<int>()) + .def("get_bar", &Foo::get_bar + , return_value_policy<copy_const_reference>()) + ; +} ++
+ Python code: +
+>>> from my_module import * +>>> f = Foo(3) # create a Foo object +>>> b = f.get_bar() # make a copy of the internal Bar object ++
+ This header provides facilities for establishing a lifetime dependency
+ between two of a function's Python argument or result objects. The ward
+ object will not be destroyed until after the custodian as long as the
+ custodian object supports weak
+ references (Boost.Python extension classes all support weak references).
+ If the custodian object does not support weak references and is not
+ None, an appropriate
+ exception will be thrown. The two class templates with_custodian_and_ward
+ and with_custodian_and_ward_postcall
+ differ in the point at which they take effect.
+
+ In order to reduce the chance of inadvertently creating dangling pointers,
+ the default is to do lifetime binding before the underlying C++ object
+ is invoked. However, before invocation the result object is not available,
+ so with_custodian_and_ward_postcall
+ is provided to bind lifetimes after invocation. Also, if a C++ exception
+ is thrown after with_custodian_and_ward<>::precall
+ but before the underlying C++ object actually stores a pointer, the lifetime
+ of the custodian and ward objects will be artificially bound together,
+ so one might choose with_custodian_and_ward_postcall
+ instead, depending on the semantics of the function being wrapped.
+
+ Please note that this is not the appropriate tool to use when wrapping + functions which transfer ownership of a raw pointer across the function-call + boundary. Please see the FAQ if you want to do that. +
+|
+ + Parameter + + |
+
+ + Requirements + + |
+
+ + Description + + |
+
+ + Default + + |
+
|---|---|---|---|
|
+ + custodian + + |
+
+
+ A positive compile-time constant of |
+
+
+ The 1-based index of the parameter which is the dependency
+ in the lifetime relationship to be established. If used to
+ wrap a member function, parameter 1 is the target object ( |
++ | +
|
+ + ward + + |
+
+
+ A positive compile-time constant of type |
+
+
+ The 1-based index of the parameter which is the dependent in
+ the lifetime relationship to be established. If used to wrap
+ a member function, parameter 1 is the target object ( |
++ | +
|
+ + Base + + |
+
+
+ A model of |
+
+ + Used for policy composition. + + |
+
+ + default_call_policies + + |
+
namespace boost { namespace python +{ + template <std::size_t custodian, std::size_t ward, class Base = default_call_policies> + struct with_custodian_and_ward : Base + { + static bool precall(PyObject* args); + }; +}}+
bool precall(PyObject* args);+
+
+ PyTuple_Check(args)
+ != 0
+
+ Makes the lifetime of the argument indicated by ward dependent + on the lifetime of the argument indicated by custodian. +
+ false and PyErr_Occurred() != 0 upon failure, true otherwise. +
|
+ + Parameter + + |
+
+ + Requirements + + |
+
+ + Description + + |
+
+ + Default + + |
+
|---|---|---|---|
|
+ + custodian + + |
+
+
+ A positive compile-time constant of type |
+
+
+ The index of the parameter which is the dependency in the lifetime
+ relationship to be established. Zero indicates the result object;
+ 1 indicates the first argument. If used to wrap a member function,
+ parameter 1 is the target object ( |
++ | +
|
+ + ward + + |
+
+
+ A positive compile-time constant of type |
+
+
+ The index of the parameter which is the dependent in the lifetime
+ relationship to be established. Zero indicates the result object;
+ 1 indicates the first argument. If used to wrap a member function,
+ parameter 1 is the target object ( |
++ | +
|
+ + Base + + |
+
+
+ A model of |
+
+ + Used for policy composition. + + |
+
+ + default_call_policies + + |
+
namespace boost { namespace python +{ + template <std::size_t custodian, std::size_t ward, class Base = default_call_policies> + struct with_custodian_and_ward_postcall : Base + { + static PyObject* postcall(PyObject* args, PyObject* result); + }; +}} ++
PyObject *postcall(PyObject* args, PyObject* result);+
+
+ PyTuple_Check(args)
+ != 0,
+ result !=
+ 0
+
+ Makes the lifetime of the object indicated by ward dependent on + the lifetime of the object indicated by custodian. +
+ 0 and PyErr_Occurred()
+ != 0
+ upon failure, true
+ otherwise.
+
+ The following example shows how with_custodian_and_ward_postcall
+ is used by the library to implement return_internal_reference
+
template <std::size_t owner_arg = 1, class Base = default_call_policies> +struct return_internal_reference + : with_custodian_and_ward_postcall<0, owner_arg, Base> +{ + typedef reference_existing_object result_converter; +}; ++
| + | + |
![]() |
<boost/python/to_python_indirect.hpp> supplies a way to construct new Python objects that hold wrapped C++ class instances via a pointer or smart pointer. +[endsect] ++
+ Class template to_python_indirect
+ converts objects of its first argument type to python as extension
+ class instances, using the ownership policy provided by its 2nd argument.
+
|
+ + Parameter + + |
+
+ + Requirements + + |
+
+ + Description + + |
+
|---|---|---|
|
+ + T + + |
+
+
+ Either |
+
+
+ |
+
|
+ + MakeHolder + + |
+
+
+ |
+
+
+ A class whose static |
+
+ Instantiations of to_python_indirect are models of ResultConverter.
+
namespace boost { namespace python +{ + template <class T, class MakeHolder> + struct to_python_indirect + { + static bool convertible(); + PyObject* operator()(T ptr_or_reference) const; + private: + static PyTypeObject* type(); + }; +}} ++
PyObject* operator()(T x) const;+
+
+ x refers to an
+ object (if it is a pointer type, it is non-null). convertible()
+ == true.
+
+ Creates an appropriately-typed Boost.Python extension class instance, + uses MakeHolder to create an instance_holder from x, installs + the instance_holder in the new extension class instance, and + returns a pointer to it. +
bool convertible()+
+
+ Returns true iff any module has registered a Python type corresponding + to U. +
+ This example replicates the functionality of reference_existing_object, but
+ without some of the compile-time error checking.
+
struct make_reference_holder +{ + typedef boost::python::objects::instance_holder* result_type; + template <class T> + static result_type execute(T* p) + { + return new boost::python::objects::pointer_holder<T*, T>(p); + } +}; + +struct reference_existing_object +{ + // metafunction returning the ResultConverter + template <class T> + struct apply + { + typedef boost::python::to_python_indirect<T,make_reference_holder> type; + }; +}; ++
+ to_python_value is a
+ model of ResultConverter
+ which copies its argument into a new Python object.
+
namespace boost { namespace python +{ + template <class T> + struct to_python_value + { + typedef typename add_reference< + typename add_const<T>::type + >::type argument_type; + + static bool convertible(); + PyObject* operator()(argument_type) const; + }; +}} ++
static bool convertible();+
+
+ true iff a converter
+ has been registered which can convert T
+ to python by-value.
+
PyObject* operator()(argument_type x) const;+
+
+ convertible()
+ == true
+
+ converts x to python
+
+ the resulting Python object iff a converter for T
+ has been registered, 0
+ otherwise.
+
| + | + |
![]() |
+ copy_const_reference
+ is a model of ResultConverterGenerator
+ which can be used to wrap C++ functions returning a reference-to-const
+ type such that the referenced value is copied into a new Python object.
+
namespace boost { namespace python +{ + struct copy_const_reference + { + template <class T> struct apply; + }; +}} ++
template <class T> struct apply+
+
+ T is U const& for some U.
+
+ typedef to_python_value<T> type;
+
+ C++ module definition: +
+#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/copy_const_reference.hpp> +#include <boost/python/return_value_policy.hpp> + +// classes to wrap +struct Bar { int x; } + +struct Foo { + Foo(int x) : { b.x = x; } + Bar const& get_bar() const { return b; } + private: + Bar b; +}; + +// Wrapper code +using namespace boost::python; +BOOST_PYTHON_MODULE(my_module) +{ + class_<Bar>("Bar"); + + class_<Foo>("Foo", init<int>()) + .def("get_bar", &Foo::get_bar + , return_value_policy<copy_const_reference>()) + ; +} ++
+ Python code: +
+>>> from my_module import * +>>> f = Foo(3) # create a Foo object +>>> b = f.get_bar() # make a copy of the internal Bar object ++
+ copy_non_const_reference
+ is a model of ResultConverterGenerator
+ which can be used to wrap C++ functions returning a reference-to-non-const
+ type such that the referenced value is copied into a new Python object.
+
namespace boost { namespace python +{ + struct copy_non_const_reference + { + template <class T> struct apply; + }; +}} ++
template <class T> struct apply+
+
+ T is U &
+ for some non-const U.
+
+ typedef to_python_value<T> type;
+
+ C++ module definition: +
+#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/copy_non_const_reference.hpp> +#include <boost/python/return_value_policy.hpp> + +// classes to wrap +struct Bar { int x; } + +struct Foo { + Foo(int x) : { b.x = x; } + Bar& get_bar() { return b; } + private: + Bar b; +}; + +// Wrapper code +using namespace boost::python; +BOOST_PYTHON_MODULE(my_module) +{ + class_<Bar>("Bar"); + + class_<Foo>("Foo", init<int>()) + .def("get_bar", &Foo::get_bar + , return_value_policy<copy_non_const_reference>()) + ; +} ++
+ Python code: +
+>>> from my_module import * +>>> f = Foo(3) # create a Foo object +>>> b = f.get_bar() # make a copy of the internal Bar object ++
+ manage_new_object is
+ a model of ResultConverterGenerator
+ which can be used to wrap C++ functions which return a pointer to an
+ object allocated with a new-expression, and expect the caller to take
+ responsibility for deleting that object.
+
namespace boost { namespace python +{ + struct manage_new_object + { + template <class T> struct apply; + }; +}} ++
template <class T> struct apply+
+
+ T is U*
+ for some U.
+
+ typedef to_python_indirect<T> type;
+
+ In C++: +
+#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/manage_new_object.hpp> +#include <boost/python/return_value_policy.hpp> + + +struct Foo { + Foo(int x) : x(x){} + int get_x() { return x; } + int x; +}; + +Foo* make_foo(int x) { return new Foo(x); } + +// Wrapper code +using namespace boost::python; +BOOST_PYTHON_MODULE(my_module) +{ + def("make_foo", make_foo, return_value_policy<manage_new_object>()) + class_<Foo>("Foo") + .def("get_x", &Foo::get_x) + ; +} ++
+ Python code: +
+>>> from my_module import * +>>> f = make_foo(3) # create a Foo object +>>> f.get_x() +3 ++
+ reference_existing_object
+ is a model of ResultConverterGenerator
+ which can be used to wrap C++ functions which return a reference or pointer
+ to a C++ object. When the wrapped function is called, the value referenced
+ by its return value is not copied. A new Python object is created which
+ contains a pointer to the referent, and no attempt is made to ensure
+ that the lifetime of the referent is at least as long as that of the
+ corresponding Python object. Thus, it can be highly
+ dangerous to use reference_existing_object
+ without additional lifetime management from such models of CallPolicies
+ as with_custodian_and_ward. This
+ class is used in the implementation of return_internal_reference.
+
namespace boost { namespace python +{ + struct reference_existing_object + { + template <class T> struct apply; + }; +}} ++
template <class T> struct apply+
+
+ T is U&
+ or U*
+ for some U.
+
+ typedef to_python_indirect<T, V> type;, where V is a class whose static
+ execute function constructs an instance holder containing an unowned
+ U*
+ pointing to the referent of the wrapped function's return value.
+
+ In C++: +
+#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/reference_existing_object.hpp> +#include <boost/python/return_value_policy.hpp> +#include <utility> + +// classes to wrap +struct Singleton +{ + Singleton() : x(0) {} + + int exchange(int n) // set x and return the old value + { + std::swap(n, x); + return n; + } + + int x; +}; + +Singleton& get_it() +{ + static Singleton just_one; + return just_one; +} + +// Wrapper code +using namespace boost::python; +BOOST_PYTHON_MODULE(singleton) +{ + def("get_it", get_it, + return_value_policy<reference_existing_object>()); + + class_<Singleton>("Singleton") + .def("exchange", &Singleton::exchange) + ; +} ++
+ Python code: +
+>>> import singleton +>>> s1 = singleton.get_it() +>>> s2 = singleton.get_it() +>>> id(s1) == id(s2) # s1 and s2 are not the same object +0 +>>> s1.exchange(42) # but they reference the same C++ Singleton +0 +>>> s2.exchange(99) +42 ++
+ return_by_value is a
+ model of ResultConverterGenerator
+ which can be used to wrap C++ functions returning any reference or value
+ type such that the return value is copied into a new Python object.
+
namespace boost { namespace python +{ + struct return_by_value + { + template <class T> struct apply; + }; +}} ++
template <class T> struct apply+
+
+ typedef to_python_value<T> type;
+
+ In C++: +
+#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/return_by_value.hpp> +#include <boost/python/return_value_policy.hpp> + +// classes to wrap +struct Bar { }; + +Bar global_bar; + +// functions to wrap: +Bar b1(); +Bar& b2(); +Bar const& b3(); + +// Wrapper code +using namespace boost::python; +template <class R> +void def_void_function(char const* name, R (*f)()) +{ + def(name, f, return_value_policy<return_by_value>()); +} + +BOOST_PYTHON_MODULE(my_module) +{ + class_<Bar>("Bar"); + def_void_function("b1", b1); + def_void_function("b2", b2); + def_void_function("b3", b3); +} ++
+ Python code: +
+>>> from my_module import * +>>> b = b1() # each of these calls +>>> b = b2() # creates a brand +>>> b = b3() # new Bar object ++
+ return_opaque_pointer is a model of ResultConverterGenerator + which can be used to wrap C++ functions returning pointers to undefined + types such that the return value is copied into a new Python object. +
+
+ In addition to specifying the return_opaque_pointer
+ policy the BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID
+ macro must be used to define specializations for the type_id function on the type
+ pointed to by returned pointer.
+
namespace boost { namespace python +{ + struct return_opaque_pointer + { + template <class R> struct apply; + }; +}} ++
template <class T> struct apply+
+
+ detail::opaque_conversion_holder<R>
+ type;
+
+ In C++: +
+# include <boost/python/return_opaque_pointer.hpp> +# include <boost/python/def.hpp> +# include <boost/python/module.hpp> +# include <boost/python/return_value_policy.hpp> + +typedef struct opaque_ *opaque; + +opaque the_op = ((opaque) 0x47110815); + +opaque get () { return the_op; } +void use (opaque op) { + if (op != the_op) + throw std::runtime_error (std::string ("failed")); +} + +void failuse (opaque op) { + if (op == the_op) + throw std::runtime_error (std::string ("success")); +} + +BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID(opaque_) + +namespace bpl = boost::python; + +BOOST_PYTHON_MODULE(opaque_ext) +{ + bpl::def ( + "get", &::get, bpl::return_value_policy<bpl::return_opaque_pointer>()); + bpl::def ("use", &::use); + bpl::def ("failuse", &::failuse); +} ++
+ Python code: +
+""" +>>> from opaque_ext import * +>>> # +>>> # Check for correct conversion +>>> use(get()) +>>> failuse(get()) +Traceback (most recent call last): + ... +RuntimeError: success +>>> # +>>> # Check that there is no conversion from integers ... +>>> use(0) +Traceback (most recent call last): + ... +TypeError: bad argument type for built-in operation +>>> # +>>> # ... and from strings to opaque objects +>>> use("") +Traceback (most recent call last): + ... +TypeError: bad argument type for built-in operation +""" +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print "running..." + import sys + sys.exit(run()[0]) ++
| + | + |
![]() |
+
+ The number of argumnts accepted by a function or member function. Unless
+ otherwise specified, the hidden this
+ argument to member functions is not counted when specifying arity.
+
+ Null-Terminated Byte String, or 'C'-string. C++ string literals are + ntbses. An ntbs + must never be null. +
+ Exceptions in Python are "raised", not "thrown",
+ as they are in C++. When this documentation says that some Python exception
+ is "raised" in the context of C++ code, it means that the corresponding
+ Python exception is set via the Python/'C'
+ API, and throw_error_already_set() is called.
+
+ A technical term from the C++ standard. Short for "Plain Ol'Data": + A POD-struct is an aggregate class that has no non-static data members + of type pointer to member, non-POD-struct, non-POD-union (or array of + such types) or reference, and has no user-defined copy assign- ment operator + and no user-defined destructor. Similarly, a POD-union is an aggregate + union that has no non-static data members of type pointer to member, + non-POD-struct, non-POD-union (or array of such types) or reference, + and has no user-defined copy assignment operator and no user-defined + destructor. A POD class is a class that is either a POD-struct or a POD-union. + An aggregate is an array or a class (clause 9) with no user-declared + constructors (12.1), no private or protected non-static data members + (clause 11), no base classes (clause 10), and no virtual functions (10.3). +
+ The "One Definition Rule", which says that any entity in a + C++ program must have the same definition in all translation units (object + files) which make up a program. +
| + | + |
![]() |
Table of Contents
+
+ <boost/python/class.hpp>
+ defines the interface through which users expose their C++ classes to Python.
+ It declares the class_
+ class template, which is parameterized on the class type being exposed.
+ It also exposes the init,
+ optional and bases utility class templates, which
+ are used in conjunction with class_.
+
+ <boost/python/class_fwd.hpp> contains a forward declaration of the
+ class_ class template.
+
+ Creates a Python class associated with the C++ type passed as its first + parameter. Although it has four template parameters, only the first one + is required. The three optional arguments can actually be supplied in any order; Boost.Python determines the role of + the argument from its type. +
+|
+ + Template Parameter + + |
+
+ + Requirements + + |
+
+ + Semantics + + |
+
+ + Default + + |
+
|---|---|---|---|
|
+
+ |
+
+ + A class type. + + |
+
+ + The class being wrapped + + |
++ | +
|
+ + Bases + + |
+
+
+ A specialization of bases<...>
+ which specifies previously-exposed C++ base classes of |
+
+
+ Registers |
+
+ + bases<> + + |
+
|
+ + HeldType + + |
+
+
+ Must be |
+
+
+ Specifies the type that is actually embedded in a Python object
+ wrapping a |
+
+
+ |
+
|
+ + NonCopyable + + |
+
+
+ If supplied, must be |
+
+
+ Suppresses automatic registration of |
+
+ + An unspecified type other than boost::noncopyable. + + |
+
T,
+ its exposed constructor(s) must accept an initial PyObject* argument which refers back to the
+ Python object that contains the HeldType instance, as shown in this
+ example. This argument is not included in the init-expression
+ passed to def(init_expr),
+ below, nor is it passed explicitly by users when Python instances
+ of T are created.
+ This idiom allows C++ virtual functions which will be overridden
+ in Python to access the Python object so the Python method can be
+ invoked. Boost.Python automatically registers additional converters
+ which allow wrapped instances of T
+ to be passed to wrapped C++ functions expecting HeldType arguments.
+ T to be passed in place of HeldType
+ arguments, specifying a smart pointer for HeldType allows users to
+ pass Python T instances
+ where a smart pointer-to-T is expected. Smart pointers such as std::auto_ptr<>
+ or boost::shared_ptr<>
+ which contain a nested type element_type
+ designating the referent type are automatically supported; additional
+ smart pointer types can be supported by specializing pointee<HeldType>.
+ T, the initial
+ PyObject*
+ argument must be supplied by all of HeldType's exposed constructors.
+ PyObject* argument by specializing has_back_reference<T>.
+ namespace boost { namespace python +{ +template <class T + , class Bases = bases<> + , class HeldType = T + , class NonCopyable = unspecified + > +class class_ : public object +{ + // Constructors with default __init__ + class_(char const* name); + class_(char const* name, char const* docstring); + + // Constructors, specifying non-default __init__ + template <class Init> + class_(char const* name, Init); + template <class Init> + class_(char const* name, char const* docstring, Init); + + // Exposing additional __init__ functions + template <class Init> + class_& def(Init); + + // defining methods + template <class F> + class_& def(char const* name, F f); + template <class Fn, class A1> + class_& def(char const* name, Fn fn, A1 const&); + template <class Fn, class A1, class A2> + class_& def(char const* name, Fn fn, A1 const&, A2 const&); + template <class Fn, class A1, class A2, class A3> + class_& def(char const* name, Fn fn, A1 const&, A2 const&, A3 const&); + + // declaring method as static + class_& staticmethod(char const* name); + + // exposing operators + template <unspecified> + class_& def(detail::operator_<unspecified>); + + // Raw attribute modification + template <class U> + class_& setattr(char const* name, U const&); + + // exposing data members + template <class D> + class_& def_readonly(char const* name, D T::*pm); + + template <class D> + class_& def_readwrite(char const* name, D T::*pm); + + // exposing static data members + template <class D> + class_& def_readonly(char const* name, D const& d); + template <class D> + class_& def_readwrite(char const* name, D& d); + + // property creation + template <class Get> + void add_property(char const* name, Get const& fget, char const* doc=0); + template <class Get, class Set> + void add_property( + char const* name, Get const& fget, Set const& fset, char const* doc=0); + + template <class Get> + void add_static_property(char const* name, Get const& fget); + template <class Get, class Set> + void add_static_property(char const* name, Get const& fget, Set const& fset); + + // pickle support + template <typename PickleSuite> + self& def_pickle(PickleSuite const&); + self& enable_pickling(); +}; +}} ++
class_(char const* name); +class_(char const* name, char const* docstring); +template <class Init> +class_(char const* name, Init init_spec); +template <class Init> +class_(char const* name, char const* docstring, Init init_spec); ++
+
+ name is an ntbs which conforms to Python's
+ identifier
+ naming rules. If docstring is supplied, it must be an
+ ntbs. If init_spec
+ is supplied, it must be either the special enumeration constant
+ no_init or an
+ init-expression
+ compatible with T.
+
+ Constructs a class_
+ object holding a Boost.Python extension class named name. The named
+ attribute of the current
+ scope is bound to the new extension class.
+
+ * If supplied, the value of docstring is bound to the __doc__ attribute of the extension
+ class. * If init_spec
+ is no_init, a special
+ __init__ function
+ is generated which always raises a Python exception. Otherwise,
+ this->def(init_spec)
+ is called. * If init_spec
+ is not supplied, this->def(init<>()) is called.
+
+ Allowing the user to specify constructor arguments in the class_<>
+ constructor helps her to avoid the common run-time errors which
+ result from invoking wrapped member functions without having exposed
+ an __init__ function
+ which creates the requisite T
+ instance. Types which are not default-constructible will cause
+ a compile-time error unless Init
+ is supplied. The user must always supply name as there is currently
+ no portable method to derive the text of the class name from its
+ type.
+
template <class Init> +class_& def(Init init_expr); ++
+
+ init_expr is the
+ result of an init-expression
+ compatible with T.
+
+ For each valid
+ prefix P
+ of Init, adds an
+ __init__(...)
+ function overload to the extension class accepting P as arguments.
+ Each overload generated constructs an object of HeldType according
+ to the semantics described above, using a copy of init_expr's call
+ policies. If the longest valid
+ prefix of Init contains N types and init_expr holds M keywords,
+ an initial sequence of the keywords are used for all but the first
+ N - M arguments of each overload.
+
+ *this
+
+ Allows users to easily expose a class' constructor to Python. +
template <class F> +class_& def(char const* name, Fn fn); +template <class Fn, class A1> +class_& def(char const* name, Fn fn, A1 const& a1); +template <class Fn, class A1, class A2> +class_& def(char const* name, Fn fn, A1 const& a1, A2 const& a2); +template <class Fn, class A1, class A2, class A3> +class_& def(char const* name, Fn fn, A1 const& a1, A2 const& a2, A3 const& a3); ++
+
+ name is an ntbs which conforms to Python's + identifier + naming rules. * If a1 is the result of an overload-dispatch-expression, + only the second form is allowed and fn must be a pointer to function + or pointer to member function whose arity + is the same as A1's maximum + arity. +
+
+ Effects: For each prefix P of Fn's
+ sequence of argument types, beginning with the one whose length
+ is A1's minimum
+ arity, adds a name(...) method overload to the extension
+ class. Each overload generated invokes a1's call-expression with
+ P, using a copy
+ of a1's call policies. If the longest valid prefix of A1 contains N
+ types and a1 holds M
+ keywords, an initial sequence of the keywords are used for all
+ but the first N - M
+ arguments of each overload.
+
+ * Otherwise, a single method overload is built around fn, which + must not be null: +
++ * If fn is a function pointer, its first argument must be of the + form U, U cv&, U cv*, or U cv* const&, where T* is convertible + to U*, and a1-a3, if supplied, may be selected in any order from + the table below. * Otherwise, if fn is a member function pointer, + its target must be T or one of its public base classes, and a1-a3, + if supplied, may be selected in any order from the table below. + * Otherwise, Fn must be [derived from] object, + and a1-a2, if supplied, may be selcted in any order from the first + two rows of the table below. To be useful, fn should be callable. +
+|
+ + Mnemonic Name + + |
+
+ + Requirements/Type properties + + |
+
+ + Effects + + |
+
|---|---|---|
|
+ + docstring + + |
+
+ + Any ntbs + + |
+
+ + Value will be bound to the __doc__ attribute of the resulting + method overload. If an earlier overload supplied a docstring, + two newline characters and the new docstring are appended + to it. + + |
+
|
+ + policies + + |
+
+ + A model of CallPolicies + + |
+
+ + A copy will be used as the call policies of the resulting + method overload. + + |
+
|
+ + keywords + + |
+
+ + The result of a keyword-expression + specifying no more arguments than the arity + of fn. + + |
+
+ + A copy will be used as the call policies of the resulting + method overload. + + |
+
+ *this
+
class_& staticmethod(char const* name);+
+
+ name is an ntbs which conforms to Python's + identifier + naming rules, and corresponds to a method whose overloads + have all been defined. +
+ Replaces the existing named attribute x
+ with the result of invoking staticmethod(x) in Python. Specifies that the
+ corresponding method is static and therefore no object instance
+ will be passed to it. This is equivalent to the Python statement:
+
setattr(self, name, staticmethod(getattr(self, name)))+
+
+ Attempting to invoke def(name,...) after invoking staticmethod(name) + will raise a RuntimeError. +
+ *this
+
template <unspecified> +class_& def(detail::operator_<unspecified>); ++
+
+ Adds a Python special + method as described here. +
+ *this
+
template <class U> +class_& setattr(char const* name, U const& u); ++
+
+ name is an ntbs which conforms to Python's + identifier + naming rules. +
+ Converts u to Python
+ and adds it to the attribute dictionary of the extension class:
+
PyObject_SetAttrString(this->ptr(), name, object(u).ptr());+
+ *this
+
template <class Get> +void add_property(char const* name, Get const& fget, char const* doc=0); +template <class Get, class Set> +void add_property( + char const* name, Get const& fget, Set const& fset, char const* doc=0); ++
+
+ name is an ntbs which conform to Python's + identifier + naming rules. +
+ Creates a new Python property
+ class instance, passing object(fget) (and object(fset) in the second form) with an (optional)
+ docstring doc to
+ its constructor, then adds that property to the Python class object
+ under construction with the given attribute name.
+
+ *this
+
+ Allows users to easily expose functions that can be invoked from + Python with attribute access syntax. +
template <class Get> +void add_static_property(char const* name, Get const& fget); +template <class Get, class Set> +void add_static_property(char const* name, Get const& fget, Set const& fset); ++
+
+ name is an ntbs which conforms to Python's + identifier + naming rules. +
+ Creates a Boost.Python.StaticProperty object, passing object(fget)
+ (and object(fset)
+ in the second form) to its constructor, then adds that property
+ to the Python class under construction with the given attribute
+ name. StaticProperty is a special subclass of Python's property
+ class which can be called without an initial self argument.
+
+ *this
+
+ Allows users to easily expose functions that can be invoked from + Python with static attribute access syntax. +
template <class D> +class_& def_readonly(char const* name, D T::*pm, char const* doc=0); +template <class D> +class_& def_readonly(char const* name, D const& d); ++
+
+ name is an ntbs which conforms to Python's
+ identifier
+ naming rules. doc
+ is also an ntbs.
+
this->add_property(name, make_getter(pm), doc);+
+ and +
+this->add_static_property(name, make_getter(d));+
+ respectively. +
+
+ *this
+
+ Allows users to easily expose a class' data member or free variable + such that it can be inspected from Python with a natural syntax. +
template <class D> +class_& def_readwrite(char const* name, D T::*pm, char const* doc=0); +template <class D> +class_& def_readwrite(char const* name, D& d); ++
+
this->add_property(name, make_getter(pm), make_setter(pm), doc);+
+ and +
+this->add_static_property(name, make_getter(d), make_setter(d));+
+ respectively. +
+
+ *this
+
+ Allows users to easily expose a class' data or free variable member + such that it can be inspected and set from Python with a natural + syntax. +
template <typename PickleSuite> +class_& def_pickle(PickleSuite const&); ++
+
+ PickleSuite must be publically derived from pickle_suite. +
+ Defines a legal combination of the special attributes and methods: + __getinitargs__, __getstate__, __setstate__, __getstate_manages_dict__, + __safe_for_unpickling__, __reduce__ +
+ *this
+
+ Provides an easy + to use high-level interface for establishing complete pickle support + for the wrapped class. The user is protected by compile-time consistency + checks. +
class_& enable_pickling();+
+
+ Defines the __reduce__ method and the __safe_for_unpickling__ attribute. +
+ *this
+
+ Light-weight alternative to def_pickle(). Enables implementation + of pickle support from Python. +
+ An MPL sequence which can be used in class_<...> instantiations indicate + a list of base classes. +
+namespace boost { namespace python +{ + template <T1 = unspecified,...Tn = unspecified> + struct bases + {}; +}} ++
+ Given a C++ class declaration: +
+class Foo : public Bar, public Baz +{ + public: + Foo(int x, char const* y); + Foo(double); + + std::string const& name() { return m_name; } + void name(char const*); + + double value; // public data + private: + ... +}; ++
+ A corresponding Boost.Python extension class can be created with: +
+using namespace boost::python; + +class_<Foo,bases<Bar,Baz> >("Foo", + "This is Foo's docstring." + "It describes our Foo extension class", + + init<int,char const*>(args("x","y"), "__init__ docstring") + ) + .def(init<double>()) + .def("get_name", &Foo::get_name, return_internal_reference<>()) + .def("set_name", &Foo::set_name) + .def_readwrite("value", &Foo::value); ++
| + | + |
![]() |
+ def()
+ is the function which can be used to expose C++ functions and callable
+ objects as Python functions in the current
+ scope.
+
template <class F> +void def(char const* name, F f); + +template <class Fn, class A1> +void def(char const* name, Fn fn, A1 const&); + +template <class Fn, class A1, class A2> +void def(char const* name, Fn fn, A1 const&, A2 const&); + +template <class Fn, class A1, class A2, class A3> +void def(char const* name, Fn fn, A1 const&, A2 const&, A3 const&); ++
+
+ name is an ntbs which conforms to Python's + identifier + naming rules. +
+
+ * If Fn is [derived
+ from] object,
+ it will be added to the current
+ scope as a single overload. To be useful, fn
+ should be callable.
+ * If a1 is the result
+ of an overload-dispatch-expression,
+ only the second form is allowed and fn
+ must be a pointer to function or pointer to member function whose
+ arity is the same as A1's maximum
+ arity.
+
+ Effects: For each prefix P of Fn's
+ sequence of argument types, beginning with the one whose length is
+ A1's minimum
+ arity, adds a name(...) function overload to the current
+ scope. Each overload generated invokes a1's call-expression
+ with P, using a copy of a1's call policies. If the longest valid
+ prefix of A1 contains N types and a1 holds M keywords, an initial
+ sequence of the keywords are used for all but the first N - M arguments
+ of each overload.
+
+ * Otherwise, fn must be a non-null function or member function pointer, + and a single function overload built around fn is added to the current + scope. If any of a1-a3 are supplied, they may be selected in any + order from the table below. +
+|
+ + Mnemonic Name + + |
+
+ + Requirements/Type properties + + |
+
+ + Effects + + |
+
|---|---|---|
|
+ + docstring + + |
+
+ + Any ntbs + + |
+
+
+ Value will be bound to the |
+
|
+ + policies + + |
+
+ + A model of CallPolicies + + |
+
+ + A copy will be used as the call policies of the resulting + method overload. + + |
+
|
+ + keywords + + |
+
+
+ The result of a keyword-expression
+ specifying no more arguments than the arity
+ of |
+
+ + A copy will be used as the call policies of the resulting + method overload. + + |
+
#include <boost/python/def.hpp> +#include <boost/python/module.hpp> +#include <boost/python/args.hpp> + +using namespace boost::python; + +char const* foo(int x, int y) { return "foo"; } + +BOOST_PYTHON_MODULE(def_test) +{ + def("foo", foo, args("x", "y"), "foo's docstring"); +} ++
| + | + |
![]() |
+ <boost/python/def_visitor.hpp> provides a generic visitation interface
+ through which the class_
+ def member functionality can be extended non-intrusively to avoid cluttering
+ the class_
+ interface. It declares the def_visitor<T> class template, which is parameterized
+ on the derived type DerivedVisitor,
+ which provides the actual def
+ functionality through its visit
+ member functions.
+
+ The class def_visitor is
+ a base class paramaterized by its derived class. The def_visitor
+ class is a protocol class. Its derived class, DerivedVisitor, is expected
+ to have a member function visit.
+ The def_visitor class is
+ never instantiated directly. Instead, an instance of its subclass, DerivedVisitor,
+ is passed on as an argument to the class_
+ def member function.
+
namespace boost { namespace python { + + template <class DerivedVisitor> + class def_visitor {}; +} ++
+
+ The client supplied class DerivedVisitor template parameter is expected + to: * be privately derived from def_visitor * grant friend access + to class def_visitor_access * define either or both visit member + functions listed in the table below: +
+|
+ + Expression + + |
+
+ + Return Type + + |
+
+ + Requirements + + |
+
+ + Effects + + |
+
|---|---|---|---|
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ A call to |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ A call to |
+
class X {/*...*/}; + +class my_def_visitor : boost::python::def_visitor<my_def_visitor> +{ + friend class def_visitor_access; + + template <class classT> + void visit(classT& c) const + { + c.def("foo", &my_def_visitor::foo); + c.def("bar", &my_def_visitor::bar); + } + + static void foo(X& self); + static void bar(X& self); +}; + +BOOST_PYTHON_MODULE(my_ext) +{ + class_<X>("X") + .def(my_def_visitor()); +} ++
| + | + |
![]() |
+ Boost.Python supports user-defined docstrings with automatic appending + of C++ signatures. These features are enabled by default. The class docstring_options + is available to selectively suppress the user-defined docstrings, signatures, + or both. +
++ Controls the appearance of docstrings of wrapped functions and member functions + for the life-time of the instance. The instances are noncopyable to eliminate + the possibility of surprising side effects. +
+namespace boost { namespace python { + + class docstring_options : boost::noncopyable + { + public: + docstring_options(bool show_all=true); + docstring_options(bool show_user_defined, bool show_signatures); + docstring_options(bool show_user_defined, bool show_py_signatures, bool show_cpp_signatures); + ~docstring_options(); + void disable_user_defined(); + void enable_user_defined(); + void disable_signatures(); + void enable_signatures(); + void disable_py_signatures(); + void enable_py_signatures(); + void disable_cpp_signatures(); + void enable_cpp_signatures(); + void disable_all(); + void enable_all(); + }; +}} ++
docstring_options(bool show_all=true); ++
+
+ Constructs a docstring_options object which controls the appearance
+ of function and member-function docstrings defined in the code that
+ follows. If show_all is true, both the user-defined docstrings and
+ the automatically generated Python and C++ signatures are shown.
+ If show_all is false the __doc__
+ attributes are None.
+
docstring_options(bool show_user_defined, bool show_signatures); ++
+
+ Constructs a docstring_options
+ object which controls the appearance of function and member-function
+ docstrings defined in the code that follows. Iff show_user_defined
+ is true, the user-defined
+ docstrings are shown. Iff show_signatures
+ is true, Python and
+ C++ signatures are automatically added. If both show_user_defined
+ and show_signatures
+ are false, the __doc__ attributes are None.
+
docstring_options(bool show_user_defined, bool show_py_signatures, bool show_cpp_signatures); ++
+
+ Constructs a docstring_options
+ object which controls the appearance of function and member-function
+ docstrings defined in the code that follows. Iff show_user_defined
+ is true, the user-defined
+ docstrings are shown. Iff show_py_signatures
+ is true, Python signatures
+ are automatically added. Iff show_cpp_signatures
+ is true, C++ signatures are automatically added. If all parameters
+ are false, the __doc__ attributes are None.
+
~docstring_options();+
+
+ Restores the previous state of the docstring options. In particular,
+ if docstring_options
+ instances are in nested C++ scopes the settings effective in the
+ enclosing scope are restored. If the last docstring_options
+ instance goes out of scope the default "all on" settings
+ are restored.
+
void disable_user_defined(); +void enable_user_defined(); +void disable_signatures(); +void enable_signatures(); +void disable_py_signatures(); +void enable_py_signatures(); +void disable_cpp_signatures(); +void enable_cpp_signatures(); +void disable_all(); +void enable_all(); ++
+ These member functions dynamically change the appearance of docstrings
+ in the code that follows. The *_user_defined()
+ and *_signatures() member functions are provided for fine-grained
+ control. The *_all() member functions are convenient shortcuts
+ to manipulate all settings simultaneously.
+
#include <boost/python/module.hpp> +#include <boost/python/def.hpp> +#include <boost/python/docstring_options.hpp> + +void foo() {} + +BOOST_PYTHON_MODULE(demo) +{ + using namespace boost::python; + docstring_options doc_options(DEMO_DOCSTRING_SHOW_ALL); + def("foo", foo, "foo doc"); +} ++
+ If compiled with -DDEMO_DOCSTRING_SHOW_ALL=true:
+
>>> import demo +>>> print demo.foo.__doc__ +foo() -> None : foo doc +C++ signature: + foo(void) -> void ++
+ If compiled with -DDEMO_DOCSTRING_SHOW_ALL=false:
+
>>> import demo +>>> print demo.foo.__doc__ +None ++
#include <boost/python/module.hpp> +#include <boost/python/def.hpp> +#include <boost/python/args.hpp> +#include <boost/python/docstring_options.hpp> + +int foo1(int i) { return i; } +int foo2(long l) { return static_cast<int>(l); } +int foo3(float f) { return static_cast<int>(f); } +int foo4(double d) { return static_cast<int>(d); } + +BOOST_PYTHON_MODULE(demo) +{ + using namespace boost::python; + docstring_options doc_options; + def("foo1", foo1, arg("i"), "foo1 doc"); + doc_options.disable_user_defined(); + def("foo2", foo2, arg("l"), "foo2 doc"); + doc_options.disable_signatures(); + def("foo3", foo3, arg("f"), "foo3 doc"); + doc_options.enable_user_defined(); + def("foo4", foo4, arg("d"), "foo4 doc"); + doc_options.enable_py_signatures(); + def("foo5", foo4, arg("d"), "foo5 doc"); + doc_options.disable_py_signatures(); + doc_options.enable_cpp_signatures(); + def("foo6", foo4, arg("d"), "foo6 doc"); +} ++
+ Python code: +
+>>> import demo +>>> print demo.foo1.__doc__ +foo1( (int)i) -> int : foo1 doc +C++ signature: + foo1(int i) -> int +>>> print demo.foo2.__doc__ +foo2( (int)l) -> int : +C++ signature: + foo2(long l) -> int +>>> print demo.foo3.__doc__ +None +>>> print demo.foo4.__doc__ +foo4 doc +>>> print demo.foo5.__doc__ +foo5( (float)d) -> int : foo5 doc +>>> print demo.foo6.__doc__ +foo6 doc +C++ signature: + foo6(double d) -> int ++
#include <boost/python/module.hpp> +#include <boost/python/def.hpp> +#include <boost/python/args.hpp> +#include <boost/python/docstring_options.hpp> + +int foo1(int i) { return i; } +int foo2(long l) { return static_cast<int>(l); } + +int bar1(int i) { return i; } +int bar2(long l) { return static_cast<int>(l); } + +namespace { + + void wrap_foos() + { + using namespace boost::python; + // no docstring_options here + // -> settings from outer C++ scope are in effect + def("foo1", foo1, arg("i"), "foo1 doc"); + def("foo2", foo2, arg("l"), "foo2 doc"); + } + + void wrap_bars() + { + using namespace boost::python; + bool show_user_defined = true; + bool show_signatures = false; + docstring_options doc_options(show_user_defined, show_signatures); + def("bar1", bar1, arg("i"), "bar1 doc"); + def("bar2", bar2, arg("l"), "bar2 doc"); + } +} + +BOOST_PYTHON_MODULE(demo) +{ + boost::python::docstring_options doc_options(false); + wrap_foos(); + wrap_bars(); +} ++
+ Python code: +
+>>> import demo +>>> print demo.foo1.__doc__ +None +>>> print demo.foo2.__doc__ +None +>>> print demo.bar1.__doc__ +bar1 doc +>>> print demo.bar2.__doc__ +bar2 doc ++
| + | + |
![]() |
+ <boost/python/enum.hpp> defines the interface through which users
+ expose their C++ enumeration types to Python. It declares the enum_ class template, which is parameterized
+ on the enumeration type being exposed.
+
+ Creates a Python class derived from Python's int
+ type which is associated with the C++ type passed as its first parameter.
+
namespace boost { namespace python +{ + template <class T> + class enum_ : public object + { + enum_(char const* name, char const* doc = 0); + enum_<T>& value(char const* name, T); + enum_<T>& export_values(); + }; +}} ++
enum_(char const* name, char const* doc=0);+
+
+ name is an ntbs which conforms to Python's + identifier + naming rules. +
+ Constructs an enum_
+ object holding a Python extension type derived from int which is named name.
+ The named attribute of the current
+ scope is bound to the new extension type.
+
enum_<T>& value(char const* name, T x);+
+
+ name is an ntbs which conforms to Python's + identifier + naming rules. +
+ adds an instance of the wrapped enumeration type with value x to + the type's dictionary as the named attribute. +
+ *this
+
enum_<T>& export_values();+
+
+ sets attributes in the current + scope with the same names and values as all enumeration values + exposed so far by calling value(). +
+ *this
+
+ C++ module definition +
+#include <boost/python/enum.hpp> +#include <boost/python/def.hpp> +#include <boost/python/module.hpp> + +using namespace boost::python; + +enum color { red = 1, green = 2, blue = 4 }; + +color identity_(color x) { return x; } + +BOOST_PYTHON_MODULE(enums) +{ + enum_<color>("color") + .value("red", red) + .value("green", green) + .export_values() + .value("blue", blue) + ; + + def("identity", identity_); +} ++
+ Interactive Python: +
+>>> from enums import * + +>>> identity(red) +enums.color.red + +>>> identity(color.red) +enums.color.red + +>>> identity(green) +enums.color.green + +>>> identity(color.green) +enums.color.green + +>>> identity(blue) +Traceback (most recent call last): + File "<stdin>", line 1, in ? +NameError: name 'blue' is not defined + +>>> identity(color.blue) +enums.color.blue + +>>> identity(color(1)) +enums.color.red + +>>> identity(color(2)) +enums.color.green + +>>> identity(color(3)) +enums.color(3) + +>>> identity(color(4)) +enums.color.blue + +>>> identity(1) +Traceback (most recent call last): + File "<stdin>", line 1, in ? +TypeError: bad argument type for built-in operation ++
| + | + |
![]() |
+ <boost/python/errors.hpp> provides types and functions for managing + and translating between Python and C++ exceptions. This is relatively low-level + functionality that is mostly used internally by Boost.Python. Users should + seldom need it. +
+
+ error_already_set is an exception type which can be thrown to indicate
+ that a Python error has occurred. If thrown, the precondition is that
+ PyErr_Occurred()
+ returns a value convertible to true.
+ Portable code shouldn't throw this exception type directly, but should
+ instead use throw_error_already_set(),
+ below.
+
namespace boost { namespace python +{ + class error_already_set {}; +}} ++
template <class T> bool handle_exception(T f) throw(); +void handle_exception() throw(); ++
+
+ The first form requires that the expression function0<void>(f) + is valid. The second form requires that a C++ exception is currently + being handled (see section 15.1 in the C++ standard). +
+ The first form calls f() inside a try block which first attempts + to use all registered exception + translators. If none of those translates the exception, the + catch clauses then set an appropriate Python exception for the C++ + exception caught, returning true if an exception was thrown, false + otherwise. The second form passes a function which rethrows the exception + currently being handled to the first form. +
+ No exception is being handled +
+ nothing +
+ At inter-language boundaries it is important to ensure that no C++ + exceptions escape, since the calling language usually doesn't have + the equipment necessary to properly unwind the stack. Use handle_exception + to manage exception translation whenever your C++ code is called + directly from the Python API. This is done for you automatically + by the usual function wrapping facilities: make_function(), + make_constructor(), + def() + and class_::def(). + The second form can be more convenient to use (see the example below), + but various compilers have problems when exceptions are rethrown + from within an enclosing try block. +
template <class T> T* expect_non_null(T* x);+
+
+ x +
+ error_already_set() iff x == 0. +
+ Simplifies error-handling when calling functions in the Python/C + API which return 0 on error. +
void throw_error_already_set();+
+
+ throw error_already_set(); +
+ Simplifies error-handling when calling functions in the Python/C + API which return 0 on error. +
void throw_error_already_set();+
+
+ throw error_already_set(); +
+ Many platforms and compilers are not able to consistently catch exceptions + thrown across shared library boundaries. Using this function from + the Boost.Python library ensures that the appropriate catch block + in handle_exception() can catch the exception. +
#include <string> +#include <boost/python/errors.hpp> +#include <boost/python/object.hpp> +#include <boost/python/handle.hpp> + +// Returns a std::string which has the same value as obj's "__name__" +// attribute. +std::string get_name(boost::python::object obj) +{ + // throws if there's no __name__ attribute + PyObject* p = boost::python::expect_non_null( + PyObject_GetAttrString(obj.ptr(), "__name__")); + + char const* s = PyString_AsString(p); + if (s != 0) + Py_DECREF(p); + + // throws if it's not a Python string + std::string result( + boost::python::expect_non_null( + PyString_AsString(p))); + + Py_DECREF(p); // Done with p + + return result; +} + +// +// Demonstrate form 1 of handle_exception +// + +// Place into result a Python Int object whose value is 1 if a and b have +// identical "__name__" attributes, 0 otherwise. +void same_name_impl(PyObject*& result, boost::python::object a, boost::python::object b) +{ + result = PyInt_FromLong( + get_name(a) == get_name(a2)); +} + +object borrowed_object(PyObject* p) +{ + return boost::python::object( + boost::python::handle<>( + boost::python::borrowed(a1))); +} + +// This is an example Python 'C' API interface function +extern "C" PyObject* +same_name(PyObject* args, PyObject* keywords) +{ + PyObject* a1; + PyObject* a2; + PyObject* result = 0; + + if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &a1, &a2)) + return 0; + + // Use boost::bind to make an object compatible with + // boost::Function0<void> + if (boost::python::handle_exception( + boost::bind<void>(same_name_impl, boost::ref(result), borrowed_object(a1), borrowed_object(a2)))) + { + // an exception was thrown; the Python error was set by + // handle_exception() + return 0; + } + + return result; +} + +// +// Demonstrate form 2 of handle_exception. Not well-supported by all +// compilers. +// +extern "C" PyObject* +same_name2(PyObject* args, PyObject* keywords) +{ + PyObject* a1; + PyObject* a2; + PyObject* result = 0; + + if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &a1, &a2)) + return 0; + + try { + return PyInt_FromLong( + get_name(borrowed_object(a1)) == get_name(borrowed_object(a2))); + } + catch(...) + { + // If an exception was thrown, translate it to Python + boost::python::handle_exception(); + return 0; + } +} ++
| + | + |
![]() |
+ As described here, + it is important to make sure that exceptions thrown by C++ code do not + pass into the Python interpreter core. By default, Boost.Python translates + all C++ exceptions thrown by wrapped functions and module init functions + into Python, but the default translators are extremely limited: most C++ + exceptions will appear in Python as a RuntimeError + exception whose representation is 'Unidentifiable C++ Exception'. To produce + better error messages, users can register additional exception translators + as described below. +
+template<class ExceptionType, class Translate> +void register_exception_translator(Translate translate); ++
+
+ Translate is CopyConstructible, and the following code must be well-formed: +
+void f(ExceptionType x) { translate(x); }+
+ . The expression translate(x) must either throw a C++ exception,
+ or a subsequent call to PyErr_Occurred() must return 1.
+
+ Adds a copy of translate to the sequence of exception translators + tried when Boost.Python catches an exception that is about to pass + into Python's core interpreter. The new translator will get "first + shot" at translating all exceptions matching the catch clause + shown above. Any subsequently-registered translators will be allowed + to translate the exception earlier. A translator which cannot translate + a given C++ exception can re-throw it, and it will be handled by + a translator which was registered earlier (or by the default translator). +
#include <boost/python/module.hpp> +#include <boost/python/def.hpp> +#include <boost/python/exception_translator.hpp> +#include <exception> + +struct my_exception : std::exception +{ + char const* what() throw() { return "One of my exceptions"; } +}; + +void translate(my_exception const& e) +{ + // Use the Python 'C' API to set up an exception object + PyErr_SetString(PyExc_RuntimeError, e.what()); +} + +void something_which_throws() +{ + ... + throw my_exception(); + ... +} + +BOOST_PYTHON_MODULE(exception_translator_ext) +{ + using namespace boost::python; + register_exception_translator<my_exception>(&translate); + + def("something_which_throws", something_which_throws); +} ++
| + | + |
![]() |
+ <boost/python/init.hpp> defines the interface for exposing C++ constructors
+ to Python as extension class __init__
+ functions.
+
+ An init-expression is used to describe a family of __init__
+ methods to be generated for an extension class, and the result has the
+ following properties:
+
+
+ An ntbs whose value will bound to the
+ method's __doc__
+ attribute
+
+ A keyword-expression
+ which will be used to name (a trailing subsequence of) the arguments
+ to the generated __init__
+ function(s).
+
+ An instance of a model of CallPolicies. +
+ An MPL sequence of C++ argument types which will be used to construct + the wrapped C++ object. An init expression has one or more valid + prefixes which are given by a sequence of prefixes of its argument + types. +
+ A MPL sequence which can be used to specify a family of one or more __init__ + functions. Only the last Ti supplied may be an instantiation of optional<...>. +
+namespace boost { namespace python +{ + template <T1 = unspecified,...Tn = unspecified> + struct init + { + init(char const* doc = 0); + template <class Keywords> init(Keywords const& kw, char const* doc = 0); + template <class Keywords> init(char const* doc, Keywords const& kw); + + template <class CallPolicies> + unspecified operator[](CallPolicies const& policies) const + }; +}} ++
init(char const* doc = 0); +template <class Keywords> init(Keywords const& kw, char const* doc = 0); +template <class Keywords> init(char const* doc, Keywords const& kw); ++
+
+ If supplied, doc is an ntbs. If supplied, + kw is the result of a +
+ The result is an init-expression whose docstring is doc and whose + keywords are a reference to kw. If the first form is used, the + resulting expression's keywords are empty. The expression's call + policies are an instance of default_call_policies. + If Tn is optional<U1, + U2,... Um>, the expression's valid prefixes are given + by: +
+(T1, T2,...Tn-1), (T1, T2,...Tn-1 , U1), (T1, T2,...Tn-1 , U1, U2), ...(T1, T2,...Tn-1 , U1, U2,...Um)+
+ . Otherwise, the expression has one valid prefix given by the template + arguments the user specified. +
+template <class Policies> +unspecified operator[](Policies const& policies) const ++
+
+ Policies is a model of CallPolicies. +
+ Returns a new init-expression + with all the same properties as the init object except that its + call policies are replaced by a reference to policies. +
+ A MPL sequence which can be used to specify the optional arguments to an + __init__ function. +
+namespace boost { namespace python +{ + template <T1 = unspecified,...Tn = unspecified> + struct optional {}; +}} ++
+ Given the C++ declarations: +
+class Y; +class X +{ + public: + X(int x, Y* y) : m_y(y) {} + X(double); + private: + Y* m_y; +}; ++
+ A corresponing Boost.Python extension class can be created with: +
+using namespace boost::python; + +class_<X>("X", "This is X's docstring.", + init<int,char const*>(args("x","y"), "X.__init__'s docstring")[ + with_custodian_and_ward<1,3>()] + ) + .def(init<double>()) + ; ++
| + | + |
![]() |
+ <boost/python/iterator.hpp> provides types and functions for creating
+ Python
+ iterators from C++ Containers and Iterators. Note that if your
+ class_ supports random-access
+ iterators, implementing __getitem__
+ (also known as the Sequence Protocol) may serve you better than using this
+ facility: Python will automatically create an iterator type for you (see
+ iter()),
+ and each access can be range-checked, leaving no possiblity of accessing
+ through an invalidated C++ iterator.
+
+ Instances of iterator<C,P> hold a reference to a callable Python
+ object which, when invoked from Python, expects a single argument c convertible
+ to C and creates a Python iterator that traverses [c.begin(), c.end()). The optional CallPolicies
+ P can be used to control
+ how elements are returned during iteration.
+
+ In the table below, c is an instance of Container. +
+|
+ + Template Parameter + + |
+
+ + Requirements + + |
+
+ + Semantics + + |
+
+ + Default + + |
+
|---|---|---|---|
|
+ + Container + + |
+
+
+ |
+
+
+ The result will convert its argument to c and call c.begin()
+ and c.end() to acquire iterators. To invoke Container's const
+ |
++ | +
|
+ + NextPolicies + + |
+
+ + A default-constructible model of CallPolicies. + + |
+
+
+ Applied to the resulting iterators' |
+
+ + An unspecified model of CallPolicies + which always makes a copy of the result of deferencing the underlying + C++ iterator + + |
+
namespace boost { namespace python + { + template <class Container, class NextPolicies = unspecified> + struct iterator : object + { + iterator(); + }; + }} ++
iterator()+
+
+ Initializes its base class with the result of: +
+range<NextPolicies>(&iterators<Container>::begin, &iterators<Container>::end)+
+ this->get()
+ points to a Python callable object which creates a Python iterator
+ as described above.
+
+ Provides an easy way to create iterators for the common case where
+ a C++ class being wrapped provides begin() and end().
+
+ A utility class template which provides a way to reliably call its argument's
+ begin()
+ and end()
+ member functions. Note that there is no portable way to take the address
+ of a member function of a C++ standard library container, so iterators<>
+ can be particularly helpful when wrapping them.
+
+ In the table below, x is an instance of C. +
+|
+ + Required Valid Expression + + |
+
+ + Type + + |
+
|---|---|
|
+ + x.begin() + + |
+
+ + Convertible to C::const_iterator if C is a const type; convertible + to C::iterator otherwise. + + |
+
|
+ + x.end() + + |
+
+ + Convertible to C::const_iterator if C is a const type; convertible + to C::iterator otherwise. + + |
+
namespace boost { namespace python +{ + template <class C> + struct iterators + { + typedef typename C::const_iterator iterator; + static iterator begin(C& x); + static iterator end(C& x); + }; +}} ++
+ If C is a const type, +
+typedef typename C::const_iterator iterator;+
+ Otherwise: +
+typedef typename C::iterator iterator;+
static iterator begin(C&);+
+
+ x.begin()
+
static iterator end(C&);+
+
+ x.end()
+
template <class NextPolicies, class Target, class Accessor1, class Accessor2> +object range(Accessor1 start, Accessor2 finish); + +template <class NextPolicies, class Accessor1, class Accessor2> +object range(Accessor1 start, Accessor2 finish); + +template <class Accessor1, class Accessor2> +object range(Accessor1 start, Accessor2 finish); ++
+
+ NextPolicies is a default-constructible model of CallPolicies. +
+ The first form creates a Python callable object which, when invoked,
+ converts its argument to a Target object x, and creates a Python
+ iterator which traverses [bind(start,_1)(x), bind(finish,_1)(x)),
+ applying NextPolicies to the iterator's next() function. The second form is identical
+ to the first, except that Target is deduced from Accessor1 as follows:
+
+ # If Accessor1 is a function type, Target is the type of its first
+ argument. # If Accessor1 is a data member pointer of the form R (T::*),
+ Target is identical to T.
+ # If Accessor1 is a member function pointer of the form R (T::*)(arguments...)
+ cv-opt, where cv-opt is an optional
+ cv-qualifier, Target is identical to T.
+
+ The third form is identical to the second, except that NextPolicies + is an unspecified model of CallPolicies + which always makes a copy of the result of deferencing the underlying + C++ iterator +
+
+ The use of boost::bind() allows C++ iterators to be accessed
+ through functions, member functions or data member pointers. Customization
+ of NextPolicies (e.g. using return_internal_reference)
+ is useful when it is expensive to copy sequence elements of a wrapped
+ class type. Customization of Target is useful when Accessor1 is a
+ function object, or when a base class of the intended target type
+ would otherwise be deduced.
+
#include <boost/python/module.hpp> +#include <boost/python/class.hpp> + +#include <vector> + +using namespace boost::python; +BOOST_PYTHON_MODULE(demo) +{ + class_<std::vector<double> >("dvec") + .def("__iter__", iterator<std::vector<double> >()) + ; +} ++
| + | + |
![]() |
+ This header provides the basic facilities needed to create a Boost.Python + extension module. +
+
+ BOOST_PYTHON_MODULE(name)
+ is used to declare Python module
+ initialization functions. The name argument must exactly match
+ the name of the module to be initialized, and must conform to Python's
+ identifier
+ naming rules. Where you would normally write
+
extern "C" void initname() +{ + ... +} ++
+ Boost.Python modules should be initialized with +
+BOOST_PYTHON_MODULE(name) +{ + ... +} ++
+ This macro generates two functions in the scope where it is used: extern "C"
+ void initname(), and void
+ init_module_name(),
+ whose body must follow the macro invocation. init_name
+ passes init_module_name
+ to handle_exception()
+ so that any C++ exceptions generated are safely processeed. During the
+ body of init_name, the
+ current scope
+ refers to the module being initialized.
+
+ C++ module definition: +
+#include <boost/python/module.hpp> + +BOOST_PYTHON_MODULE(xxx) +{ + throw "something bad happened" +} ++
+ Interactive Python: +
+>>> import xxx +Traceback (most recent call last): + File "", line 1, in ? +RuntimeError: Unidentifiable C++ Exception ++
| + | + |
![]() |
+ <boost/python/operators.hpp> provides types and functions for automatically + generating Python special + methods from the corresponding C++ constructs. Most of these constructs + are operator expressions, hence the name. To use the facility, substitute + the self + object for an object of the class type being wrapped in the expression + to be exposed, and pass the result to class_<>::def(). + Much of what is exposed in this header should be considered part of the + implementation, so is not documented in detail here. +
+
+ self_ns::self_t is the actual type of the self
+ object. The library isolates self_t
+ in its own namespace, self_ns,
+ in order to prevent the generalized operator templates which operate on
+ it from being found by argument-dependent lookup in other contexts. This
+ should be considered an implementation detail, since users should never
+ have to mention self_t
+ directly.
+
namespace boost { namespace python { namespace self_ns { +{ + unspecified-type-declaration self_t; + + // inplace operators + template <class T> operator_<unspecified> operator+=(self_t, T); + template <class T> operator_<unspecified> operator-=(self_t, T); + template <class T> operator_<unspecified> operator*=(self_t, T); + template <class T> operator_<unspecified> operator/=(self_t, T); + template <class T> operator_<unspecified> operator%=(self_t, T); + template <class T> operator_<unspecified> operator>>=(self_t, T); + template <class T> operator_<unspecified> operator<<=(self_t, T); + template <class T> operator_<unspecified> operator&=(self_t, T); + template <class T> operator_<unspecified> operator^=(self_t, T); + template <class T> operator_<unspecified> operator|=(self_t, T); + + // comparisons + template <class L, class R> operator_<unspecified> operator==(L const&, R const&); + template <class L, class R> operator_<unspecified> operator!=(L const&, R const&); + template <class L, class R> operator_<unspecified> operator<(L const&, R const&); + template <class L, class R> operator_<unspecified> operator>(L const&, R const&); + template <class L, class R> operator_<unspecified> operator<=(L const&, R const&); + template <class L, class R> operator_<unspecified> operator>=(L const&, R const&); + + // non-member operations + template <class L, class R> operator_<unspecified> operator+(L const&, R const&); + template <class L, class R> operator_<unspecified> operator-(L const&, R const&); + template <class L, class R> operator_<unspecified> operator*(L const&, R const&); + template <class L, class R> operator_<unspecified> operator/(L const&, R const&); + template <class L, class R> operator_<unspecified> operator%(L const&, R const&); + template <class L, class R> operator_<unspecified> operator>>(L const&, R const&); + template <class L, class R> operator_<unspecified> operator<<(L const&, R const&); + template <class L, class R> operator_<unspecified> operator&(L const&, R const&); + template <class L, class R> operator_<unspecified> operator^(L const&, R const&); + template <class L, class R> operator_<unspecified> operator|(L const&, R const&); + template <class L, class R> operator_<unspecified> pow(L const&, R const&); + + // unary operations + operator_<unspecified> operator-(self_t); + operator_<unspecified> operator+(self_t); + operator_<unspecified> operator~(self_t); + operator_<unspecified> operator!(self_t); + + // value operations + operator_<unspecified> int_(self_t); + operator_<unspecified> long_(self_t); + operator_<unspecified> float_(self_t); + operator_<unspecified> complex_(self_t); + operator_<unspecified> str(self_t); + + operator_<unspecified> repr(self_t); +}}}; ++
+ The tables below describe the methods generated when the results of the
+ expressions described are passed as arguments to class_<>::def().
+ x is an object of the class
+ type being wrapped.
+
+ In the table below, If r
+ is an object of type other<T>,
+ y is an object of type
+ T; otherwise, y is an object of the same type as
+ r.
+
|
+ + C++ Expression + + |
+
+ + Python Method Name + + |
+
+ + C++ Implementation + + |
+
|---|---|---|
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+ In the tables below, if r
+ is of type self_t,
+ y is an object of the
+ same type as x; if l or r
+ is an object of type other<T>,
+ y is an object of type
+ T; otherwise, y is an object of the same type as
+ l or r.
+ l is never of type self_t.
+
+ The column of Python Expressions illustrates the expressions that will + be supported in Python for objects convertible to the types of x and + y. The secondary operation arises due to Python's reflection + rules for rich comparison operators, and are only used when the + corresponding operation is not defined as a method of the y object. +
+|
+ + C++ Expression + + |
+
+ + Python Method Name + + |
+
+ + C++ Implementation + + |
+
+ + Python Expression (primary, secondary) + + |
+
|---|---|---|---|
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+ The operations whose names begin with "__r" below will only + be called if the left-hand operand does not already support the given + operation, as described here. +
+|
+ + C++ Expression + + |
+
+ + Python Method Name + + |
+
+ + C++ Implementation + + |
+
|---|---|---|
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+ + C++ Expression + + |
+
+ + Python Method Name + + |
+
+ + C++ Implementation + + |
+
|---|---|---|
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+ + C++ Expression + + |
+
+ + Python Method Name + + |
+
+ + C++ Implementation + + |
+
|---|---|---|
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+
+ |
+
+ Instances of other<T>
+ can be used in operator expressions with self;
+ the result is equivalent to the same expression with a T
+ object in place of other<T>. Use other<T> to prevent construction of a T object in case it is heavyweight, when
+ no constructor is available, or simply for clarity.
+
namespace boost { namespace python +{ + template <class T> + struct other + { + }; +}} ++
+ Instantiations of detail::operator_<> are used as the return type of
+ operator expressions involving self.
+ This should be considered an implementation detail and is only documented
+ here as a way of showing how the result of self-expressions match calls
+ to class_<>::def().
+
namespace boost { namespace python { namespace detail +{ + template <unspecified> + struct operator_ + { + }; +}}} ++
namespace boost { namespace python +{ + using self_ns::self; +}} ++
#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/operators.hpp> +#include <boost/operators.hpp> + +struct number + : boost::integer_arithmetic<number> +{ + explicit number(long x_) : x(x_) {} + operator long() const { return x; } + + template <class T> + number& operator+=(T const& rhs) + { x += rhs; return *this; } + + template <class T> + number& operator-=(T const& rhs) + { x -= rhs; return *this; } + + template <class T> + number& operator*=(T const& rhs) + { x *= rhs; return *this; } + + template <class T> + number& operator/=(T const& rhs) + { x /= rhs; return *this; } + + template <class T> + number& operator%=(T const& rhs) + { x %= rhs; return *this; } + + long x; +}; + +using namespace boost::python; +BOOST_PYTHON_MODULE(demo) +{ + class_<number>("number", init<long>()) + // interoperate with self + .def(self += self) + .def(self + self) + .def(self -= self) + .def(self - self) + .def(self *= self) + .def(self * self) + .def(self /= self) + .def(self / self) + .def(self %= self) + .def(self % self) + + // Convert to Python int + .def(int_(self)) + + // interoperate with long + .def(self += long()) + .def(self + long()) + .def(long() + self) + .def(self -= long()) + .def(self - long()) + .def(long() - self) + .def(self *= long()) + .def(self * long()) + .def(long() * self) + .def(self /= long()) + .def(self / long()) + .def(long() / self) + .def(self %= long()) + .def(self % long()) + .def(long() % self) + ; +} ++
| + | + |
![]() |
+ Defines facilities for querying and controlling the Python scope (namespace) + which will contain new wrapped classes and functions. +
++ The scope class has an associated global Python object which controls the + Python namespace in which new extension classes and wrapped functions will + be defined as attributes. Default-constructing a new scope object binds + it to the associated global Python object. Constructing a scope object + with an argument changes the associated global Python object to the one + held by the argument, until the lifetime of the scope object ends, at which + time the associated global Python object reverts to what it was before + the scope object was constructed. +
+namespace boost { namespace python +{ + class scope : public object + { + public: + scope(scope const&); + scope(object const&); + scope(); + ~scope() + private: + void operator=(scope const&); + }; +}} ++
explicit scope(scope const& x); +explicit scope(object const& x); ++
+ Stores a reference to the current associated scope object, and sets the + associated scope object to the one referred to by x.ptr(). The object base + class is initialized with x. +
+scope();+
+ Stores a reference to the current associated scope object. The object base + class is initialized with the current associated scope object. Outside + any module initialization function, the current associated Python object + is None. +
+~scope()+
+ Sets the current associated Python object to the stored object. +
++ The following example shows how scope setting can be used to define nested + classes. +
++ C++ Module definition: +
+#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/scope.hpp> +using namespace boost::python; + +struct X +{ + void f() {} + + struct Y { int g() { return 42; } }; +}; + +BOOST_PYTHON_MODULE(nested) +{ + // add some constants to the current (module) scope + scope().attr("yes") = 1; + scope().attr("no") = 0; + + // Change the current scope + scope outer + = class_<X>("X") + .def("f", &X::f) + ; + + // Define a class Y in the current scope, X + class_<X::Y>("Y") + .def("g", &X::Y::g) + ; +} ++
+ Interactive Python: +
+>>> import nested +>>> nested.yes +1 +>>> y = nested.X.Y() +>>> y.g() +42 ++
| + | + |
![]() |
+ <boost/python/stl_iterator.hpp> provides types for creating C++ Iterators + from Python + iterables. +
+
+ Instances of stl_input_iterator<T> hold a Python iterator and adapt it
+ for use with STL algorithms. stl_input_iterator<T> satisfies the requirements for an Input
+ Iterator.
+
|
+ + Template Parameter + + |
+
+ + Requirements + + |
+
+ + Semantics + + |
+
+ + Default + + |
+
|---|---|---|---|
|
+ + ValueType + + |
+
+ + ValueType must be CopyConstructible. + + |
+
+
+ Dereferencing an instance of |
+
+ + None + + |
+
namespace boost { namespace python +{ + template <class ValueType> + struct stl_input_iterator + { + typedef std::ptrdiff_t difference_type; + typedef ValueType value_type; + typedef ValueType* pointer; + typedef ValueType reference; + typedef std::input_iterator_tag iterator_category; + + stl_input_iterator(); + stl_input_iterator(object const& ob); + + stl_input_iterator& operator++(); + stl_input_iterator operator++(int); + + ValueType operator*() const; + + friend bool operator==(stl_input_iterator const& lhs, stl_input_iterator const& rhs); + friend bool operator!=(stl_input_iterator const& lhs, stl_input_iterator const& rhs); + private: + object it; // For exposition only + object ob; // For exposition only + }; +}} ++
stl_input_iterator() ++
+
+ Creates a past-the-end input iterator, useful for signifying the + end of a sequence. +
+ this is past-the-end
+
+ Nothing. +
stl_input_iterator(object const& ob)+
+
+ Calls ob.attr("__iter__")() and stores the resulting Python + iterator object in this->it. Then, calls this->it.attr("next")() + and stores the result in this->ob. If the sequence is exhausted, + sets this->ob to object(). +
+ this is a dereferenceable or past-the-end. +
stl_input_iterator &operator++() ++
+
+ Calls this->it.attr("next")() and stores the result + in this->ob. If the sequence is exhausted, sets this->ob to + object(). +
+ this is a dereferenceable or past-the-end. +
+ *this
+
stl_input_iterator &operator++(int)+
+
+ stl_input_iterator tmp =
+ *this; ++*this; return tmp;
+
+ this is a dereferenceable or past-the-end. +
ValueType operator*() const ++
+
+ Returns the current element in the sequence. +
+ extract<ValueType>(this->ob);
+
friend bool operator==(stl_input_iterator const& lhs, stl_input_iterator const& rhs) ++
+
+ Returns true if both iterators are dereferenceable or if both iterators + are past-the-end, false otherwise. +
+ (lhs.ob == object()) == (rhs.ob == object())
+
friend bool operator!=(stl_input_iterator const& lhs, stl_input_iterator const& rhs) ++
+
+ Returns false if both iterators are dereferenceable or if both iterators + are past-the-end, true otherwise. +
+ !(lhs
+ == rhs)
+
#include <boost/python/object.hpp> +#include <boost/python/stl_iterator.hpp> + +#include <list> + +using namespace boost::python; +std::list<int> sequence_to_int_list(object const& ob) +{ + stl_input_iterator<int> begin(ob), end; + return std::list<int>(begin, end); +} ++
| + | + |
![]() |
+ To wrap a class T such that its virtual functions can be "overridden
+ in Python"—so that the corresponding method of a Python derived
+ class will be called when the virtual function is invoked from C++—you
+ must create a C++ wrapper class derived from T
+ that overrides those virtual functions so that they call into Python. This
+ header contains classes that can be used to make that job easier.
+
+ Encapsulates a Python override of a C++ virtual function. An override object
+ either holds a callable Python object or None.
+
namespace boost +{ + class override : object + { + public: + unspecified operator() const; + template <class A0> + unspecified operator(A0) const; + template <class A0, class A1> + unspecified operator(A0, A1) const; + ... + template <class A0, class A1, ...class An> + unspecified operator(A0, A1, ...An) const; + }; +}; ++
unspecified operator() const; +template <class A0> +unspecified operator(A0) const; +template <class A0, class A1> +unspecified operator(A0, A1) const; +... +template <class A0, class A1, ...class An> +unspecified operator(A0, A1, ...An) const; ++
+
+ If *this holds a callable Python object, it is invoked with the specified + arguments in the manner specified here. Otherwise, throws error_already_set. +
+ An object of unspecified type that holds the Python result of the + invocation and, when converted to a C++ type R, attempts to convert + that result object to R. If that conversion fails, throws error_already_set. +
+ Deriving your wrapper class from both T
+ and wrapper<T>
+ makes writing that derived class easier.
+
namespace boost +{ + class wrapper + { + protected: + override get_override(char const* name) const; + }; +}; ++
override get_override(char const* name) const;+
+
+ name is a ntbs. +
+ If *this
+ is the C++ base class subobject of a Python derived class instance
+ that overrides the named function, returns an override object that
+ delegates to the Python override. Otherwise, returns an override
+ object that holds None.
+
#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/wrapper.hpp> +#include <boost/python/call.hpp> + +using namespace boost::python; + +// Class with one pure virtual function +struct P +{ + virtual ~P(){} + virtual char const* f() = 0; + char const* g() { return "P::g()"; } +}; + +struct PCallback : P, wrapper<P> +{ + char const* f() + { + return this->get_override("f")(); + } +}; + +// Class with one non-pure virtual function +struct A +{ + virtual ~A(){} + virtual char const* f() { return "A::f()"; } +}; + +struct ACallback : A, wrapper<A> +{ + char const* f() + { + if (override f = this->get_override("f")) + return f(); + return A::f(); + } + + char const* default_f() { return this->A::f(); } +}; + +BOOST_PYTHON_MODULE_INIT(polymorphism) +{ + class_<PCallback,boost::noncopyable>("P") + .def("f", pure_virtual(&P::f)) + ; + + class_<ACallback,boost::noncopyable>("A") + .def("f", &A::f, &ACallback::default_f) + ; +} ++
| + | + |
![]() |
Copyright © 2002-2005, 2015 David Abrahams, Stefan Seefeld
+ Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt +
+Table of Contents
+Last revised: October 07, 2016 at 15:41:15 GMT |
++ |
![]() |
Table of Contents
+slice_nilconst_attribute_policiesconst_attribute_policies
+ static functionsattribute_policiesattribute_policies static
+ functionsconst_objattribute_policiesconst_objattribute_policies
+ static functionsobjattribute_policiesobjattribute_policies static
+ functionsconst_item_policiesconst_item_policies static
+ functionsitem_policiesitem_policies static functionsconst_slice_policiesconst_slice_policies static
+ functionsslice_policiesslice_policies static functionsobject_operatorsobject_operators
+ observer functionsobjectobject constructors and destructorobject modifiersproxyproxy observer functionsproxy modifier functions
+ Exposes a TypeWrapper
+ for the Python dict type.
+
dict
+
+ Exposes the mapping
+ protocol of Python's built-in dict
+ type. The semantics of the constructors and member functions defined below
+ can be fully understood by reading the TypeWrapper
+ concept definition. Since dict
+ is publicly derived from object, the public object interface applies to dict instances as well.
+
namespace boost { namespace python +{ + class dict : public object + { + dict(); + + template< class T > + dict(T const & data); + + // modifiers + void clear(); + dict copy(); + + template <class T1, class T2> + tuple popitem(); + + template <class T> + object setdefault(T const &k); + + template <class T1, class T2> + object setdefault(T1 const & k, T2 const & d); + + void update(object_cref E); + + template< class T > + void update(T const & E); + + // observers + list values() const; + + object get(object_cref k) const; + + template<class T> + object get(T const & k) const; + + object get(object_cref k, object_cref d) const; + object get(T1 const & k, T2 const & d) const; + + bool has_key(object_cref k) const; + + template< class T > + bool has_key(T const & k) const; + + list items() const; + object iteritems() const; + object iterkeys() const; + object itervalues() const; + list keys() const; + }; +}} ++
using namespace boost::python; +dict swap_object_dict(object target, dict d) +{ + dict result = extract<dict>(target.attr("__dict__")); + target.attr("__dict__") = d; + return result; +} ++
| + | + |
![]() |
+ Exposes a TypeWrapper + for the Python list + type. +
+list
+
+ Exposes the mapping
+ protocol of Python's built-in list
+ type. The semantics of the constructors and member functions defined below
+ can be fully understood by reading the TypeWrapper
+ concept definition. Since list
+ is publicly derived from object, the public object interface applies to list instances as well.
+
namespace boost { namespace python +{ + class list : public object + { + public: + list(); // new list + + template <class T> + explicit list(T const& sequence); + + template <class T> + void append(T const& x); + + template <class T> + long count(T const& value) const; + + template <class T> + void extend(T const& x); + + template <class T> + long index(T const& x) const; + + template <class T> + void insert(object const& index, T const& x); // insert object before index + + object pop(); // remove and return item at index (default last) + object pop(long index); + object pop(object const& index); + + template <class T> + void remove(T const& value); + + void reverse(); // reverse *IN PLACE* + + void sort(); // sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1 + + template <class T> + void sort(T const& value); + }; +}} ++
using namespace boost::python; + +// Return the number of zeroes in the list +long zeroes(list l) +{ + return l.count(0); +} ++
| + | + |
![]() |
+ Exposes a TypeWrapper + for the Python long + integer type. +
+
+ Exposes the numeric
+ type protocol of Python's built-in long
+ type. The semantics of the constructors and member functions defined below
+ can be fully understood by reading the TypeWrapper
+ concept definition. Since long_
+ is publicly derived from object, the public object interface applies to long_ instances as well.
+
namespace boost { namespace python +{ + class long_ : public object + { + public: + long_(); // new long_ + + template <class T> + explicit long_(T const& rhs); + + template <class T, class U> + long_(T const& rhs, U const& base); + }; +}} ++
namespace python = boost::python; + +// compute a factorial without overflowing +python::long_ fact(long n) +{ + if (n == 0) + return python::long_(1); + else + return n * fact(n - 1); +} ++
| + | + |
![]() |
+ Exposes a TypeWrapper + for the Python array + type. +
++ Provides access to the array types of Numerical + Python's Numeric + and NumArray + modules. With the exception of the functions documented below, the semantics + of the constructors and member functions defined below can be fully understood + by reading the TypeWrapper + concept definition. Since array is publicly derived from object, the public + object interface applies to array instances as well. +
++ The default behavior is to use numarray.NDArray as the associated Python + type if the numarray module is installed in the default location. Otherwise + it falls back to use Numeric.ArrayType. If neither extension module is + installed, overloads of wrapped C++ functions with numeric::array parameters + will never be matched, and other attempted uses of numeric::array will + raise an appropriate Python exception. The associated Python type can be + set manually using the set_module_and_type(...) static function. +
+namespace boost { namespace python { namespace numeric +{ + class array : public object + { + public: + object astype(); + template <class Type> + object astype(Type const& type_); + + template <class Type> + array new_(Type const& type_) const; + + template <class Sequence> + void resize(Sequence const& x); + void resize(long x1); + void resize(long x1, long x2); + ... + void resize(long x1, long x2,...long xn); + + template <class Sequence> + void setshape(Sequence const& x); + void setshape(long x1); + void setshape(long x1, long x2); + ... + void setshape(long x1, long x2,...long xn); + + template <class Indices, class Values> + void put(Indices const& indices, Values const& values); + + template <class Sequence> + object take(Sequence const& sequence, long axis = 0); + + template <class File> + void tofile(File const& f) const; + + object factory(); + template <class Sequence> + object factory(Sequence const&); + template <class Sequence, class Typecode> + object factory(Sequence const&, Typecode const&, bool copy = true, bool savespace = false); + template <class Sequence, class Typecode, class Type> + object factory(Sequence const&, Typecode const&, bool copy, bool savespace, Type const&); + template <class Sequence, class Typecode, class Type, class Shape> + object factory(Sequence const&, Typecode const&, bool copy, bool savespace, Type const&, Shape const&); + + template <class T1> + explicit array(T1 const& x1); + template <class T1, class T2> + explicit array(T1 const& x1, T2 const& x2); + ... + template <class T1, class T2,...class Tn> + explicit array(T1 const& x1, T2 const& x2,...Tn const& xn); + + static void set_module_and_type(); + static void set_module_and_type(char const* package_path = 0, char const* type_name = 0); + static void get_module_name(); + + object argmax(long axis=-1); + + object argmin(long axis=-1); + + object argsort(long axis=-1); + + void byteswap(); + + object copy() const; + + object diagonal(long offset = 0, long axis1 = 0, long axis2 = 1) const; + + void info() const; + + bool is_c_array() const; + bool isbyteswapped() const; + void sort(); + object trace(long offset = 0, long axis1 = 0, long axis2 = 1) const; + object type() const; + char typecode() const; + + object getflat() const; + long getrank() const; + object getshape() const; + bool isaligned() const; + bool iscontiguous() const; + long itemsize() const; + long nelements() const; + object nonzero() const; + + void ravel(); + object repeat(object const& repeats, long axis=0); + void setflat(object const& flat); + void swapaxes(long axis1, long axis2); + str tostring() const; + void transpose(object const& axes = object()); + object view() const; + }; +}}} ++
object factory(); +template <class Sequence> +object factory(Sequence const&); +template <class Sequence, class Typecode> +object factory(Sequence const&, Typecode const&, bool copy = true, bool savespace = false); +template <class Sequence, class Typecode, class Type> +object factory(Sequence const&, Typecode const&, bool copy, bool savespace, Type const&); +template <class Sequence, class Typecode, class Type, class Shape> +object factory(Sequence const&, Typecode const&, bool copy, bool savespace, Type const&, Shape const&); ++
+ These functions map to the underlying array type's array() function family. + They are not called "array" because of the C++ limitation that + you can't define a member function with the same name as its enclosing + class. +
+template <class Type> +array new_(Type const&) const; ++
+ This function maps to the underlying array type's new() function. It is + not called "new" because that is a keyword in C++. +
+static void set_module_and_type(char const* package_path, char const* type_name); +static void set_module_and_type(); ++
+
+ package_path and type_name, if supplied, is an ntbs. +
+ The first form sets the package path of the module that supplies + the type named by type_name to package_path. The second form restores + the default search behavior. The associated Python type will be searched + for only the first time it is needed, and thereafter the first time + it is needed after an invocation of set_module_and_type. +
static std::string get_module_name()+
+
+ Returns the name of the module containing the class that will be
+ held by new numeric::array
+ instances.
+
#include <boost/python/numeric.hpp> +#include <boost/python/tuple.hpp> + +// sets the first element in a 2d numeric array +void set_first_element(numeric::array& y, double value) +{ + y[make_tuple(0,0)] = value; +} ++
| + | + |
![]() |
slice_nilconst_attribute_policiesconst_attribute_policies
+ static functionsattribute_policiesattribute_policies static
+ functionsconst_objattribute_policiesconst_objattribute_policies
+ static functionsobjattribute_policiesobjattribute_policies static
+ functionsconst_item_policiesconst_item_policies static
+ functionsitem_policiesitem_policies static functionsconst_slice_policiesconst_slice_policies static
+ functionsslice_policiesslice_policies static functionsobject_operatorsobject_operators
+ observer functionsobjectobject constructors and destructorobject modifiersproxyproxy observer functionsproxy modifier functions+ Exposes the generic Python object wrapper class object, and related classes. + In order to avoid some potenential problems with argument-dependent lookup + and the generalized operators defined on object, all these facilities are + defined in namespace boost::python::api, and object is imported into namespace + boost::python with a using-declaration. +
+class slice_nil; +static const _ = slice_nil(); ++
+ A type that can be used to get the effect of leaving out an index in a + Python slice expression: +
+>>> x[:-1] +>>> x[::-1] ++
+ C++ equivalent: +
+x.slice(_,-1) +x[slice(_,_,-1)] ++
+ The policies which are used for proxies representing an attribute access + to a const object. +
+namespace boost { namespace python { namespace api +{ + struct const_attribute_policies + { + typedef char const* key_type; + static object get(object const& target, char const* key); + }; +}}} ++
static object get(object const& target, char const* key); ++
+
+ key is an ntbs. +
+ accesses the attribute of target named by key. +
+ An object managing the result of the attribute access. +
+ error_already_set + if a Python exception is raised. +
+ The policies which are used for proxies representing an attribute access + to a mutable object. +
+namespace boost { namespace python { namespace api +{ + struct attribute_policies : const_attribute_policies + { + static object const& set(object const& target, char const* key, object const& value); + static void del(object const&target, char const* key); + }; +}}} ++
static object const& set(object const& target, char const* key, object const& value); ++
+
+ key is an ntbs. +
+ sets the attribute of target named by key to value. +
+ error_already_set + if a Python exception is raised. +
static void del(object const&target, char const* key); ++
+
+ key is an ntbs. +
+ deletes the attribute of target named by key. +
+ error_already_set + if a Python exception is raised. +
+ The policies which are used for proxies representing an attribute access + to a const object when the attribute name is given as a const object. +
+namespace boost { namespace python { namespace api +{ + struct const_objattribute_policies + { + typedef object const& key_type; + static object get(object const& target, object const& key); + }; +}}} ++
static object get(object const& target, object const& key); ++
+
+ key is an object holding a string. +
+ accesses the attribute of target named by key. +
+ An object managing the result of the attribute access. +
+ error_already_set + if a Python exception is raised. +
+ The policies which are used for proxies representing an attribute access + to a mutable object when the attribute name is given as a const object. +
+namespace boost { namespace python { namespace api +{ + struct objattribute_policies : const_objattribute_policies + { + static object const& set(object const& target, object const& key, object const& value); + static void del(object const&target, object const& key); + }; +}}} ++
static object const& set(object const& target, object const& key, object const& value); ++
+
+ key is an object holding a string. +
+ sets the attribute of target named by key to value. +
+ error_already_set + if a Python exception is raised. +
static void del(object const&target, object const& key); ++
+
+ key is an object holding a string. +
+ deletes the attribute of target named by key. +
+ error_already_set + if a Python exception is raised. +
+ The policies which are used for proxies representing an item access (via + the Python bracket operators []) to a const object. +
+namespace boost { namespace python { namespace api +{ + struct const_item_policies + { + typedef object key_type; + static object get(object const& target, object const& key); + }; +}}} ++
static object get(object const& target, object const& key); ++
+
+ accesses the item of target specified by key. +
+ An object managing the result of the item access. +
+ error_already_set + if a Python exception is raised. +
+ The policies which are used for proxies representing an item access (via + the Python bracket operators []) to a mutable object. +
+namespace boost { namespace python { namespace api +{ + struct item_policies : const_item_policies + { + static object const& set(object const& target, object const& key, object const& value); + static void del(object const& target, object const& key); + }; +}}} ++
static object const& set(object const& target, object const& key, object const& value); ++
+
+ sets the item of target specified by key to value. +
+ error_already_set + if a Python exception is raised. +
static void del(object const& target, object const& key); ++
+
+ deletes the item of target specified by key. +
+ error_already_set + if a Python exception is raised. +
+ The policies which are used for proxies representing an slice access (via + the Python slice notation [x:y]) to a const object. +
+namespace boost { namespace python { namespace api +{ + struct const_slice_policies + { + typedef std::pair<handle<>, handle<> > key_type; + static object get(object const& target, key_type const& key); + }; +}}} ++
static object get(object const& target, key_type const& key); ++
+
+ accesses the slice of target specified by key. +
+ An object managing the result of the slice access. +
+ error_already_set + if a Python exception is raised. +
+ The policies which are used for proxies representing an slice access to + a mutable object. +
+namespace boost { namespace python { namespace api +{ + struct slice_policies : const_slice_policies + { + static object const& set(object const& target, key_type const& key, object const& value); + static void del(object const& target, key_type const& key); + }; +}}} ++
static object const& set(object const& target, key_type const& key, object const& value); ++
+
+ sets the slice of target specified by key to value. +
+ error_already_set + if a Python exception is raised. +
static void del(object const& target, key_type const& key); ++
+
+ deletes the slice of target specified by key. +
+ error_already_set + if a Python exception is raised. +
+ This is the base class of object and its proxy template used to supply + common interface: member functions, and operators which must be defined + within the class body. Its template parameter U is expected to be a class + derived from object_operators<U>. In practice users should never + use this class directly, but it is documented here because it supplies + important interface to object and its proxies. +
+namespace boost { namespace python { namespace api +{ + template <class U> + class object_operators + { + public: + // function call + // + object operator()() const; + + template <class A0> + object operator()(A0 const&) const; + template <class A0, class A1> + object operator()(A0 const&, A1 const&) const; + ... + template <class A0, class A1,...class An> + object operator()(A0 const&, A1 const&,...An const&) const; + + detail::args_proxy operator* () const; + object operator()(detail::args_proxy const &args) const; + object operator()(detail::args_proxy const &args, + detail::kwds_proxy const &kwds) const; + + // truth value testing + // + typedef unspecified bool_type; + operator bool_type() const; + + // Attribute access + // + proxy<const_object_attribute> attr(char const*) const; + proxy<object_attribute> attr(char const*); + proxy<const_object_objattribute> attr(object const&) const; + proxy<object_objattribute> attr(object const&); + + // item access + // + template <class T> + proxy<const_object_item> operator[](T const& key) const; + + template <class T> + proxy<object_item> operator[](T const& key); + + // slicing + // + template <class T, class V> + proxy<const_object_slice> slice(T const& start, V const& end) const + + template <class T, class V> + proxy<object_slice> slice(T const& start, V const& end); + }; +}}} ++
object operator()() const; +template <class A0> +object operator()(A0 const&) const; +template <class A0, class A1> +object operator()(A0 const&, A1 const&) const; +... +template <class A0, class A1,...class An> +object operator()(A0 const& a1, A1 const& a2,...An const& aN) const; ++
+
+ call<object>(object(*static_cast<U*>(this)).ptr(),
+ a1,
+ a2,...aN)
+
object operator()(detail::args_proxy const &args) const;+
+
+ call object
+ with arguments
+ given by
+ the tuple
+ args
+
object operator()(detail::args_proxy const &args, + detail::kwds_proxy const &kwds) const; ++
+
+ call object
+ with arguments
+ given by
+ the tuple
+ args,
+ and named
+ arguments given
+ by the
+ dictionary kwds
+
operator bool_type() const;+
+
+ Tests truth value of *this.
+
+ call<object>(object(*static_cast<U*>(this)).ptr(),
+ a1,
+ a2,...aN)
+
proxy<const_object_attribute> attr(char const* name) const; +proxy<object_attribute> attr(char const* name); ++
+
+ name is an ntbs. +
+ accesses the named attribute of *this. +
+ a proxy object which binds object(*static_cast<U*>(this)) as its target, and name as its
+ key.
+
proxy<const_object_objattribute> attr(const object& name) const; +proxy<object_objattribute> attr(const object& name); ++
+
+ name is a object holding a string. +
+ accesses the named attribute of *this.
+
+ a proxy object which binds object(*static_cast<U*>(this)) as its target, and name as its
+ key.
+
template <class T> +proxy<const_object_item> operator[](T const& key) const; +template <class T> +proxy<object_item> operator[](T const& key); ++
+
+ accesses the item of *this indicated by key.
+
+ a proxy object which binds object(*static_cast<U*>(this)) as its target, and object(key)
+ as its key.
+
template <class T, class V> +proxy<const_object_slice> slice(T const& start; start, V const& finish) const +template <class T, class V> +proxy<object_slice> slice(T const& start; start, V const& finish); ++
+
+ accesses the slice of *this indicated by std::make_pair(object(start), object(finish)).
+
+ a proxy object which binds object(*static_cast<U*>(this)) as its target, and std::make_pair(object(start),
+ object(finish))
+ as its key.
+
+ The intention is that object acts as much like a Python variable as possible.
+ Thus expressions you'd expect to work in Python should generally work in
+ the same way from C++. Most of object's interface is provided by its base
+ class object_operators<object>, and the free functions defined in
+ this header.
+
namespace boost { namespace python { namespace api +{ + class object : public object_operators<object> + { + public: + object(); + object(object const&); + template <class T> + explicit object(T const& x); + + ~object(); + + object& operator=(object const&); + PyObject* ptr() const; + bool is_none() const; + }; +}}} ++
object();+
+
+ Constructs an object managing a reference to the Python None object. +
+ nothing. +
template <class T> +explicit object(T const& x); ++
+
+ converts x to python and manages a reference to it. +
+ error_already_set + and sets a Python TypeError exception if no such conversion is possible. +
~object(); ++
+
+ decrements the reference count of the internally-held object. +
PyObject* ptr() const;+
+
+ a pointer to the internally-held Python object. +
bool is_none() const;+
+
+ result of (ptr() == Py_None)
+
+ This template is instantiated with various Policies described in this document + in order to implement attribute, item, and slice access for object. It + stores an object of type Policies::key_type. +
+namespace boost { namespace python { namespace api +{ + template <class Policies> + class proxy : public object_operators<proxy<Policies> > + { + public: + operator object() const; + + proxy const& operator=(proxy const&) const; + template <class T> + inline proxy const& operator=(T const& rhs) const; + + void del() const; + + template <class R> + proxy operator+=(R const& rhs); + template <class R> + proxy operator-=(R const& rhs); + template <class R> + proxy operator*=(R const& rhs); + template <class R> + proxy operator/=(R const& rhs); + template <class R> + proxy operator%=(R const& rhs); + template <class R> + proxy operator<<=(R const& rhs); + template <class R> + proxy operator>>=(R const& rhs); + template <class R> + proxy operator&=(R const& rhs); + template <class R> + proxy operator|=(R const& rhs); + }; +}}} ++
operator object() const;+
+
+ applies Policies::get(target, key) with the proxy's target and key
+ objects.
+
proxy const& operator=(proxy const& rhs) const; +template <class T> +inline proxy const& operator=(T const& rhs) const; ++
+
+ Policies::set(target,
+ key ,
+ object(rhs))
+ with the proxy's target and key objects.
+
template <class R> +proxy operator+=(R const& rhs); +template <class R> +proxy operator-=(R const& rhs); +template <class R> +proxy operator*=(R const& rhs); +template <class R> +proxy operator/=(R const& rhs); +template <class R> +proxy operator%=(R const& rhs); +template <class R> +proxy operator<<=(R const& rhs); +template <class R> +proxy operator>>=(R const& rhs); +template <class R> +proxy operator&=(R const& rhs); +template <class R> +proxy operator|=(R const& rhs); ++
+
+ for a given operator@=,
+ object(*this) @= rhs;
+
+ *this
+
void del() const;+
+
+ Policies::del(target, key ) with the proxy's target and key objects. +
template <class T> +void del(proxy<T> const& x); ++
+
+ x.del()
+
template<class L,class R> object operator>(L const&l,R const&r); +template<class L,class R> object operator>=(L const&l,R const&r); +template<class L,class R> object operator<(L const&l,R const&r); +template<class L,class R> object operator<=(L const&l,R const&r); +template<class L,class R> object operator==(L const&l,R const&r); +template<class L,class R> object operator!=(L const&l,R const&r); ++
+
+ returns the result of applying the operator to object(l) and object(r), respectively, in Python.
+
template<class L,class R> object operator+(L const&l,R const&r); +template<class L,class R> object operator-(L const&l,R const&r); +template<class L,class R> object operator*(L const&l,R const&r); +template<class L,class R> object operator/(L const&l,R const&r); +template<class L,class R> object operator%(L const&l,R const&r); +template<class L,class R> object operator<<(L const&l,R const&r); +template<class L,class R> object operator>>(L const&l,R const&r); +template<class L,class R> object operator&(L const&l,R const&r); +template<class L,class R> object operator^(L const&l,R const&r); +template<class L,class R> object operator|(L const&l,R const&r); ++
+
+ returns the result of applying the operator to object(l) and object(r), respectively, in Python.
+
template<class R> object& operator+=(object&l,R const&r); +template<class R> object& operator-=(object&l,R const&r); +template<class R> object& operator*=(object&l,R const&r); +template<class R> object& operator/=(object&l,R const&r); +template<class R> object& operator%=(object&l,R const&r); +template<class R> object& operator<<=(object&l,R const&r) +template<class R> object& operator>>=(object&l,R const&r); +template<class R> object& operator&=(object&l,R const&r); +template<class R> object& operator^=(object&l,R const&r); +template<class R> object& operator|=(object&l,R const&r); ++
+
+ assigns to l the
+ result of applying the corresponding Python inplace operator to
+ l and object(r),
+ respectively.
+
+ l +
long len(object const& obj);+
+
+ PyObject_Length(obj.ptr())
+
+ len()
+ of object.
+
+ Python code: +
+def sum_items(seq): + result = 0 + for x in seq: + result += x + return result ++
+ C++ version +
+object sum_items(object seq) +{ + object result = object(0); + for (int i = 0; i < len(seq); ++i) + result += seq[i]; + return result; +} ++
| + | + |
![]() |
+ Exposes a TypeWrapper
+ for the Python slice type.
+
+ Exposes the extended slicing protocol by wrapping the built-in slice type.
+ The semantics of the constructors and member functions defined below can
+ be fully understood by reading the TypeWrapper
+ concept definition. Since slice
+ is publicly derived from object, the public object interface applies to slice instances as well.
+
namespace boost { namespace python +{ + class slice : public object + { + public: + slice(); // create an empty slice, equivalent to [::] + + template <typename Int1, typename Int2> + slice(Int1 start, Int2 stop); + + template <typename Int1, typename Int2, typename Int3> + slice(Int1 start, Int2 stop, Int3 step); + + // Access the parameters this slice was created with. + object start(); + object stop(); + object step(); + + // The return type of slice::get_indices() + template <typename RandomAccessIterator> + struct range + { + RandomAccessIterator start; + RandomAccessIterator stop; + int step; + }; + + template <typename RandomAccessIterator> + range<RandomAccessIterator> + get_indices( + RandomAccessIterator const& begin, + RandomAccessIterator const& end); + }; +}} ++
slice();+
+
+ constructs a slice with default stop, start, and step values. Equivalent
+ to the slice object created as part of the Python expression base[::].
+
+ nothing +
template <typename Int1, typename Int2> +slice(Int1 start, Int2 stop); ++
+
+ start, stop, and step
+ are of type slice_nil
+ or convertible to type object.
+
+ constructs a new slice with default step value and the provided start
+ and stop values. Equivalent to the slice object created by the built-in
+ Python function slice(start,stop), or as part of the Python expression
+ base[start:stop].
+
+ error_already_set
+ and sets a Python TypeError exception if no conversion is possible
+ from the arguments to type object.
+
template <typename Int1, typename Int2, typename Int3> +slice(Int1 start, Int2 stop, Int3 step); ++
+
+ start, stop, and step
+ are slice_nil or
+ convertible to type object.
+
+ constructs a new slice with start stop and step values. Equivalent
+ to the slice object created by the built-in Python function slice(start,stop,step),
+ or as part of the Python expression base[start:stop:step].
+
+ error_already_set
+ and sets a Python TypeError exception if no conversion is possible
+ from the arguments to type object.
+
object slice::start() const; +object slice::stop() const; +object slice::step() const; ++
+
+ None +
+ nothing +
+ the parameter that the slice was created with. If the parameter was
+ omitted or slice_nil
+ was used when the slice was created, than that parameter will be
+ a reference to PyNone
+ and compare equal to a default-constructed object. In principal,
+ any object may be used when creating a slice object, but in practice
+ they are usually integers.
+
template <typename RandomAccessIterator> +slice::range<RandomAccessIterator> +slice::get_indices( + RandomAccessIterator const& begin, + RandomAccessIterator const& end) const; ++
+
+ A pair of STL-conforming Random Access Iterators that form a half-open + range. +
+ Create a RandomAccessIterator pair that defines a fully-closed range
+ within the [begin,end) range of its arguments. This function
+ translates this slice's indices while accounting for the effects
+ of any PyNone or negative indices, and non-singular step sizes.
+
+ a slice::range that has been initialized
+ with a non-zero value of step and a pair of RandomAccessIterators
+ that point within the range of this functions arguments and define
+ a closed interval.
+
+ Raises a Python TypeError exception if any of this slice's arguments
+ are neither references to PyNone nor convertible to int. Throws
+ std::invalid_argument if the resulting
+ range would be empty. You should always wrap calls to slice::get_indices()
+ within try {
+ ...; }
+ catch (std::invalid_argument)
+ {} to handle this case and
+ take appropriate action.
+
+ closed-interval: If an open interval were used, then for step size + other than 1, the required state for the end iterator would point + beyond the one-past-the-end position or before the beginning of the + specified range. exceptions on empty slice: It is impossible to define + a closed interval over an empty range, so some other form of error + checking would have to be used to prevent undefined behavior. In + the case where the exception is not caught, it will simply be translated + to Python by the default exception handling mechanisms. +
using namespace boost::python; + +// Perform an extended slice of a Python list. +// Warning: extended slicing was not supported for built-in types prior +// to Python 2.3 +list odd_elements(list l) +{ + return l[slice(_,_,2)]; +} + +// Perform a multidimensional extended slice of a Numeric.array +numeric::array even_columns(numeric::array arr) +{ + // select every other column, starting with the second, of a 2-D array. + // Equivalent to "return arr[:, 1::2]" in Python. + return arr[make_tuple( slice(), slice(1,_,2))]; +} + +// Perform a summation over a slice of a std::vector. +double partial_sum(std::vector<double> const& Foo, const slice index) +{ + slice::range<std::vector<double>::const_iterator> bounds; + try { + bounds = index.get_indices<>(Foo.begin(), Foo.end()); + } + catch (std::invalid_argument) { + return 0.0; + } + double sum = 0.0; + while (bounds.start != bounds.stop) { + sum += *bounds.start; + std::advance( bounds.start, bounds.step); + } + sum += *bounds.start; + return sum; +} ++
| + | + |
![]() |
+ Exposes a TypeWrapper
+ for the Python str type.
+
str
+
+ Exposes the string
+ methods of Python's built-in str
+ type. The semantics of the constructors and member functions defined below,
+ except for the two-argument constructors which construct str objects from
+ a range of characters, can be fully understood by reading the TypeWrapper
+ concept definition. Since str is publicly derived from object, the public object interface applies to str instances as well.
+
namespace boost { namespace python +{ + class str : public object + { + public: + str(); // new str + + str(char const* s); // new str + + str(char const* start, char const* finish); // new str + str(char const* start, std::size_t length); // new str + + template <class T> + explicit str(T const& other); + + str capitalize() const; + + template <class T> + str center(T const& width) const; + + template<class T> + long count(T const& sub) const; + template<class T1, class T2> + long count(T1 const& sub,T2 const& start) const; + template<class T1, class T2, class T3> + long count(T1 const& sub,T2 const& start, T3 const& end) const; + + object decode() const; + template<class T> + object decode(T const& encoding) const; + template<class T1, class T2> + object decode(T1 const& encoding, T2 const& errors) const; + + object encode() const; + template <class T> + object encode(T const& encoding) const; + template <class T1, class T2> + object encode(T1 const& encoding, T2 const& errors) const; + + template <class T> + bool endswith(T const& suffix) const; + template <class T1, class T2> + bool endswith(T1 const& suffix, T2 const& start) const; + template <class T1, class T2, class T3> + bool endswith(T1 const& suffix, T2 const& start, T3 const& end) const; + + str expandtabs() const; + template <class T> + str expandtabs(T const& tabsize) const; + + template <class T> + long find(T const& sub) const; + template <class T1, class T2> + long find(T1 const& sub, T2 const& start) const; + template <class T1, class T2, class T3> + long find(T1 const& sub, T2 const& start, T3 const& end) const; + + template <class T> + long index(T const& sub) const; + template <class T1, class T2> + long index(T1 const& sub, T2 const& start) const; + template <class T1, class T2, class T3> + long index(T1 const& sub, T2 const& start, T3 const& end) const; + + bool isalnum() const; + bool isalpha() const; + bool isdigit() const; + bool islower() const; + bool isspace() const; + bool istitle() const; + bool isupper() const; + + template <class T> + str join(T const& sequence) const; + + template <class T> + str ljust(T const& width) const; + + str lower() const; + str lstrip() const; + + template <class T1, class T2> + str replace(T1 const& old, T2 const& new_) const; + template <class T1, class T2, class T3> + str replace(T1 const& old, T2 const& new_, T3 const& maxsplit) const; + + template <class T> + long rfind(T const& sub) const; + template <class T1, class T2> + long rfind(T1 const& sub, T2 const& start) const; + template <class T1, class T2, class T3> + long rfind(T1 const& sub, T2 const& start, T3 const& end) const; + + template <class T> + long rindex(T const& sub) const; + template <class T1, class T2> + long rindex(T1 const& sub, T2 const& start) const; + template <class T1, class T2, class T3> + long rindex(T1 const& sub, T2 const& start, T3 const& end) const; + + template <class T> + str rjust(T const& width) const; + + str rstrip() const; + + list split() const; + template <class T> + list split(T const& sep) const; + template <class T1, class T2> + list split(T1 const& sep, T2 const& maxsplit) const; + + list splitlines() const; + template <class T> + list splitlines(T const& keepends) const; + + template <class T> + bool startswith(T const& prefix) const; + template <class T1, class T2> + bool startswidth(T1 const& prefix, T2 const& start) const; + template <class T1, class T2, class T3> + bool startswidth(T1 const& prefix, T2 const& start, T3 const& end) const; + + str strip() const; + str swapcase() const; + str title() const; + + template <class T> + str translate(T const& table) const; + template <class T1, class T2> + str translate(T1 const& table, T2 const& deletechars) const; + + str upper() const; + }; +}} ++
using namespace boost::python; +str remove_angle_brackets(str x) +{ + return x.strip('<').strip('>'); +} ++
| + | + |
![]() |
+ Exposes a TypeWrapper + for the Python http://www.python.org/doc/current/tut/node7.html#SECTION007300000000000000000`tuple` + type. +
+
+ Exposes the interface of Python's built-in tuple type. The semantics of
+ the constructors and member functions defined below can be fully understood
+ by reading the TypeWrapper
+ concept definition. Since tuple is publicly derived from object, the public object interface applies to tuple instances as well.
+
namespace boost { namespace python +{ + class tuple : public object + { + // tuple() -> an empty tuple + tuple(); + + // tuple(sequence) -> tuple initialized from sequence's items + template <class T> + explicit tuple(T const& sequence) + }; +}} ++
namespace boost { namespace python +{ + tuple make_tuple(); + + template <class A0> + tuple make_tuple(A0 const& a0); + + template <class A0, class A1> + tuple make_tuple(A0 const& a0, A1 const& a1); + ... + template <class A0, class A1,...class An> + tuple make_tuple(A0 const& a0, A1 const& a1,...An const& an); +}} ++
+
+ Constructs a new tuple object composed of object(a0), object(a0),...object(an).
+
using namespace boost::python; +tuple head_and_tail(object sequence) +{ + return make_tuple(sequence[0],sequence[-1]); +} ++
| + | + |
![]() |
Table of Contents
+ +
+ Exposes a mechanism for extracting C++ object values from generalized Python
+ objects. Note that extract<...> can also be used to "downcast"
+ an object to some specific ObjectWrapper. Because invoking
+ a mutable python type with an argument of the same type (e.g. list([1,2]) typically makes a copy of the argument
+ object, this may be the only way to access the ObjectWrapper's
+ interface on the original object.
+
+ extract<T>
+ can be used to extract a value of an arbitrary C++ type from an instance
+ of object.
+ Two usages are supported:
+
extract<T>(o)
+ is a temporary object which is implicitly convertible to T (explicit conversion is also available
+ through the object's function-call operator). However, if no conversion
+ is available which can convert o to an object of type T, a Python TypeError exception will
+ be raised.
+ extract<T> x(o);
+ constructs an extractor whose check() member function can be used to ask
+ whether a conversion is available without causing an exception to be
+ thrown.
+ namespace boost { namespace python +{ + template <class T> + struct extract + { + typedef unspecified result_type; + + extract(PyObject*); + extract(object const&); + + result_type operator()() const; + operator result_type() const; + + bool check() const; + }; +}} ++
extract(PyObject* p); +extract(object const&); ++
+
+ The first form requires that p is non-null. +
+ Stores a pointer to the Python object managed by its constructor + argument. In particular, the reference count of the object is not + incremented. The onus is on the user to be sure it is not destroyed + before the extractor's conversion function is called. +
result_type operator()() const; +operator result_type() const; ++
+
+ Converts the stored pointer to result_type, which is either T or + T const&. +
+ An object of result_type corresponding to the one referenced by the + stored pointer. +
+ error_already_set and sets
+ a TypeError if no
+ such conversion is available. May also emit other unspecified exceptions
+ thrown by the converter which is actually used.
+
bool check() const;+
+
+ None. In particular, note that a return value of true does not preclude + an exception being thrown from operator result_type() or operator()(). +
+ false only if no conversion from the stored pointer to T is available. +
#include <cstdio> +using namespace boost::python; +int Print(str s) +{ + // extract a C string from the Python string object + char const* c_str = extract<char const*>(s); + + // Print it using printf + std::printf("%s\n", c_str); + + // Get the Python string's length and convert it to an int + return extract<int>(s.attr("__len__")()) +} ++
+ The following example shows how extract can be used along with class_<...>
+ to create and access an instance of a wrapped C++ class.
+
struct X +{ + X(int x) : v(x) {} + int value() { return v; } + private: + int v; +}; + +BOOST_PYTHON_MODULE(extract_ext) +{ + object x_class( + class_<X>("X", init<int>()) + .def("value", &X::value)) + ; + + // Instantiate an X object through the Python interface. + // Its lifetime is now managed by x_obj. + object x_obj = x_class(3); + + // Get a reference to the C++ object out of the Python object + X& x = extract<X&>(x_obj); + assert(x.value() == 3); +} ++
| + | + |
![]() |
+ implicitly_convertible
+ allows Boost.Python to implicitly take advantage of a C++ implicit or explicit
+ conversion when matching Python objects to C++ argument types.
+
template <class Source, class Target> +void implicitly_convertible(); ++
|
+ + Parameter + + |
+
+ + Description + + |
+
|---|---|
|
+ + Source + + |
+
+ + The source type of the implicit conversion + + |
+
|
+ + Target + + |
+
+ + The target type of the implicit conversion + + |
+
+
+ The declaration Target
+ t(s);,
+ where s is of type Source, is valid.
+
+ registers an rvalue from_python
+ converter to Target which can succeed for any PyObject* p
+ iff there exists any registered converter which can produce Source
+ rvalues
+
+ C++ users expect to be able to take advantage of the same sort of + interoperability in Python as they do in C++. +
+ In C++: +
+#include <boost/python/class.hpp> +#include <boost/python/implicit.hpp> +#include <boost/python/module.hpp> + +using namespace boost::python; + +struct X +{ + X(int x) : v(x) {} + operator int() const { return v; } + int v; +}; + +int x_value(X const& x) +{ + return x.v; +} + +X make_x(int n) { return X(n); } + +BOOST_PYTHON_MODULE(implicit_ext) +{ + def("x_value", x_value); + def("make_x", make_x); + + class_<X>("X", + init<int>()) + ; + + implicitly_convertible<X,int>(); + implicitly_convertible<int,X>(); +} ++
+ In Python: +
+>>> from implicit_ext import * +>>> x_value(X(42)) +42 +>>> x_value(42) +42 +>>> x = make_x(X(42)) +>>> x_value(x) +42 ++
| + | + |
![]() |
+ <boost/python/lvalue_from_pytype.hpp> supplies a facility for extracting + C++ objects from within Python instances of a given type. This is typically + useful for dealing with "traditional" Python extension types. +
++ Class template lvalue_from_pytype will register from_python converters + which, given an object of the given Python type, can extract references + and pointers to a particular C++ type. Its template arguments are: +
+In the table below, x denotes an object of type PythonObject& ++
|
+ + Parameter + + |
+
+ + Requirements + + |
+
+ + Semantics + + |
+
|---|---|---|
|
+ + Extractor + + |
+
+
+ a model of |
+
+ + Extracts the lvalue from the Python object once its type has + been confirmed + + |
+
|
+ + python_type + + |
+
+
+ A compile-time constant |
+
+ + The Python type of instances convertible by this converter. Python + subtypes are also convertible. + + |
+
namespace boost { namespace python +{ + template <class Extractor, PyTypeObject const* python_type> + struct lvalue_from_pytype + { + lvalue_from_pytype(); + }; +}} ++
lvalue_from_pytype();+
+
+ Registers converters which can convert Python objects of the given + type to lvalues of the type returned by Extractor::execute. +
+ extract_identity is a model of Extractor which can be used in
+ the common case where the C++ type to be extracted is the same as the Python
+ object type.
+
namespace boost { namespace python +{ + template <class InstanceType> + struct extract_identity + { + static InstanceType& execute(InstanceType& c); + }; +}} ++
InstanceType& execute(InstanceType& c);+
+
+ c +
+ extract_member is a model
+ of Extractor
+ which can be used in the common case in the common case where the C++ type
+ to be extracted is a member of the Python object.
+
namespace boost { namespace python +{ + template <class InstanceType, class MemberType, MemberType (InstanceType::*member)> + struct extract_member + { + static MemberType& execute(InstanceType& c); + }; +}} ++
static MemberType& execute(InstanceType& c);+
+
+ c.*member
+
+ This example presumes that someone has implemented the standard noddy example + module from the Python documentation, and we want to build a module which + manipulates Noddys. Since noddy_NoddyObject is so simple that it carries + no interesting information, the example is a bit contrived: it assumes + you want to keep track of one particular object for some reason. This module + would have to be dynamically linked to the module which defines noddy_NoddyType. +
++ In C++: +
+#include <boost/python/module.hpp> +#include <boost/python/handle.hpp> +#include <boost/python/borrowed.hpp> +#include <boost/python/lvalue_from_pytype.hpp> + +// definition lifted from the Python docs +typedef struct { + PyObject_HEAD +} noddy_NoddyObject; + +using namespace boost::python; +static handle<noddy_NoddyObject> cache; + +bool is_cached(noddy_NoddyObject* x) +{ + return x == cache.get(); +} + +void set_cache(noddy_NoddyObject* x) +{ + cache = handle<noddy_NoddyObject>(borrowed(x)); +} + +BOOST_PYTHON_MODULE(noddy_cache) +{ + def("is_cached", is_cached); + def("set_cache", set_cache); + + // register Noddy lvalue converter + lvalue_from_pytype<extract_identity<noddy_NoddyObject>,&noddy_NoddyType>(); +} ++
+ In Python: +
+>>> import noddy +>>> n = noddy.new_noddy() +>>> import noddy_cache +>>> noddy_cache.is_cached(n) +0 +>>> noddy_cache.set_cache(n) +>>> noddy_cache.is_cached(n) +1 +>>> noddy_cache.is_cached(noddy.new_noddy()) +0 ++
| + | + |
![]() |
+ opaque<>
+ registers itself as a converter from Python objects to pointers to undefined
+ types and vice versa.
+
namespace boost { namespace python +{ + template<class Pointee> + struct opaque + { + opaque(); + }; +}} ++
opaque();+
+
+ * Registers the instance as a lvalue_from_pytype converter
+ from Python objects into opaque pointers. The Python Objects created
+ are named after the type pointed to by the opaque pointer being wrapped.
+ * Registers the instance as a to_python_converter from
+ opaque pointers to Python objects.
+
![]() |
+Note | +
|---|---|
+ If there is already an instance registered by another module, this instance + doesn't try to register again in order to avoid warnings about multiple + registrations. + |
+ This macro must be used to define specializations of the type_id function which can't be
+ instantiated for incomplete types.
+
![]() |
+Note | +
|---|---|
+ The macro must be invoked in every translation unit which uses the opaque + converter. + |
| + | + |
![]() |
+ <boost/python/register_ptr_to_python.hpp> supplies register_ptr_to_python, a function template
+ which registers a conversion for smart pointers to Python. The resulting
+ Python object holds a copy of the converted smart pointer, but behaves
+ as though it were a wrapped copy of the pointee. If the pointee type has
+ virtual functions and the class representing its dynamic (most-derived)
+ type has been wrapped, the Python object will be an instance of the wrapper
+ for the most-derived type. More than one smart pointer type for a pointee's
+ class can be registered.
+
+ Note that in order to convert a Python X
+ object to a smart_ptr<X>&
+ (non-const reference), the embedded C++ object must be held by smart_ptr<X>,
+ and that when wrapped objects are created by calling the constructor from
+ Python, how they are held is determined by the HeldType parameter to class_<...>
+ instances.
+
template <class P> +void register_ptr_to_python() ++
+
+ P is Dereferenceable.
+
+ Allows conversions to-python of P instances. +
+ Here is an example of a module that contains a class A with virtual functions + and some functions that work with boost::shared_ptr<A>. +
++ In C++: +
+struct A +{ + virtual int f() { return 0; } +}; + +shared_ptr<A> New() { return shared_ptr<A>( new A() ); } + +int Ok( const shared_ptr<A>& a ) { return a->f(); } + +int Fail( shared_ptr<A>& a ) { return a->f(); } + +struct A_Wrapper: A +{ + A_Wrapper(PyObject* self_): self(self_) {} + int f() { return call_method<int>(self, "f"); } + int default_f() { return A::f(); } + PyObject* self; +}; + +BOOST_PYTHON_MODULE(register_ptr) +{ + class_<A, A_Wrapper>("A") + .def("f", &A::f, &A_Wrapper::default_f) + ; + + def("New", &New); + def("Ok", &Call); + def("Fail", &Fail); + + register_ptr_to_python< shared_ptr<A> >(); +} ++
+ In Python: +
+>>> from register_ptr import * +>>> a = A() +>>> Ok(a) # ok, passed as shared_ptr<A> +0 +>>> Fail(a) # passed as shared_ptr<A>&, and was created in Python! +Traceback (most recent call last): + File "<stdin>", line 1, in ? +TypeError: bad argument type for built-in operation +>>> +>>> na = New() # now "na" is actually a shared_ptr<A> +>>> Ok(a) +0 +>>> Fail(a) +0 +>>> ++
+ If shared_ptr<A> is registered as follows: +
+class_<A, A_Wrapper, shared_ptr<A> >("A") + .def("f", &A::f, &A_Wrapper::default_f) +; ++
+ There will be an error when trying to convert shared_ptr<A> to shared_ptr<A_Wrapper>: +
+>>> a = New() +Traceback (most recent call last): +File "<stdin>", line 1, in ? +TypeError: No to_python (by-value) converter found for C++ type: class boost::shared_ptr<struct A> +>>> ++
| + | + |
![]() |
+ to_python_converter registers
+ a conversion from objects of a given C++ type into a Python object.
+
+ to_python_converter adds
+ a wrapper around a static member function of its second template parameter,
+ handling low-level details such as insertion into the converter registry.
+
+ In the table below, x denotes an object of type T +
+|
+ + Parameter + + |
+
+ + Requirements + + |
+
+ + Description + + |
+
|---|---|---|
|
+ + T + + |
++ | +
+ + The C++ type of the source object in the conversion + + |
+
|
+ + Conversion + + |
+
+
+ |
+
+ + A class type whose static member function convert does the real + work of the conversion. + + |
+
|
+ + bool has_get_pytype=false + + |
+
+
+ |
+
+
+ Optional member - if Conversion has |
+
namespace boost { namespace python +{ + template <class T, class Conversion, bool convertion_has_get_pytype_member=false> + struct to_python_converter + { + to_python_converter(); + }; +}} ++
to_python_converter();+
+
+ Registers a to_python
+ converter which uses Conversion::convert() to do its work.
+
+ This example presumes that someone has implemented the standard noddy example + module from the Python documentation, and placed the corresponding declarations + in "noddy.h". Because noddy_NoddyObject is the ultimate trivial + extension type, the example is a bit contrived: it wraps a function for + which all information is contained in the type of its return value. +
++ In C++: +
+#include <boost/python/reference.hpp> +#include <boost/python/module.hpp> +#include "noddy.h" + +struct tag {}; +tag make_tag() { return tag(); } + +using namespace boost::python; + +struct tag_to_noddy +{ + static PyObject* convert(tag const& x) + { + return PyObject_New(noddy_NoddyObject, &noddy_NoddyType); + } + static PyTypeObject const* get_pytype() + { + return &noddy_NoddyType; + } +}; + +BOOST_PYTHON_MODULE(to_python_converter) +{ + def("make_tag", make_tag); + to_python_converter<tag, tag_to_noddy, true>(); //"true" because tag_to_noddy has member get_pytype +} ++
+ In Python: +
+>>> import to_python_converter +>>> def always_none(): +... return None +... +>>> def choose_function(x): +... if (x % 2 != 0): +... return to_python_converter.make_tag +... else: +... return always_none +... +>>> a = [ choose_function(x) for x in range(5) ] +>>> b = [ f() for f in a ] +>>> type(b[0]) +<type 'NoneType'> +>>> type(b[1]) +<type 'Noddy'> +>>> type(b[2]) +<type 'NoneType'> +>>> type(b[3]) +<type 'Noddy'> ++
| + | + |
![]() |
Table of Contents
+ +
+ The simplest way to call a Python function from C++, given an object instance f holding the
+ function, is simply to invoke its function call operator.
+
f("tea", 4, 2) // In Python: f('tea', 4, 2)+
+ And of course, a method of an object instance x can be invoked by using the function-call
+ operator of the corresponding attribute:
+
x.attr("tea")(4, 2); // In Python: x.tea(4, 2)+
+ If you don't have an object instance, Boost.Python provides two families of function
+ templates, call and call_method, for invoking Python
+ functions and methods respectively on PyObject*s. The interface for calling a Python function
+ object (or any Python callable object) looks like:
+
call<ResultType>(callable_object, a1, a2... aN);+
+ Calling a method of a Python object is similarly easy: +
+call_method<ResultType>(self_object, "method-name", a1, a2... aN);+
+ This comparitively low-level interface is the one you'll use when implementing + C++ virtual functions that can be overridden in Python. +
+
+ Arguments are converted to Python according to their type. By default,
+ the arguments a1...aN are copied into new Python objects,
+ but this behavior can be overridden by the use of ptr()
+ and ref():
+
class X : boost::noncopyable +{ + ... +}; + +void apply(PyObject* callable, X& x) +{ + // Invoke callable, passing a Python object which holds a reference to x + boost::python::call<void>(callable, boost::ref(x)); +} ++
+ In the table below, x denotes the actual argument object and cv denotes + an optional cv-qualification: "const", "volatile", + or "const volatile". +
+|
+ + Argument Type + + |
+
+ + Behavior + + |
+
|---|---|
|
+
+ |
+
+ + The Python argument is created by the same means used for the + return value of a wrapped C++ function returning T. When T is + a class type, that normally means *x is copy-constructed into + the new Python object. + + |
+
|
+ + T* + + |
+
+ + If x == 0, the Python argument will be None. Otherwise, the Python + argument is created by the same means used for the return value + of a wrapped C++ function returning T. When T is a class type, + that normally means *x is copy-constructed into the new Python + object. + + |
+
|
+ + boost::reference_wrapper<T> + + |
+
+ + The Python argument contains a pointer to, rather than a copy + of, x.get(). Note: failure to ensure that no Python code holds + a reference to the resulting object beyond the lifetime of *x.get() + may result in a crash! + + |
+
|
+ + pointer_wrapper<T> + + |
+
+ + If x.get() == 0, the Python argument will be None. Otherwise, + the Python argument contains a pointer to, rather than a copy + of, *x.get(). Note: failure to ensure that no Python code holds + a reference to the resulting object beyond the lifetime of *x.get() + may result in a crash! + + |
+
+ In general, call<ResultType>()
+ and call_method<ResultType>() return ResultType by exploiting all
+ lvalue and rvalue from_python converters registered for ResultType and
+ returning a copy of the result. However, when ResultType is a pointer or
+ reference type, Boost.Python searches only for lvalue converters. To prevent
+ dangling pointers and references, an exception will be thrown if the Python
+ result object has only a single reference count.
+
+ In general, to get Python arguments corresponding to a1...aN, a new Python + object must be created for each one; should the C++ object be copied into + that Python object, or should the Python object simply hold a reference/pointer + to the C++ object? In general, the latter approach is unsafe, since the + called function may store a reference to the Python object somewhere. If + the Python object is used after the C++ object is destroyed, we'll crash + Python. +
++ In keeping with the philosophy that users on the Python side shouldn't + have to worry about crashing the interpreter, the default behavior is to + copy the C++ object, and to allow a non-copying behavior only if the user + writes boost::ref(a1) instead of a1 directly. At least this way, the user + doesn't get dangerous behavior "by accident". It's also worth + noting that the non-copying ("by-reference") behavior is in general + only available for class types, and will fail at runtime with a Python + exception if used otherwise[1]. +
++ However, pointer types present a problem: one approach is to refuse to + compile if any aN has pointer type: after all, a user can always pass *aN + to pass "by-value" or ref(*aN) to indicate a pass-by-reference + behavior. However, this creates a problem for the expected null pointer + to None conversion: it's illegal to dereference a null pointer value. +
++ The compromise I've settled on is this: +
++ As for results, we have a similar problem: if ResultType is allowed to + be a pointer or reference type, the lifetime of the object it refers to + is probably being managed by a Python object. When that Python object is + destroyed, our pointer dangles. The problem is particularly bad when the + ResultType is char const* - the corresponding Python String object is typically + uniquely-referenced, meaning that the pointer dangles as soon as call<char + const*>(...) returns. +
++ The old Boost.Python v1 deals with this issue by refusing to compile any + uses of call<char const*>(), but this goes both too far and not far + enough. It goes too far because there are cases where the owning Python + string object survives beyond the call (just for instance, when it's the + name of a Python class), and it goes not far enough because we might just + as well have the same problem with a returned pointer or reference of any + other type. +
++ In Boost.Python this is dealt with by: +
+char
+ const *
+ callback returns
+ call<U>(...) when U
+ is a pointer or reference type.
+
+ This should be acceptably safe because users have to explicitly specify
+ a pointer/reference for U
+ in call<U>,
+ and they will be protected against dangles at runtime, at least long enough
+ to get out of the call<U>(...) invocation.
+
| + | + |
![]() |
+ Indexing is a Boost Python
+ facility for easy exportation of indexable C++ containers to Python. Indexable
+ containers are containers that allow random access through the operator[]
+ (e.g. std::vector).
+
+ While Boost Python
+ has all the facilities needed to expose indexable C++ containers such as
+ the ubiquitous std::vector to Python, the procedure is not as straightforward
+ as we'd like it to be. Python containers do not map easily to C++ containers.
+ Emulating Python containers in C++ (see Python Reference Manual, Emulating
+ container types) using Boost.Python
+ is non trivial. There are a lot of issues to consider before we can map
+ a C++ container to Python. These involve implementing wrapper functions
+ for the methods __len__,
+ __getitem__, __setitem__, __delitem__,
+ __iter__ and __contains__.
+
+ The goals: +
+__getitem__) such
+ that c[i] can be mutable. Require:
+val = c[i] +c[i].m() +val == c[i] ++
+ where m is a non-const (mutating) member function (method). +
+__getitem__
+ such that subsequent adds and deletes to and from the container will
+ not result in dangling references (will not crash Python).
+ lists,
+ tuples) wherever appropriate.
+
+ The indexing_suite class
+ is the base class for the management of C++ containers intended to be integrated
+ to Python. The objective is make a C++ container look and feel and behave
+ exactly as we'd expect a Python container. The class automatically wraps
+ these special Python methods (taken from the Python reference: Emulating
+ container types):
+
+
+ Called to implement the built-in function len(). Should return the length of the
+ object, an integer >= 0. Also, an object that doesn't define
+ a __nonzero__()
+ method and whose __len__() method returns zero is considered
+ to be false in a Boolean context.
+
+ Called to implement evaluation of self[key]. For sequence types, the accepted
+ keys should be integers and slice objects. Note that the special
+ interpretation of negative indexes (if the class wishes to emulate
+ a sequence type) is up to the __getitem__() method. If key is of an inappropriate
+ type, TypeError may
+ be raised; if of a value outside the set of indexes for the sequence
+ (after any special interpretation of negative values), IndexError
+ should be raised. [Note: for loops expect that an IndexError will
+ be raised for illegal indexes to allow proper detection of the end
+ of the sequence.]
+
+ Called to implement assignment to self[key]. Same note as for __getitem__(). + This should only be implemented for mappings if the objects support + changes to the values for keys, or if new keys can be added, or for + sequences if elements can be replaced. The same exceptions should + be raised for improper key values as for the __getitem__() method. +
+ Called to implement deletion of self[key]. Same note as for __getitem__(). + This should only be implemented for mappings if the objects support + removal of keys, or for sequences if elements can be removed from + the sequence. The same exceptions should be raised for improper key + values as for the __getitem__() method. +
+ This method is called when an iterator is required for a container. + This method should return a new iterator object that can iterate + over all the objects in the container. For mappings, it should iterate + over the keys of the container, and should also be made available + as the method iterkeys(). +
++ Iterator objects also need to implement this method; they are required + to return themselves. For more information on iterator objects, see + Iterator + Types in the Python + Library Reference. +
++ Called to implement membership test operators. Should return true + if item is in self, false otherwise. For mapping objects, this should + consider the keys of the mapping rather than the values or the key-item + pairs. +
+ The indexing_suite is not
+ meant to be used as is. A couple of policy functions must be supplied by
+ subclasses of indexing_suite.
+ However, a set of indexing_suite subclasses for the standard indexable
+ STL containers will be provided, In most cases, we can simply use the available
+ predefined suites. In some cases, we can refine the predefined suites to
+ suit our needs.
+
+ The vector_indexing_suite
+ class is a predefined indexing_suite
+ derived class designed to wrap std::vector
+ (and std::vector-like [i.e. a class with std::vector interface]) classes. It provides
+ all the policies required by the indexing_suite.
+
+ Example usage: +
+class X {...}; +... +class_<std::vector<X> >("XVec") + .def(vector_indexing_suite<std::vector<X> >()) +; ++
+ XVec is now a full-fledged Python container (see the example in full, + along with its python test). +
+
+ The map_indexing_suite
+ class is a predefined indexing_suite
+ derived class designed to wrap std::map
+ (and std::map-like [i.e. a class with std::map interface]) classes. It provides
+ all the policies required by the indexing_suite.
+
+ Example usage: +
+class X {...}; +... + +class_<std::map<X> >("XMap") + .def(map_indexing_suite<std::map<X> >()) +; ++
+ By default indexed elements are returned by proxy. This can be disabled
+ by supplying true in the
+ NoProxy template parameter.
+ XMap is now a full-fledged Python container (see the example in full,
+ along with its python test).
+
|
+ + Template Parameter + + |
+
+ + Requirements + + |
+
+ + Semantics + + |
+
+ + Default + + |
+
|---|---|---|---|
|
+ + Container + + |
+
+ + A class type + + |
+
+ + The container type to be wrapped to Python. + + |
++ | +
|
+ + DerivedPolicies + + |
+
+ + A subclass of indexing_suite + + |
+
+ + Derived classes provide the policy hooks. See DerivedPolicies + below. + + |
++ | +
|
+ + NoProxy + + |
+
+ + A boolean + + |
+
+ + By default indexed elements have Python reference semantics and + are returned by proxy. This can be disabled by supplying true + in the NoProxy template parameter. + + |
+
+ + false + + |
+
|
+ + NoSlice + + |
+
+ + A boolean + + |
+
+ + Do not allow slicing. + + |
+
+ + false + + |
+
|
+ + Data + + |
++ | +
+ + The container's data type. + + |
+
+ + Container::value_type + + |
+
|
+ + Index + + |
++ | +
+ + The container's index type. + + |
+
+ + Container::size_type + + |
+
|
+ + Key + + |
++ | +
+ + The container's key type. + + |
+
+ + Container::value_type + + |
+
template <class Container, + class DerivedPolicies, + bool NoProxy = false, + bool NoSlice = false, + class Data = typename Container::value_type, + class Index = typename Container::size_type, + class Key = typename Container::value_type> +class indexing_suite : unspecified +{ +public: + indexing_suite(); // default constructor +} ++
+ Derived classes provide the hooks needed by the indexing_suite: +
+data_type& +get_item(Container& container, index_type i); + +static object +get_slice(Container& container, index_type from, index_type to); + +static void +set_item(Container& container, index_type i, data_type const& v); + +static void +set_slice( + Container& container, index_type from, + index_type to, data_type const& v +); + +template <class Iter> +static void +set_slice(Container& container, index_type from, + index_type to, Iter first, Iter last +); + +static void +delete_item(Container& container, index_type i); + +static void +delete_slice(Container& container, index_type from, index_type to); + +static size_t +size(Container& container); + +template <class T> +static bool +contains(Container& container, T const& val); + +static index_type +convert_index(Container& container, PyObject* i); + +static index_type +adjust_index(index_type current, index_type from, + index_type to, size_type len); ++
+ Most of these policies are self explanatory. However, convert_index and + adjust_index deserve some explanation. +
++ convert_index converts a Python index into a C++ index that the container + can handle. For instance, negative indexes in Python, by convention, + start counting from the right(e.g. C1 + indexes the rightmost element in C). convert_index should handle the + necessary conversion for the C++ container (e.g. convert -1 to C.size()-1). + convert_index should also be able to convert the type of the index (A + dynamic Python type) to the actual type that the C++ container expects. +
++ When a container expands or contracts, held indexes to its elements must + be adjusted to follow the movement of data. For instance, if we erase + 3 elements, starting from index 0 from a 5 element vector, what used + to be at index 4 will now be at index 1: +
+[a][b][c][d][e] ---> [d][e] + ^ ^ + 4 1 ++
+ adjust_index takes care of the adjustment. Given a current index, the + function should return the adjusted index when data in the container + at index from..to is replaced by len elements. +
+|
+ + Template Parameter + + |
+
+ + Requirements + + |
+
+ + Semantics + + |
+
+ + Default + + |
+
|---|---|---|---|
|
+ + Container + + |
+
+ + A class type + + |
+
+ + The container type to be wrapped to Python. + + |
++ | +
|
+ + NoProxy + + |
+
+ + A boolean + + |
+
+ + By default indexed elements have Python reference semantics and + are returned by proxy. This can be disabled by supplying true + in the NoProxy template parameter. + + |
+
+ + false + + |
+
|
+ + DerivedPolicies + + |
+
+ + A subclass of indexing_suite + + |
+
+ + The vector_indexing_suite may still be derived to further tweak + any of the predefined policies. Static polymorphism through CRTP + (James Coplien. "Curiously Recurring Template Pattern". + C++ Report, Feb. 1995) enables the base indexing_suite class + to call policy function of the most derived class + + |
++ | +
template <class Container, + bool NoProxy = false, + class DerivedPolicies = unspecified_default> +class vector_indexing_suite : unspecified_base +{ +public: + + typedef typename Container::value_type data_type; + typedef typename Container::value_type key_type; + typedef typename Container::size_type index_type; + typedef typename Container::size_type size_type; + typedef typename Container::difference_type difference_type; + + data_type& + get_item(Container& container, index_type i); + + static object + get_slice(Container& container, index_type from, index_type to); + + static void + set_item(Container& container, index_type i, data_type const& v); + + static void + set_slice(Container& container, index_type from, + index_type to, data_type const& v); + + template <class Iter> + static void + set_slice(Container& container, index_type from, + index_type to, Iter first, Iter last); + + static void + delete_item(Container& container, index_type i); + + static void + delete_slice(Container& container, index_type from, index_type to); + + static size_t + size(Container& container); + + static bool + contains(Container& container, key_type const& key); + + static index_type + convert_index(Container& container, PyObject* i); + + static index_type + adjust_index(index_type current, index_type from, + index_type to, size_type len); +}; ++
|
+ + Template Parameter + + |
+
+ + Requirements + + |
+
+ + Semantics + + |
+
+ + Default + + |
+
|---|---|---|---|
|
+ + Container + + |
+
+ + A class type + + |
+
+ + The container type to be wrapped to Python. + + |
++ | +
|
+ + NoProxy + + |
+
+ + A boolean + + |
+
+ + By default indexed elements have Python reference semantics and + are returned by proxy. This can be disabled by supplying true + in the NoProxy template parameter. + + |
+
+ + false + + |
+
|
+ + DerivedPolicies + + |
+
+ + A subclass of indexing_suite + + |
+
+ + The vector_indexing_suite may still be derived to further tweak + any of the predefined policies. Static polymorphism through CRTP + (James Coplien. "Curiously Recurring Template Pattern". + C++ Report, Feb. 1995) enables the base indexing_suite class + to call policy function of the most derived class + + |
++ | +
template <class Container, + bool NoProxy = false, + class DerivedPolicies = unspecified_default> +class map_indexing_suite : unspecified_base +{ +public: + + typedef typename Container::value_type value_type; + typedef typename Container::value_type::second_type data_type; + typedef typename Container::key_type key_type; + typedef typename Container::key_type index_type; + typedef typename Container::size_type size_type; + typedef typename Container::difference_type difference_type; + + static data_type& + get_item(Container& container, index_type i); + + static void + set_item(Container& container, index_type i, data_type const& v); + + static void + delete_item(Container& container, index_type i); + + static size_t + size(Container& container); + + static bool + contains(Container& container, key_type const& key); + + static bool + compare_index(Container& container, index_type a, index_type b); + + static index_type + convert_index(Container& container, PyObject* i); +}; ++
| + | + |
![]() |
+ Pickle is a Python module for object serialization, also known as persistence, + marshalling, or flattening. +
++ 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 through the interface
+ as described in detail in the Python
+ Library Reference for pickle. This interface involves the special
+ methods __getinitargs__,
+ __getstate__ and __setstate__ as described in the following.
+ Note that Boost.Python is also fully compatible with
+ Python's cPickle module.
+
+ At the user level, the Boost.Python pickle interface involves three special + methods: +
++
+ When an instance of a Boost.Python extension class is pickled, the
+ pickler tests if the instance has a __getinitargs__
+ method. This method must return a Python tuple
+ (it is most convenient to use a boost::python::tuple). When the instance
+ is restored by the unpickler, the contents of this tuple are used
+ as the arguments for the class constructor.
+
+ If __getinitargs__
+ is not defined, pickle.load
+ will call the constructor (__init__)
+ without arguments; i.e., the object must be default-constructible.
+
+ When an instance of a Boost.Python
+ extension class is pickled, the pickler tests if the instance has
+ a __getstate__ method.
+ This method should return a Python object representing the state
+ of the instance.
+
+ When an instance of a Boost.Python
+ extension class is restored by the unpickler (pickle.load),
+ it is first constructed using the result of __getinitargs__
+ as arguments (see above). Subsequently the unpickler tests if the
+ new instance has a __setstate__
+ method. If so, this method is called with the result of __getstate__ (a Python object)
+ as the argument.
+
+ The three special methods described above may be .def()'ed
+ individually by the user. However, Boost.Python
+ provides an easy to use high-level interface via the boost::python::pickle_suite
+ class that also enforces consistency: __getstate__
+ and __setstate__ must be
+ defined as pairs. Use of this interface is demonstrated by the following
+ examples.
+
+ There are three files in python/test
+ that show how to provide pickle support.
+
+ The C++ class in this example can be fully restored by passing the appropriate
+ argument to the constructor. Therefore it is sufficient to define the
+ pickle interface method __getinitargs__.
+ This is done in the following way: Definition of the C++ pickle function:
+
struct world_pickle_suite : boost::python::pickle_suite +{ + static + boost::python::tuple + getinitargs(world const& w) + { + return boost::python::make_tuple(w.get_country()); + } +}; ++
+ Establishing the Python binding: +
+class_<world>("world", args<const std::string&>()) + // ... + .def_pickle(world_pickle_suite()) + // ... ++
+ The C++ class in this example contains member data that cannot be restored
+ by any of the constructors. Therefore it is necessary to provide the
+ __getstate__/__setstate__ pair of pickle interface
+ methods:
+
+ Definition of the C++ pickle functions: +
+struct world_pickle_suite : boost::python::pickle_suite + { + static + boost::python::tuple + getinitargs(const world& w) + { + // ... + } + + static + boost::python::tuple + getstate(const world& w) + { + // ... + } + + static + void + setstate(world& w, boost::python::tuple state) + { + // ... + } + }; ++
+ Establishing the Python bindings for the entire suite: +
+class_<world>("world", args<const std::string&>()) + // ... + .def_pickle(world_pickle_suite()) + // ... ++
+ 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 guard described below will
+ catch the cases where this assumption is violated.
+
+ This example is similar to pickle2.cpp. However, the object's __dict__ is included in the result
+ of __getstate__. This
+ requires a little more code but is unavoidable if the object's __dict__ is not always empty.
+
+ The pickle protocol described above has an important pitfall that the end + user of a Boost.Python extension module might not be aware of: +
+
+ __getstate__
+ is defined and the instance's __dict__
+ is not empty.
+
+ The author of a Boost.Python extension class might provide
+ a __getstate__ method without
+ considering the possibilities that: * his class is used in Python as a
+ base class. Most likely the __dict__
+ of instances of the derived class needs to be pickled in order to restore
+ the instances correctly. * the user adds items to the instance's __dict__ directly. Again, the __dict__ of the instance then needs to
+ be pickled.
+
+ 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 either at the C++ or the Python level.
+ Finally, the safety guard should intentionally be overridden. E.g. in C++
+ (from pickle3.cpp):
+
struct world_pickle_suite : boost::python::pickle_suite +{ + // ... + + static bool getstate_manages_dict() { return true; } +}; ++
+ Alternatively 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 ++
Boost.Python extension modules with many
+ extension classes, providing complete pickle support for all classes
+ would be a significant overhead. In general complete pickle support
+ should only be implemented for extension classes that will eventually
+ be pickled.
+ __getstate__
+ if the instance can also be reconstructed by way of __getinitargs__.
+ This automatically avoids the pitfall described above.
+ __getstate__ is
+ required, include the instance's __dict__
+ in the Python object that is returned.
+ + The pickle4.cpp example demonstrates an alternative technique for implementing + pickle support. First we direct Boost.Python via the class_::enable_pickling() + member function to define only the basic attributes required for pickling: +
+class_<world>("world", args<const std::string&>()) + // ... + .enable_pickling() + // ... ++
+ This enables the standard Python pickle interface as described in the Python
+ documentation. By "injecting" a __getinitargs__
+ method into the definition of the wrapped class we make all instances pickleable:
+
# import the wrapped world class +from pickle4_ext import world + +# definition of __getinitargs__ +def world_getinitargs(self): + return (self.get_country(),) + +# now inject __getinitargs__ (Python is a dynamic language!) +world.__getinitargs__ = world_getinitargs ++
+ See also the tutorial section on injecting additional methods from Python. +
+| + | + |
![]() |
Table of Contents
+ +
+ <boost/python/has_back_reference.hpp> defines the predicate metafunction
+ has_back_reference<>,
+ which can be specialized by the user to indicate that a wrapped class instance
+ holds a PyObject*
+ corresponding to a Python object.
+
+ A unary metafunction whose value is true iff its argument is a pointer_wrapper<>.
+
namespace boost { namespace python +{ + template<class WrappedClass> class has_back_reference + { + typedef mpl::false_ type; + }; +}} ++
+ A metafunction that is inspected by Boost.Python to determine how wrapped + classes can be constructed. +
+
+ type::value is an integral constant convertible
+ to bool of unspecified type. Specializations may substitute a true-valued
+ integral constant wrapper for type iff for each invocation of class_<WrappedClass>::def(init< type-sequence...>())
+ and the implicitly wrapped copy constructor (unless it is noncopyable),
+ there exists a corresponding constructor WrappedClass::WrappedClass(PyObject*, type-sequence...). If such a specialization exists, the
+ WrappedClass constructors will be called with a "back reference"
+ pointer to the corresponding Python object whenever they are invoked from
+ Python. The easiest way to provide this nested type is to derive the specialization
+ from mpl::true_.
+
+ In C++: +
+#include <boost/python/class.hpp> +#include <boost/python/module.hpp> +#include <boost/python/has_back_reference.hpp> +#include <boost/python/handle.hpp> +#include <boost/shared_ptr.hpp> + +using namespace boost::python; +using boost::shared_ptr; + +struct X +{ + X(PyObject* self) : m_self(self), m_x(0) {} + X(PyObject* self, int x) : m_self(self), m_x(x) {} + X(PyObject* self, X const& other) : m_self(self), m_x(other.m_x) {} + + handle<> self() { return handle<>(borrowed(m_self)); } + int get() { return m_x; } + void set(int x) { m_x = x; } + + PyObject* m_self; + int m_x; +}; + +// specialize has_back_reference for X +namespace boost { namespace python +{ + template <> + struct has_back_reference<X> + : mpl::true_ + {}; +}} + +struct Y +{ + Y() : m_x(0) {} + Y(int x) : m_x(x) {} + int get() { return m_x; } + void set(int x) { m_x = x; } + + int m_x; +}; + +shared_ptr<Y> +Y_self(shared_ptr<Y> self) { return self; } + +BOOST_PYTHON_MODULE(back_references) +{ + class_<X>("X") + .def(init<int>()) + .def("self", &X::self) + .def("get", &X::get) + .def("set", &X::set) + ; + + class_<Y, shared_ptr<Y> >("Y") + .def(init<int>()) + .def("get", &Y::get) + .def("set", &Y::set) + .def("self", Y_self) + ; +} ++
+ The following Python session illustrates that x.self() returns the same + Python object on which it is invoked, while y.self() must create a new + Python object which refers to the same Y instance. +
++ In Python: +
+>>> from back_references import * +>>> x = X(1) +>>> x2 = x.self() +>>> x2 is x +1 +>>> (x.get(), x2.get()) +(1, 1) +>>> x.set(10) +>>> (x.get(), x2.get()) +(10, 10) +>>> +>>> +>>> y = Y(2) +>>> y2 = y.self() +>>> y2 is y +0 +>>> (y.get(), y2.get()) +(2, 2) +>>> y.set(20) +>>> (y.get(), y2.get()) +(20, 20) ++
| + | + |
![]() |
+ <boost/python/handle.hpp> provides class template handle,
+ a smart pointer for managing reference-counted Python objects.
+
+ handle is a smart pointer
+ to a Python object type; it holds a pointer of type T*, where T
+ is its template parameter. T must be either a type derived from PyObject or a POD
+ type whose initial sizeof(PyObject) bytes are layout-compatible with PyObject. Use handle<> at the boundary between the Python/'C'
+ API and high-level code; prefer object for a generalized interface to Python
+ objects.
+
+ In this document, the term "upcast" refers to an operation which
+ converts a pointer Y*
+ to a base class pointer T* via
+ static_cast<T*>
+ if Y is derived from T, or via C-style cast (T*) if
+ it is not. However, in the latter case the "upcast" is ill-formed
+ if the initial sizeof(PyObject)
+ bytes of Y are not layout-compatible
+ with PyObject.
+
namespace boost { namespace python +{ + template <class T> + class handle + { + typedef unspecified-member-function-pointer bool_type; + + public: // types + typedef T element_type; + + public: // member functions + ~handle(); + + template <class Y> + explicit handle(detail::borrowed<null_ok<Y> >* p); + + template <class Y> + explicit handle(null_ok<detail::borrowed<Y> >* p); + + template <class Y> + explicit handle(detail::borrowed<Y>* p); + + template <class Y> + explicit handle(null_ok<Y>* p); + + template <class Y> + explicit handle(Y* p); + + handle(); + + handle& operator=(handle const& r); + + template<typename Y> + handle& operator=(handle<Y> const & r); // never throws + + + template <typename Y> + handle(handle<Y> const& r); + + handle(handle const& r); + + T* operator-> () const; + T& operator* () const; + T* get() const; + void reset(); + T* release(); + + operator bool_type() const; // never throws + private: + T* m_p; + }; + + template <class T> struct null_ok; + namespace detail { template <class T> struct borrowed; } +}} ++
virtual ~handle();+
+
+ Py_XDECREF(upcast<PyObject*>(m_p))
+
template <class Y> +explicit handle(detail::borrowed<null_ok<Y> >* p); ++
+
Py_XINCREF(upcast<PyObject*>(p)); +m_p = upcast<T*>(p); +
template <class Y> +explicit handle(null_ok<detail::borrowed<Y> >* p);+
+
Py_XINCREF(upcast<PyObject*>(p)); + m_p = upcast<T*>(p); +
template <class Y> +explicit handle(detail::borrowed<Y>* p);+
+
Py_XINCREF(upcast<PyObject*>(p)); + m_p = upcast<T*>(expect_non_null(p)); +
template <class Y> +explicit handle(null_ok<Y>* p); ++
+
+ m_p =
+ upcast<T*>(p);
+
template <class Y> +explicit handle(Y* p); ++
+
+ m_p =
+ upcast<T*>(expect_non_null(p));
+
handle(); ++
+
+ m_p =
+ 0;
+
template <typename Y> +handle(handle<Y> const& r); +handle(handle const& r); ++
+
+ m_p = r.m_p; Py_XINCREF(upcast<PyObject*>(m_p)); +
handle& operator=(handle const& r); +template<typename Y> +handle& operator=(handle<Y> const & r); // never throws ++
+
+ Py_XINCREF(upcast<PyObject*>(r.m_p));
+ Py_XDECREF(
+ upcast<PyObject*>(m_p));
+ m_p =
+ r.m_p;
+
T* release(); ++
+
+ T*
+ x =
+ m_p;
+ m_p =
+ 0;
+ return x;
+
void reset(); ++
+
+ *this
+ = handle<T>();
+
T* operator-> () const; +T* get() const; ++
+
+ m_p;
+
T& operator* () const; ++
+
+ *m_p;
+
operator bool_type() const; // never throws ++
+
+ 0 if m_p ==
+ 0, a pointer convertible
+ to true otherwise.
+
template <class T> +detail::borrowed<T>* borrowed(T* p) +{ + return (detail::borrowed<T>*)p; +} ++
template <class T> +null_ok<T>* allow_null(T* p) +{ + return (null_ok<T>*)p; +} ++
| + | + |
![]() |
+ <boost/python/instance_holder.hpp> provides class instance_holder,
+ the base class for types which hold C++ instances of wrapped classes.
+
+ instance_holder is an abstract
+ base class whose concrete derived classes hold C++ class instances within
+ their Python object wrappers. To allow multiple inheritance in Python from
+ C++ class wrappers, each such Python object contains a chain of instance_holders.
+ When an __init__ function
+ for a wrapped C++ class is invoked, a new instance_holder
+ instance is created and installed in the Python object using its install()
+ function. Each concrete class derived from instance_holder
+ must provide a holds()
+ implementation which allows Boost.Python to query it for the type(s) it
+ is holding. In order to support the held type's wrapped constructor(s),
+ the class must also provide constructors that can accept an initial PyObject*
+ argument referring to the owning Python object, and which forward the rest
+ of their arguments to the constructor of the held type. The initial argument
+ is needed to enable virtual function overriding in Python, and may be ignored,
+ depending on the specific instance_holder
+ subclass.
+
namespace boost { namespace python +{ + class instance_holder : noncopyable + { + public: + // destructor + virtual ~instance_holder(); + + // instance_holder modifiers + void install(PyObject* inst) throw(); + + // instance_holder observers + virtual void* holds(type_info) = 0; + }; +}} ++
virtual ~instance_holder();+
+
+ destroys the object +
void install(PyObject* inst) throw();+
+
+ inst is a Python
+ instance of a wrapped C++ class type, or is a type derived from
+ a wrapped C++ class type.
+
+ installs the new instance at the head of the Python object's chain + of held instances. +
+ nothing +
virtual void *holds(type_info x) = 0;+
+
+ A pointer to an object of the type described by x
+ if *this
+ contains such an object, 0 otherwise.
+
+ The following is a simplified version of the instance holder template used + by Boost.Python to wrap classes held by smart pointers: +
+template <class SmartPtr, class Value> +struct pointer_holder : instance_holder +{ + // construct from the SmartPtr type + pointer_holder(SmartPtr p) + :m_p(p) + + // Forwarding constructors for the held type + pointer_holder(PyObject*) + :m_p(new Value()) + { + } + + template<class A0> + pointer_holder(PyObject*,A0 a0) + :m_p(new Value(a0)) + { + } + + template<class A0,class A1> + pointer_holder(PyObject*,A0 a0,A1 a1) + :m_p(new Value(a0,a1)) + { + } + ... + + private: // required holder implementation + void* holds(type_info dst_t) + { + // holds an instance of the SmartPtr type... + if (dst_t == python::type_id<SmartPtr>()) + return &this->m_p; + + // ...and an instance of the SmartPtr's element_type, if the + // pointer is non-null + return python::type_id<Value>() == dst_t ? &*this->m_p : 0; + } + + private: // data members + SmartPtr m_p; +}; ++
| + | + |
![]() |
+ <boost/python/pointee.hpp> introduces a traits metafunction template pointee<T> that can be used to extract the "pointed-to"
+ type from the type of a pointer or smart pointer.
+
+ pointee<T>
+ is used by the class_<...>
+ template to deduce the type being held when a pointer or smart pointer
+ type is used as its HeldType argument.
+
namespace boost { namespace python +{ + template <class T> struct pointee + { + typedef T::element_type type; + }; + + // specialization for pointers + template <T> struct pointee<T*> + { + typedef T type; + }; +} ++
+ Given a 3rd-party smart pointer type smart_pointer<T>, one might partially specialize pointee<smart_pointer<T> > so that it can be used as the HeldType
+ for a class wrapper:
+
#include <boost/python/pointee.hpp> +#include <boost/python/class.hpp> +#include <third_party_lib.hpp> + +namespace boost { namespace python +{ + template <class T> struct pointee<smart_pointer<T> > + { + typedef T type; + }; +}} + +BOOST_PYTHON_MODULE(pointee_demo) +{ + class_<third_party_class, smart_pointer<third_party_class> >("third_party_class") + .def(...) + ... + ; +} ++
| + | + |
![]() |
+ Python 2.5 introduces a new Py_ssize_t
+ typedef and two related macros (PEP
+ 353). The <boost/python/ssize_t.hpp> header imports these
+ definitions into the boost::python
+ namespace as ssize_t,
+ ssize_t_max, and ssize_t_min. Appropriate definitions
+ are provided for backward compatibility with previous Python versions.
+
+ Imports Py_ssize_t into
+ the boost::python namespace if available, or provides
+ an appropriate typedef for backward compatibility:
+
#if PY_VERSION_HEX >= 0x02050000 +typedef Py_ssize_t ssize_t; +#else +typedef int ssize_t; +#endif ++
+ Imports PY_SSIZE_T_MAX
+ and PY_SSIZE_T_MIN as constants
+ into the boost::python namespace if available, or provides
+ appropriate constants for backward compatibility:
+
#if PY_VERSION_HEX >= 0x02050000 +ssize_t const ssize_t_max = PY_SSIZE_T_MAX; +ssize_t const ssize_t_min = PY_SSIZE_T_MIN; +#else +ssize_t const ssize_t_max = INT_MAX; +ssize_t const ssize_t_min = INT_MIN; +#endif ++
| + | + |
![]() |
+ <boost/python/type_id.hpp> provides types and functions for runtime
+ type identification like those of of <typeinfo>.
+ It exists mostly to work around certain compiler bugs and platform-dependent
+ interactions with shared libraries.
+
+ type_info instances identify
+ a type. As std::type_info is specified to (but unlike
+ its implementation in some compilers), boost::python::type_info
+ never represents top-level references or cv-qualification (see section
+ 5.2.8 in the C++ standard). Unlike std::type_info,
+ boost::python::type_info instances are copyable, and
+ comparisons always work reliably across shared library boundaries.
+
namespace boost { namespace python +{ + class type_info : totally_ordered<type_info> + { + public: + // constructor + type_info(std::type_info const& = typeid(void)); + + // comparisons + bool operator<(type_info const& rhs) const; + bool operator==(type_info const& rhs) const; + + // observers + char const* name() const; + }; +}} ++
type_info(std::type_info const& = typeid(void));+
+
+ constructs a type_info
+ object which identifies the same type as its argument.
+
+ Since it is occasionally necessary to make an array of type_info objects a benign default
+ argument is supplied. Note: this constructor does not correct for
+ non-conformance of compiler typeid() implementations. See type_id, below.
+
bool operator<(type_info const &rhs) const;+
+
+ yields a total order over type_info
+ objects.
+
bool operator==(type_info const &rhs) const;+
+
+ true iff the two values
+ describe the same type.
+
+ The use of totally_ordered<type_info> as a private base class supplies
+ operators <=,
+ >=, >, and !=
+
char const* name() const; ++
+
+ The result of calling name() on the argument used to construct
+ the object.
+
std::ostream& operator<<(std::ostream&s, type_info const&x); ++
+
+ Writes a description of the type described by to x
+ into s.
+
+ Not every C++ implementation provides a truly human-readable type_info::name()
+ string, but for some we may be able to decode the string and produce
+ a reasonable representation.
+
+ On some non-conforming C++ implementations, the code is not actually + as simple as described above; the semantics are adjusted to work + as-if the C++ implementation were conforming. +
template <class T> type_info type_id() ++
+
+ type_info(typeid(T))
+
+ On some non-conforming C++ implementations, the code is not actually + as simple as described above; the semantics are adjusted to work + as-if the C++ implementation were conforming. +
+ The following example, though silly, illustrates how the type_id facility + might be used +
+#include <boost/python/type_id.hpp> + +// Returns true iff the user passes an int argument +template <class T> +bool is_int(T x) +{ + using boost::python::type_id; + return type_id<T>() == type_id<int>(); +} ++
| + | + |
![]() |
+ This is a list of available resources for support with Boost.Python problems + and feature requests. Please try to resist emailing the Boost.Python developers + directly for support. Use the following resources instead; the developers are + listening! +
++ The Boost.Python Issue + tracker +
+![]() |
+Note | +
|---|---|
+ In the past we used Trac, which still hosts a considerable number of + open + issues. We hope to be able to either close them or migrate + them to the new issue tracker. + |
| + | + |
![]() |
Copyright © 2002-2005 Joel + de Guzman, David Abrahams
+ Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt +
+Table of Contents
+ ++ The Boost Python Library is a framework for interfacing Python and C++. It + allows you to quickly and seamlessly expose C++ classes functions and objects + to Python, and vice-versa, using no special tools -- just your C++ compiler. + It is designed to wrap C++ interfaces non-intrusively, so that you should not + have to change the C++ code at all in order to wrap it, making Boost.Python + ideal for exposing 3rd-party libraries to Python. The library's use of advanced + metaprogramming techniques simplifies its syntax for users, so that wrapping + code takes on the look of a kind of declarative interface definition language + (IDL). +
++ Following C/C++ tradition, let's start with the "hello, world". A + C++ Function: +
+char const* greet() +{ + return "hello, world"; +} ++
+ can be exposed to Python by writing a Boost.Python wrapper: +
+#include <boost/python.hpp> + +BOOST_PYTHON_MODULE(hello_ext) +{ + using namespace boost::python; + def("greet", greet); +} ++
+ That's it. We're done. We can now build this as a shared library. The resulting + DLL is now visible to Python. Here's a sample Python session: +
+>>> import hello_ext +>>> print hello_ext.greet() +hello, world ++
+ Next stop... Building your Hello World module + from start to finish... +
Last revised: October 07, 2016 at 15:41:24 GMT |
++ |
![]() |
+ By now you should know how to use Boost.Python to call your C++ code from Python. + However, sometimes you may need to do the reverse: call Python code from the + C++-side. This requires you to embed the Python interpreter + into your C++ program. +
+
+ Currently, Boost.Python does not directly support everything you'll need when
+ embedding. Therefore you'll need to use the Python/C
+ API to fill in the gaps. However, Boost.Python already makes embedding
+ a lot easier and, in a future version, it may become unnecessary to touch the
+ Python/C API at all. So stay tuned...
+
+ To be able to embed python into your programs, you have to link to both Boost.Python's + as well as Python's own runtime library. +
+
+ Boost.Python's library comes in two variants. Both are located in Boost's
+ /libs/python/build/bin-stage subdirectory. On Windows, the
+ variants are called boost_python.lib (for release builds)
+ and boost_python_debug.lib (for debugging). If you can't
+ find the libraries, you probably haven't built Boost.Python yet. See Building and Testing on how to do this.
+
+ Python's library can be found in the /libs subdirectory
+ of your Python directory. On Windows it is called pythonXY.lib where X.Y is
+ your major Python version number.
+
+ Additionally, Python's /include subdirectory has to be added
+ to your include path.
+
+ In a Jamfile, all the above boils down to: +
+projectroot c:\projects\embedded_program ; # location of the program + +# bring in the rules for python +SEARCH on python.jam = $(BOOST_BUILD_PATH) ; +include python.jam ; + +exe embedded_program # name of the executable + : #sources + embedded_program.cpp + : # requirements + <find-library>boost_python <library-path>c:\boost\libs\python + $(PYTHON_PROPERTIES) + <library-path>$(PYTHON_LIB_PATH) + <find-library>$(PYTHON_EMBEDDED_LIBRARY) ; ++
+ Being able to build is nice, but there is nothing to build yet. Embedding the + Python interpreter into one of your C++ programs requires these 4 steps: +
+<boost/python.hpp>
+ __main__ module.
+ ![]() |
+Note | +
|---|---|
+ Note that at this time you must not call Py_Finalize() + to stop the interpreter. This may be fixed in a future version of boost.python. + |
+ (Of course, there can be other C++ code between all of these steps.) +
++ Now that we can embed the interpreter in + our programs, lets see how to put it to use... +
+ As you probably already know, objects in Python are reference-counted. Naturally,
+ the PyObjects of the Python C API are also reference-counted.
+ There is a difference however. While the reference-counting is fully automatic
+ in Python, the Python C API requires you to do it by
+ hand. This is messy and especially hard to get right in the presence
+ of C++ exceptions. Fortunately Boost.Python provides the handle
+ and object
+ class templates to automate the process.
+
+ Boost.python provides three related functions to run Python code from C++. +
+object eval(str expression, object globals = object(), object locals = object()) +object exec(str code, object globals = object(), object locals = object()) +object exec_file(str filename, object globals = object(), object locals = object()) ++
+ eval evaluates the given expression and returns the resulting value. exec + executes the given code (typically a set of statements) returning the result, + and exec_file executes the code contained in the given file. +
+
+ The globals and locals parameters are
+ Python dictionaries containing the globals and locals of the context in which
+ to run the code. For most intents and purposes you can use the namespace
+ dictionary of the __main__ module for both parameters.
+
+ Boost.python provides a function to import a module: +
+object import(str name) ++
+ import imports a python module (potentially loading it into the running process + first), and returns it. +
+
+ Let's import the __main__ module and run some Python code
+ in its namespace:
+
object main_module = import("__main__"); +object main_namespace = main_module.attr("__dict__"); + +object ignored = exec("hello = file('hello.txt', 'w')\n" + "hello.write('Hello world!')\n" + "hello.close()", + main_namespace); ++
+ This should create a file called 'hello.txt' in the current directory containing + a phrase that is well-known in programming circles. +
+
+ Often we'd like to have a class to manipulate Python objects. But we have
+ already seen such a class above, and in the previous
+ section: the aptly named object class and its derivatives.
+ We've already seen that they can be constructed from a handle.
+ The following examples should further illustrate this fact:
+
object main_module = import("__main__"); +object main_namespace = main_module.attr("__dict__"); +object ignored = exec("result = 5 ** 2", main_namespace); +int five_squared = extract<int>(main_namespace["result"]); ++
+ Here we create a dictionary object for the __main__ module's
+ namespace. Then we assign 5 squared to the result variable and read this
+ variable from the dictionary. Another way to achieve the same result is to
+ use eval instead, which returns the result directly:
+
object result = eval("5 ** 2"); +int five_squared = extract<int>(result); ++
+ If an exception occurs in the evaluation of the python expression, error_already_set + is thrown: +
+try +{ + object result = eval("5/0"); + // execution will never get here: + int five_divided_by_zero = extract<int>(result); +} +catch(error_already_set const &) +{ + // handle the exception in some way +} ++
+ The error_already_set exception class doesn't carry any
+ information in itself. To find out more about the Python exception that occurred,
+ you need to use the exception
+ handling functions of the Python C API in your catch-statement. This
+ can be as simple as calling PyErr_Print()
+ to print the exception's traceback to the console, or comparing the type
+ of the exception with those of the standard
+ exceptions:
+
catch(error_already_set const &) +{ + if (PyErr_ExceptionMatches(PyExc_ZeroDivisionError)) + { + // handle ZeroDivisionError specially + } + else + { + // print all other errors to stderr + PyErr_Print(); + } +} ++
+ (To retrieve even more information from the exception you can use some of + the other exception handling functions listed here.) +
+| + | + |
![]() |
+ All C++ exceptions must be caught at the boundary with Python code. This boundary + is the point where C++ meets Python. Boost.Python provides a default exception + handler that translates selected standard exceptions, then gives up: +
+raise RuntimeError, 'unidentifiable C++ Exception' ++
+ Users may provide custom translation. Here's an example: +
+struct PodBayDoorException; +void translator(PodBayDoorException const& x) { + PyErr_SetString(PyExc_UserWarning, "I'm sorry Dave..."); +} +BOOST_PYTHON_MODULE(kubrick) { + register_exception_translator< + PodBayDoorException>(translator); + ... ++
| + | + |
![]() |
+ Now let's expose a C++ class to Python. +
++ Consider a C++ class/struct that we want to expose to Python: +
+struct World +{ + void set(std::string msg) { this->msg = msg; } + std::string greet() { return msg; } + std::string msg; +}; ++
+ We can expose this to Python by writing a corresponding Boost.Python C++ Wrapper: +
+#include <boost/python.hpp> +using namespace boost::python; + +BOOST_PYTHON_MODULE(hello) +{ + class_<World>("World") + .def("greet", &World::greet) + .def("set", &World::set) + ; +} ++
+ Here, we wrote a C++ class wrapper that exposes the member functions greet
+ and set. Now, after building our module as a shared library,
+ we may use our class World in Python. Here's a sample Python
+ session:
+
>>> import hello +>>> planet = hello.World() +>>> planet.set('howdy') +>>> planet.greet() +'howdy' ++
+ Our previous example didn't have any explicit constructors. Since World
+ is declared as a plain struct, it has an implicit default constructor. Boost.Python
+ exposes the default constructor by default, which is why we were able to
+ write
+
>>> planet = hello.World() ++
+ We may wish to wrap a class with a non-default constructor. Let us build + on our previous example: +
+struct World +{ + World(std::string msg): msg(msg) {} // added constructor + void set(std::string msg) { this->msg = msg; } + std::string greet() { return msg; } + std::string msg; +}; ++
+ This time World has no default constructor; our previous
+ wrapping code would fail to compile when the library tried to expose it.
+ We have to tell class_<World> about the constructor
+ we want to expose instead.
+
#include <boost/python.hpp> +using namespace boost::python; + +BOOST_PYTHON_MODULE(hello) +{ + class_<World>("World", init<std::string>()) + .def("greet", &World::greet) + .def("set", &World::set) + ; +} ++
+ init<std::string>() exposes the constructor taking
+ in a std::string (in Python, constructors are spelled
+ ""__init__"").
+
+ We can expose additional constructors by passing more init<...>s
+ to the def() member function. Say for example we have
+ another World constructor taking in two doubles:
+
class_<World>("World", init<std::string>()) + .def(init<double, double>()) + .def("greet", &World::greet) + .def("set", &World::set) +; ++
+ On the other hand, if we do not wish to expose any constructors at all, we
+ may use no_init instead:
+
class_<Abstract>("Abstract", no_init) ++
+ This actually adds an __init__ method which always raises
+ a Python RuntimeError exception.
+
+ Data members may also be exposed to Python so that they can be accessed as
+ attributes of the corresponding Python class. Each data member that we wish
+ to be exposed may be regarded as read-only
+ or read-write. Consider this class Var:
+
struct Var +{ + Var(std::string name) : name(name), value() {} + std::string const name; + float value; +}; ++
+ Our C++ Var class and its data members can be exposed
+ to Python:
+
class_<Var>("Var", init<std::string>()) + .def_readonly("name", &Var::name) + .def_readwrite("value", &Var::value); ++
+ Then, in Python, assuming we have placed our Var class inside the namespace + hello as we did before: +
+>>> x = hello.Var('pi') +>>> x.value = 3.14 +>>> print x.name, 'is around', x.value +pi is around 3.14 ++
+ Note that name is exposed as read-only
+ while value is exposed as read-write.
+
>>> x.name = 'e' # can't change name +Traceback (most recent call last): + File "<stdin>", line 1, in ? +AttributeError: can't set attribute ++
+ In C++, classes with public data members are usually frowned upon. Well designed + classes that take advantage of encapsulation hide the class' data members. + The only way to access the class' data is through access (getter/setter) + functions. Access functions expose class properties. Here's an example: +
+struct Num +{ + Num(); + float get() const; + void set(float value); + ... +}; ++
+ However, in Python attribute access is fine; it doesn't neccessarily break
+ encapsulation to let users handle attributes directly, because the attributes
+ can just be a different syntax for a method call. Wrapping our Num
+ class using Boost.Python:
+
class_<Num>("Num") + .add_property("rovalue", &Num::get) + .add_property("value", &Num::get, &Num::set); ++
+ And at last, in Python: +
+>>> x = Num() +>>> x.value = 3.14 +>>> x.value, x.rovalue +(3.14, 3.14) +>>> x.rovalue = 2.17 # error! ++
+ Take note that the class property rovalue is exposed as
+ read-only since the rovalue
+ setter member function is not passed in:
+
.add_property("rovalue", &Num::get) ++
+ In the previous examples, we dealt with classes that are not polymorphic. + This is not often the case. Much of the time, we will be wrapping polymorphic + classes and class hierarchies related by inheritance. We will often have + to write Boost.Python wrappers for classes that are derived from abstract + base classes. +
++ Consider this trivial inheritance structure: +
+struct Base { virtual ~Base(); }; +struct Derived : Base {}; ++
+ And a set of C++ functions operating on Base and Derived
+ object instances:
+
void b(Base*); +void d(Derived*); +Base* factory() { return new Derived; } ++
+ We've seen how we can wrap the base class Base:
+
class_<Base>("Base") + /*...*/ + ; ++
+ Now we can inform Boost.Python of the inheritance relationship between Derived
+ and its base class Base. Thus:
+
class_<Derived, bases<Base> >("Derived") + /*...*/ + ; ++
+ Doing so, we get some things for free: +
+Derived
+ objects which have been passed to Python via a pointer or reference to
+ Base can be passed where a pointer or reference to
+ Derived is expected.
+
+ Now, we will expose the C++ free functions b and d
+ and factory:
+
def("b", b); +def("d", d); +def("factory", factory); ++
+ Note that free function factory is being used to generate
+ new instances of class Derived. In such cases, we use
+ return_value_policy<manage_new_object> to instruct
+ Python to adopt the pointer to Base and hold the instance
+ in a new Python Base object until the the Python object
+ is destroyed. We will see more of Boost.Python call
+ policies later.
+
// Tell Python to take ownership of factory's result +def("factory", factory, + return_value_policy<manage_new_object>()); ++
+ In this section, we will learn how to make functions behave polymorphically
+ through virtual functions. Continuing our example, let us add a virtual function
+ to our Base class:
+
struct Base +{ + virtual ~Base() {} + virtual int f() = 0; +}; ++
+ 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. It is not ideal to add anything
+ to our class Base. Yet, when
+ you have a virtual function that's going to be overridden in Python and called
+ polymorphically from C++, we'll need to
+ add some scaffoldings to make things work properly. What we'll do is write
+ a class wrapper that derives from Base
+ that will unintrusively hook into the virtual functions so that a Python
+ override may be called:
+
struct BaseWrap : Base, wrapper<Base> +{ + int f() + { + return this->get_override("f")(); + } +}; ++
+ Notice too that in addition to inheriting from Base,
+ we also multiply- inherited wrapper<Base> (See Wrapper).
+ The wrapper template makes
+ the job of wrapping classes that are meant to overridden in Python, easier.
+
+ BaseWrap's overridden virtual member function f
+ in effect calls the corresponding method of the Python object through get_override.
+
+ Finally, exposing Base:
+
class_<BaseWrap, boost::noncopyable>("Base") + .def("f", pure_virtual(&Base::f)) + ; ++
+ pure_virtual signals Boost.Python
+ that the function f is a
+ pure virtual function.
+
![]() |
+Note | +
|---|---|
|
+ + member function and methods + ++ Python, like many object oriented languages uses the term methods. + Methods correspond roughly to C++'s member functions + + |
+ We've seen in the previous section how classes with pure virtual functions + are wrapped using Boost.Python's class + wrapper facilities. If we wish to wrap non-pure-virtual + functions instead, the mechanism is a bit different. +
++ Recall that in the previous + section, we wrapped a class with a pure virtual function that we then + implemented in C++, or Python classes derived from it. Our base class: +
+struct Base +{ + virtual int f() = 0; +}; ++
+ had a pure virtual function f. If, however, its member
+ function f was not declared as pure virtual:
+
struct Base +{ + virtual ~Base() {} + virtual int f() { return 0; } +}; ++
+ We wrap it this way: +
+struct BaseWrap : Base, wrapper<Base> +{ + int f() + { + if (override f = this->get_override("f")) + return f(); // *note* + return Base::f(); + } + + int default_f() { return this->Base::f(); } +}; ++
+ Notice how we implemented BaseWrap::f. Now,
+ we have to check if there is an override for f.
+ If none, then we call Base::f().
+
+ Finally, exposing: +
+class_<BaseWrap, boost::noncopyable>("Base") + .def("f", &Base::f, &BaseWrap::default_f) + ; ++
+ Take note that we expose both &Base::f and &BaseWrap::default_f. Boost.Python needs to keep track
+ of 1) the dispatch function f and 2) the forwarding function
+ to its default implementation default_f. There's a special
+ def function for this purpose.
+
+ In Python, the results would be as expected: +
+>>> base = Base() +>>> class Derived(Base): +... def f(self): +... return 42 +... +>>> derived = Derived() ++
+ Calling base.f():
+
>>> base.f() +0 ++
+ Calling derived.f():
+
>>> derived.f() +42 ++
+ C is well known for the abundance of operators. C++ extends this to the extremes + by allowing operator overloading. Boost.Python takes advantage of this and + makes it easy to wrap C++ operator-powered classes. +
+
+ Consider a file position class FilePos and a set of operators
+ that take on FilePos instances:
+
class FilePos { /*...*/ }; + +FilePos operator+(FilePos, int); +FilePos operator+(int, FilePos); +int operator-(FilePos, FilePos); +FilePos operator-(FilePos, int); +FilePos& operator+=(FilePos&, int); +FilePos& operator-=(FilePos&, int); +bool operator<(FilePos, FilePos); ++
+ The class and the various operators can be mapped to Python rather easily + and intuitively: +
+class_<FilePos>("FilePos") + .def(self + int()) // __add__ + .def(int() + self) // __radd__ + .def(self - self) // __sub__ + .def(self - int()) // __sub__ + .def(self += int()) // __iadd__ + .def(self -= other<int>()) + .def(self < self); // __lt__ ++
+ The code snippet above is very clear and needs almost no explanation at all.
+ It is virtually the same as the operators' signatures. Just take note that
+ self refers to FilePos object. Also, not every class
+ T that you might need to interact with in an operator
+ expression is (cheaply) default-constructible. You can use other<T>()
+ in place of an actual T instance when writing "self
+ expressions".
+
+ Python has a few more Special Methods. Boost.Python + supports all of the standard special method names supported by real Python + class instances. A similar set of intuitive interfaces can also be used to + wrap C++ functions that correspond to these Python special functions. + Example: +
+class Rational +{ public: operator double() const; }; + +Rational pow(Rational, Rational); +Rational abs(Rational); +ostream& operator<<(ostream&,Rational); + +class_<Rational>("Rational") + .def(float_(self)) // __float__ + .def(pow(self, other<Rational>)) // __pow__ + .def(abs(self)) // __abs__ + .def(str(self)) // __str__ + ; ++
+ Need we say more? +
+![]() |
+Note | +
|---|---|
+ What is the business of |
| + | + |
![]() |
+ In this chapter, we'll look at Boost.Python powered functions in closer detail. + We will see some facilities to make exposing C++ functions to Python safe from + potential pifalls such as dangling pointers and references. We will also see + facilities that will make it even easier for us to expose C++ functions that + take advantage of C++ features such as overloading and default arguments. +
++ Read on... +
+ But before you do, you might want to fire up Python 2.2 or later and type
+ >>> import this.
+
>>> import this
+The Zen of Python, by Tim Peters
+Beautiful is better than ugly.
+Explicit is better than implicit.
+Simple is better than complex.
+Complex is better than complicated.
+Flat is better than nested.
+Sparse is better than dense.
+Readability counts.
+Special cases aren't special enough to break the rules.
+Although practicality beats purity.
+Errors should never pass silently.
+Unless explicitly silenced.
+In the face of ambiguity, refuse the temptation to guess.
+There should be one-- and preferably only one --obvious way to do it
+Although that way may not be obvious at first unless you're Dutch.
+Now is better than never.
+Although never is often better than right now.
+If the implementation is hard to explain, it's a bad idea.
+If the implementation is easy to explain, it may be a good idea.
+Namespaces are one honking great idea -- let's do more of those!
+
++ In C++, we often deal with arguments and return types such as pointers and + references. Such primitive types are rather, ummmm, low level and they really + don't tell us much. At the very least, we don't know the owner of the pointer + or the referenced object. No wonder languages such as Java and Python never + deal with such low level entities. In C++, it's usually considered a good + practice to use smart pointers which exactly describe ownership semantics. + Still, even good C++ interfaces use raw references and pointers sometimes, + so Boost.Python must deal with them. To do this, it may need your help. Consider + the following C++ function: +
+X& f(Y& y, Z* z); ++
+ How should the library wrap this function? A naive approach builds a Python + X object around result reference. This strategy might or might not work out. + Here's an example where it didn't +
+>>> x = f(y, z) # x refers to some C++ X +>>> del y +>>> x.some_method() # CRASH! ++
+ What's the problem? +
++ Well, what if f() was implemented as shown below: +
+X& f(Y& y, Z* z) +{ + y.z = z; + return y.x; +} ++
+ The problem is that the lifetime of result X& is tied to the lifetime + of y, because the f() returns a reference to a member of the y object. This + idiom is is not uncommon and perfectly acceptable in the context of C++. + However, Python users should not be able to crash the system just by using + our C++ interface. In this case deleting y will invalidate the reference + to X. We have a dangling reference. +
++ Here's what's happening: +
+f is called passing in a reference to y
+ and a pointer to z
+ y.x is returned
+ y is deleted. x is a dangling reference
+ x.some_method() is called
+ + We could copy result into a new object: +
+>>> f(y, z).set(42) # Result disappears +>>> y.x.get() # No crash, but still bad +3.14 ++
+ This is not really our intent of our C++ interface. We've broken our promise + that the Python interface should reflect the C++ interface as closely as + possible. +
++ Our problems do not end there. Suppose Y is implemented as follows: +
+struct Y +{ + X x; Z* z; + int z_value() { return z->value(); } +}; ++
+ Notice that the data member z is held by class Y using
+ a raw pointer. Now we have a potential dangling pointer problem inside Y:
+
>>> x = f(y, z) # y refers to z +>>> del z # Kill the z object +>>> y.z_value() # CRASH! ++
+ For reference, here's the implementation of f again:
+
X& f(Y& y, Z* z) +{ + y.z = z; + return y.x; +} ++
+ Here's what's happening: +
+f is called passing in a reference to y
+ and a pointer to z
+ z is held by y
+ y.x is returned
+ z is deleted. y.z is a dangling
+ pointer
+ y.z_value() is called
+ z->value() is called
+
+ Call Policies may be used in situations such as the example detailed above.
+ In our example, return_internal_reference and with_custodian_and_ward
+ are our friends:
+
def("f", f, + return_internal_reference<1, + with_custodian_and_ward<1, 2> >()); ++
+ What are the 1 and 2 parameters, you
+ ask?
+
return_internal_reference<1 ++
+ Informs Boost.Python that the first argument, in our case Y&
+ y, is the owner of the returned reference: X&.
+ The "1" simply specifies the first argument.
+ In short: "return an internal reference X& owned
+ by the 1st argument Y& y".
+
with_custodian_and_ward<1, 2> ++
+ Informs Boost.Python that the lifetime of the argument indicated by ward
+ (i.e. the 2nd argument: Z* z) is dependent on the lifetime
+ of the argument indicated by custodian (i.e. the 1st argument: Y&
+ y).
+
+ It is also important to note that we have defined two policies above. Two + or more policies can be composed by chaining. Here's the general syntax: +
+policy1<args..., + policy2<args..., + policy3<args...> > > ++
+ Here is the list of predefined call policies. A complete reference detailing + these can be found here. +
++ The following illustrates a scheme for manually wrapping an overloaded member + functions. Of course, the same technique can be applied to wrapping overloaded + non-member functions. +
++ We have here our C++ class: +
+struct X +{ + bool f(int a) + { + return true; + } + + bool f(int a, double b) + { + return true; + } + + bool f(int a, double b, char c) + { + return true; + } + + int f(int a, int b, int c) + { + return a + b + c; + }; +}; ++
+ Class X has 4 overloaded functions. We will start by introducing some member + function pointer variables: +
+bool (X::*fx1)(int) = &X::f; +bool (X::*fx2)(int, double) = &X::f; +bool (X::*fx3)(int, double, char)= &X::f; +int (X::*fx4)(int, int, int) = &X::f; ++
+ With these in hand, we can proceed to define and wrap this for Python: +
+.def("f", fx1) +.def("f", fx2) +.def("f", fx3) +.def("f", fx4) ++
+ Boost.Python wraps (member) function pointers. Unfortunately, C++ function
+ pointers carry no default argument info. Take a function f
+ with default arguments:
+
int f(int, double = 3.14, char const* = "hello"); ++
+ But the type of a pointer to the function f has no information
+ about its default arguments:
+
int(*g)(int,double,char const*) = f; // defaults lost! ++
+ When we pass this function pointer to the def function,
+ there is no way to retrieve the default arguments:
+
def("f", f); // defaults lost! ++
+ Because of this, when wrapping C++ code, we had to resort to manual wrapping + as outlined in the previous + section, or writing thin wrappers: +
+// write "thin wrappers" +int f1(int x) { return f(x); } +int f2(int x, double y) { return f(x,y); } + +/*...*/ + + // in module init + def("f", f); // all arguments + def("f", f2); // two arguments + def("f", f1); // one argument ++
+ When you want to wrap functions (or member functions) that either: +
++ Boost.Python now has a way to make it easier. For instance, given a function: +
+int foo(int a, char b = 1, unsigned c = 2, double d = 3) +{ + /*...*/ +} ++
+ The macro invocation: +
+BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads, foo, 1, 4) ++
+ will automatically create the thin wrappers for us. This macro will create
+ a class foo_overloads that can be passed on to def(...).
+ The third and fourth macro argument are the minimum arguments and maximum
+ arguments, respectively. In our foo function the minimum
+ number of arguments is 1 and the maximum number of arguments is 4. The def(...)
+ function will automatically add all the foo variants for us:
+
def("foo", foo, foo_overloads()); ++
+ Objects here, objects there, objects here there everywhere. More frequently + than anything else, we need to expose member functions of our classes to + Python. Then again, we have the same inconveniences as before when default + arguments or overloads with a common sequence of initial arguments come into + play. Another macro is provided to make this a breeze. +
+
+ Like BOOST_PYTHON_FUNCTION_OVERLOADS, BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS
+ may be used to automatically create the thin wrappers for wrapping member
+ functions. Let's have an example:
+
struct george +{ + void + wack_em(int a, int b = 0, char c = 'x') + { + /*...*/ + } +}; ++
+ The macro invocation: +
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(george_overloads, wack_em, 1, 3) ++
+ will generate a set of thin wrappers for george's wack_em
+ member function accepting a minimum of 1 and a maximum of 3 arguments (i.e.
+ the third and fourth macro argument). The thin wrappers are all enclosed
+ in a class named george_overloads that can then be used
+ as an argument to def(...):
+
.def("wack_em", &george::wack_em, george_overloads()); ++
+ See the overloads + reference for details. +
+
+ A similar facility is provided for class constructors, again, with default
+ arguments or a sequence of overloads. Remember init<...>?
+ For example, given a class X with a constructor:
+
struct X +{ + X(int a, char b = 'D', std::string c = "constructor", double d = 0.0); + /*...*/ +} ++
+ You can easily add this constructor to Boost.Python in one shot: +
+.def(init<int, optional<char, std::string, double> >()) ++
+ Notice the use of init<...> and optional<...>
+ to signify the default (optional arguments).
+
+ It was mentioned in passing in the previous section that BOOST_PYTHON_FUNCTION_OVERLOADS
+ and BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS can also be
+ used for overloaded functions and member functions with a common sequence
+ of initial arguments. Here is an example:
+
void foo() +{ + /*...*/ +} + +void foo(bool a) +{ + /*...*/ +} + +void foo(bool a, int b) +{ + /*...*/ +} + +void foo(bool a, int b, char c) +{ + /*...*/ +} ++
+ Like in the previous section, we can generate thin wrappers for these overloaded + functions in one-shot: +
+BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads, foo, 0, 3) ++
+ Then... +
+.def("foo", (void(*)(bool, int, char))0, foo_overloads()); ++
+ Notice though that we have a situation now where we have a minimum of zero + (0) arguments and a maximum of 3 arguments. +
++ It is important to emphasize however that the overloaded + functions must have a common sequence of initial arguments. Otherwise, + our scheme above will not work. If this is not the case, we have to wrap + our functions manually. +
+
+ Actually, we can mix and match manual wrapping of overloaded functions and
+ automatic wrapping through BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS
+ and its sister, BOOST_PYTHON_FUNCTION_OVERLOADS. Following
+ up on our example presented in the section on
+ overloading, since the first 4 overload functins have a common sequence
+ of initial arguments, we can use BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS
+ to automatically wrap the first three of the defs and
+ manually wrap just the last. Here's how we'll do this:
+
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(xf_overloads, f, 1, 4) ++
+ Create a member function pointers as above for both X::f overloads: +
+bool (X::*fx1)(int, double, char) = &X::f; +int (X::*fx2)(int, int, int) = &X::f; ++
+ Then... +
+.def("f", fx1, xf_overloads()); +.def("f", fx2) ++
| + | + |
![]() |
+ Now the first thing you'd want to do is to build the Hello World module and + try it for yourself in Python. In this section, we will outline the steps necessary + to achieve that. We will use the build tool that comes bundled with every boost + distribution: bjam. +
+![]() |
+Note | +
|---|---|
|
+ + Building without bjam + +
+ Besides bjam, there are of course other ways to get your module built. What's
+ written here should not be taken as "the one and only way". There
+ are of course other build tools apart from + Take note however that the preferred build tool for Boost.Python is bjam. + There are so many ways to set up the build incorrectly. Experience shows + that 90% of the "I can't build Boost.Python" problems come from + people who had to use a different tool. + + |
+ We will skip over the details. Our objective will be to simply create the hello + world module and run it in Python. For a complete reference to building Boost.Python, + check out: building.html. After + this brief bjam tutorial, we should have built the DLLs + and run a python program using the extension. +
+
+ The tutorial example can be found in the directory: libs/python/example/tutorial.
+ There, you can find:
+
+ The hello.cpp file is our C++ hello world example. The
+ Jamroot is a minimalist bjam script
+ that builds the DLLs for us. Finally, hello.py is our Python
+ program that uses the extension in hello.cpp.
+
+ Before anything else, you should have the bjam executable in your boost directory
+ or somewhere in your path such that bjam can be executed
+ in the command line. Pre-built Boost.Jam executables are available for most
+ platforms. The complete list of Bjam executables can be found here.
+
+
+
+ Here is our minimalist
+ Jamroot file. Simply copy the file and tweak use-project boost
+ to where your boost root directory is and you're OK.
+
+ The comments contained in the Jamrules file above should be sufficient to get + you going. +
++ bjam is run using your operating system's command line + interpreter. +
++ Start it up. +
+ A file called user-config.jam in your home directory is used to configure your + tools. In Windows, your home directory can be found by typing: +
+ECHO %HOMEDRIVE%%HOMEPATH% ++
+ into a command prompt window. Your file should at least have the rules for + your compiler and your python installation. A specific example of this on Windows + would be: +
+# MSVC configuration
+using msvc : 8.0 ;
+
+# Python configuration
+using python : 2.4 : C:dev/tools/Python ;
+
+
+ The first rule tells Bjam to use the MSVC 8.0 compiler and associated tools.
+ The second rule provides information on Python, its version and where it is
+ located. The above assumes that the Python installation is in C:dev/tools\/Python.
+ If you have one fairly "standard" python installation for your platform,
+ you might not need to do this.
+
+ Now we are ready... Be sure to cd to libs/python/example/tutorial
+ where the tutorial "hello.cpp" and the "Jamroot"
+ is situated.
+
+ Finally: +
+bjam
+
++ It should be building now: +
+cd C:\dev\boost\libs\python\example\tutorial +bjam +...patience... +...found 1101 targets... +...updating 35 targets... ++
+ And so on... Finally: +
+ Creating library path-to-boost_python.dll
+ Creating library /path-to-hello_ext.exp/
+**passed** ... hello.test
+...updated 35 targets...
+
++ Or something similar. If all is well, you should now have built the DLLs and + run the Python program. +
++ There you go... Have fun! +
| + | + |
![]() |
+ In C++, and STL in particular, we see iterators everywhere. Python also has + iterators, but these are two very different beasts. +
++ C++ iterators: +
++ Python Iterators: +
+
+ The typical Python iteration protocol: for y
+ in x... is as follows:
+
iter = x.__iter__() # get iterator +try: + while 1: + y = iter.next() # get each item + ... # process y +except StopIteration: pass # iterator exhausted ++
+ Boost.Python provides some mechanisms to make C++ iterators play along nicely
+ as Python iterators. What we need to do is to produce appropriate __iter__ function from C++ iterators that
+ is compatible with the Python iteration protocol. For example:
+
object get_iterator = iterator<vector<int> >(); +object iter = get_iterator(v); +object first = iter.next(); ++
+ Or for use in class_<>: +
+.def("__iter__", iterator<vector<int> >()) ++
+ range +
++ We can create a Python savvy iterator using the range function: +
++ Here, start/finish may be one of: +
++ iterator +
+
+ Given a container T, iterator is a shortcut that simply
+ calls range with &T::begin, &T::end.
+
+ Let's put this into action... Here's an example from some hypothetical bogon + Particle accelerator code: +
+f = Field() +for x in f.pions: + smash(x) +for y in f.bogons: + count(y) ++
+ Now, our C++ Wrapper: +
+class_<F>("Field") + .property("pions", range(&F::p_begin, &F::p_end)) + .property("bogons", range(&F::b_begin, &F::b_end)); ++
+ stl_input_iterator +
+
+ So far, we have seen how to expose C++ iterators and ranges to Python. Sometimes
+ we wish to go the other way, though: we'd like to pass a Python sequence to
+ an STL algorithm or use it to initialize an STL container. We need to make
+ a Python iterator look like an STL iterator. For that, we use stl_input_iterator<>.
+ Consider how we might implement a function that exposes std::list<int>::assign() to Python:
+
template<typename T> +void list_assign(std::list<T>& l, object o) { + // Turn a Python sequence into an STL input range + stl_input_iterator<T> begin(o), end; + l.assign(begin, end); +} + +// Part of the wrapper for list<int> +class_<std::list<int> >("list_int") + .def("assign", &list_assign<int>) + // ... + ; ++
+ Now in Python, we can assign any integer sequence to list_int
+ objects:
+
x = list_int(); +x.assign([1,2,3,4,5]) ++
| + | + |
![]() |
+ Python is dynamically typed, unlike C++ which is statically typed. Python variables
+ may hold an integer, a float, list, dict, tuple, str, long etc., among other
+ things. In the viewpoint of Boost.Python and C++, these Pythonic variables
+ are just instances of class object. We will see in this
+ chapter how to deal with Python objects.
+
+ As mentioned, one of the goals of Boost.Python is to provide a bidirectional
+ mapping between C++ and Python while maintaining the Python feel. Boost.Python
+ C++ objects are as close as possible to Python. This should
+ minimize the learning curve significantly.
+
+
+
+ Class object wraps PyObject*. All the
+ intricacies of dealing with PyObjects such as managing
+ reference counting are handled by the object class. C++
+ object interoperability is seamless. Boost.Python C++ objects
+ can in fact be explicitly constructed from any C++ object.
+
+ To illustrate, this Python code snippet: +
+def f(x, y): + if (y == 'foo'): + x[3:7] = 'bar' + else: + x.items += y(3, x) + return x + +def getfunc(): + return f; ++
+ Can be rewritten in C++ using Boost.Python facilities this way: +
+object f(object x, object y) { + if (y == "foo") + x.slice(3,7) = "bar"; + else + x.attr("items") += y(3, x); + return x; +} +object getfunc() { + return object(f); +} ++
+ Apart from cosmetic differences due to the fact that we are writing the code + in C++, the look and feel should be immediately apparent to the Python coder. +
+
+ Boost.Python comes with a set of derived object types
+ corresponding to that of Python's:
+
+ These derived object types act like real Python types.
+ For instance:
+
str(1) ==> "1" ++
+ Wherever appropriate, a particular derived object has
+ corresponding Python type's methods. For instance, dict
+ has a keys() method:
+
d.keys() ++
+ make_tuple is provided for declaring tuple literals.
+ Example:
+
make_tuple(123, 'D', "Hello, World", 0.0); ++
+ In C++, when Boost.Python objects are used as arguments
+ to functions, subtype matching is required. For example, when a function
+ f, as declared below, is wrapped, it will only accept
+ instances of Python's str type and subtypes.
+
void f(str name) +{ + object n2 = name.attr("upper")(); // NAME = name.upper() + str NAME = name.upper(); // better + object msg = "%s is bigger than %s" % make_tuple(NAME,name); +} ++
+ In finer detail: +
+str NAME = name.upper(); ++
+ Illustrates that we provide versions of the str type's methods as C++ member + functions. +
+object msg = "%s is bigger than %s" % make_tuple(NAME,name); ++
+ Demonstrates that you can write the C++ equivalent of "format"
+ % x,y,z in Python, which is useful since there's no easy way to
+ do that in std C++.
+
+ Python: +
+>>> d = dict(x.__dict__) # copies x.__dict__ +>>> d['whatever'] = 3 # modifies the copy ++
+ C++: +
+dict d(x.attr("__dict__")); // copies x.__dict__ +d['whatever'] = 3; // modifies the copy ++
+ Due to the dynamic nature of Boost.Python objects, any class_<T>
+ may also be one of these types! The following code snippet wraps the class
+ (type) object.
+
+ We can use this to create wrapped instances. Example: +
+object vec345 = ( + class_<Vec2>("Vec2", init<double, double>()) + .def_readonly("length", &Point::length) + .def_readonly("angle", &Point::angle) + )(3.0, 4.0); + +assert(vec345.attr("length") == 5.0); ++
+ At some point, we will need to get C++ values out of object instances. This
+ can be achieved with the extract<T> function. Consider
+ the following:
+
double x = o.attr("length"); // compile error ++
+ In the code above, we got a compiler error because Boost.Python object
+ can't be implicitly converted to doubles. Instead, what
+ we wanted to do above can be achieved by writing:
+
double l = extract<double>(o.attr("length")); +Vec2& v = extract<Vec2&>(o); +assert(l == v.length()); ++
+ The first line attempts to extract the "length" attribute of the
+ Boost.Python object. The second line attempts to extract
+ the Vec2 object from held by the Boost.Python object.
+
+ Take note that we said "attempt to" above. What if the Boost.Python
+ object does not really hold a Vec2
+ type? This is certainly a possibility considering the dynamic nature of Python
+ objects. To be on the safe side, if the C++ type can't
+ be extracted, an appropriate exception is thrown. To avoid an exception,
+ we need to test for extractibility:
+
extract<Vec2&> x(o); +if (x.check()) { + Vec2& v = x(); ... ++
+
+ The astute reader might have noticed that the extract<T>
+ facility in fact solves the mutable copying problem:
+
dict d = extract<dict>(x.attr("__dict__")); +d["whatever"] = 3; // modifies x.__dict__ ! ++
+ Boost.Python has a nifty facility to capture and wrap C++ enums. While Python
+ has no enum type, we'll often want to expose our C++ enums
+ to Python as an int. Boost.Python's enum facility makes
+ this easy while taking care of the proper conversions from Python's dynamic
+ typing to C++'s strong static typing (in C++, ints cannot be implicitly converted
+ to enums). To illustrate, given a C++ enum:
+
enum choice { red, blue }; ++
+ the construct: +
+enum_<choice>("choice") + .value("red", red) + .value("blue", blue) + ; ++
+ can be used to expose to Python. The new enum type is created in the current
+ scope(), which is usually the current module. The snippet
+ above creates a Python class derived from Python's int
+ type which is associated with the C++ type passed as its first parameter.
+
![]() |
+Note | +
|---|---|
|
+ + what is a scope? + ++ The scope is a class that has an associated global Python object which + controls the Python namespace in which new extension classes and wrapped + functions will be defined as attributes. Details can be found here. + + |
+ You can access those values in Python as +
+>>> my_module.choice.red +my_module.choice.red ++
+ where my_module is the module where the enum is declared. You can also create + a new scope around a class: +
+scope in_X = class_<X>("X") + .def( ... ) + .def( ... ) + ; + +// Expose X::nested as X.nested +enum_<X::nested>("nested") + .value("red", red) + .value("blue", blue) + ; ++
+ When you want a boost::python::object to manage a pointer to PyObject*
+ pyobj one does:
+
boost::python::object o(boost::python::handle<>(pyobj)); ++
+ In this case, the o object,
+ manages the pyobj, it won’t
+ increase the reference count on construction.
+
+ Otherwise, to use a borrowed reference: +
+boost::python::object o(boost::python::handle<>(boost::python::borrowed(pyobj))); ++
+ In this case, Py_INCREF is
+ called, so pyobj is not destructed
+ when object o goes out of scope.
+
| + | + |
![]() |
+ Here are presented some useful techniques that you can use while wrapping code + with Boost.Python. +
++ A Python package is a collection of modules that provide to the user a certain + functionality. If you're not familiar on how to create packages, a good introduction + to them is provided in the Python + Tutorial. +
++ But we are wrapping C++ code, using Boost.Python. How can we provide a nice + package interface to our users? To better explain some concepts, let's work + with an example. +
+
+ We have a C++ library that works with sounds: reading and writing various
+ formats, applying filters to the sound data, etc. It is named (conveniently)
+ sounds. Our library already has a neat C++ namespace hierarchy,
+ like so:
+
sounds::core +sounds::io +sounds::filters ++
+ We would like to present this same hierarchy to the Python user, allowing + him to write code like this: +
+import sounds.filters +sounds.filters.echo(...) # echo is a C++ function ++
+ The first step is to write the wrapping code. We have to export each module + separately with Boost.Python, like this: +
+/* file core.cpp */ +BOOST_PYTHON_MODULE(core) +{ + /* export everything in the sounds::core namespace */ + ... +} + +/* file io.cpp */ +BOOST_PYTHON_MODULE(io) +{ + /* export everything in the sounds::io namespace */ + ... +} + +/* file filters.cpp */ +BOOST_PYTHON_MODULE(filters) +{ + /* export everything in the sounds::filters namespace */ + ... +} ++
+ Compiling these files will generate the following Python extensions: core.pyd,
+ io.pyd and filters.pyd.
+
![]() |
+Note | +
|---|---|
+ The extension |
+ Now, we create this directory structure for our Python package: +
+sounds/ + __init__.py + core.pyd + filters.pyd + io.pyd ++
+ The file __init__.py is what tells Python that the directory
+ sounds/ is actually a Python package. It can be a empty
+ file, but can also perform some magic, that will be shown later.
+
+ Now our package is ready. All the user has to do is put sounds
+ into his PYTHONPATH
+ and fire up the interpreter:
+
>>> import sounds.io +>>> import sounds.filters +>>> sound = sounds.io.open('file.mp3') +>>> new_sound = sounds.filters.echo(sound, 1.0) ++
+ Nice heh? +
++ This is the simplest way to create hierarchies of packages, but it is not + very flexible. What if we want to add a pure Python + function to the filters package, for instance, one that applies 3 filters + in a sound object at once? Sure, you can do this in C++ and export it, but + why not do so in Python? You don't have to recompile the extension modules, + plus it will be easier to write it. +
++ If we want this flexibility, we will have to complicate our package hierarchy + a little. First, we will have to change the name of the extension modules: +
+/* file core.cpp */ +BOOST_PYTHON_MODULE(_core) +{ + ... + /* export everything in the sounds::core namespace */ +} ++
+ Note that we added an underscore to the module name. The filename will have
+ to be changed to _core.pyd as well, and we do the same
+ to the other extension modules. Now, we change our package hierarchy like
+ so:
+
sounds/ + __init__.py + core/ + __init__.py + _core.pyd + filters/ + __init__.py + _filters.pyd + io/ + __init__.py + _io.pyd ++
+ Note that we created a directory for each extension module, and added a __init__.py + to each one. But if we leave it that way, the user will have to access the + functions in the core module with this syntax: +
+>>> import sounds.core._core +>>> sounds.core._core.foo(...) ++
+ which is not what we want. But here enters the __init__.py
+ magic: everything that is brought to the __init__.py namespace
+ can be accessed directly by the user. So, all we have to do is bring the
+ entire namespace from _core.pyd to core/__init__.py.
+ So add this line of code to sounds/core/__init__.py:
+
from _core import * ++
+ We do the same for the other packages. Now the user accesses the functions + and classes in the extension modules like before: +
+>>> import sounds.filters +>>> sounds.filters.echo(...) ++
+ with the additional benefit that we can easily add pure Python functions
+ to any module, in a way that the user can't tell the difference between a
+ C++ function and a Python function. Let's add a pure
+ Python function, echo_noise, to the filters
+ package. This function applies both the echo and noise
+ filters in sequence in the given sound object. We create
+ a file named sounds/filters/echo_noise.py and code our
+ function:
+
import _filters +def echo_noise(sound): + s = _filters.echo(sound) + s = _filters.noise(sound) + return s ++
+ Next, we add this line to sounds/filters/__init__.py:
+
from echo_noise import echo_noise ++
+ And that's it. The user now accesses this function like any other function
+ from the filters package:
+
>>> import sounds.filters +>>> sounds.filters.echo_noise(...) ++
+ Thanks to Python's flexibility, you can easily add new methods to a class, + even after it was already created: +
+>>> class C(object): pass +>>> +>>> # a regular function +>>> def C_str(self): return 'A C instance!' +>>> +>>> # now we turn it in a member function +>>> C.__str__ = C_str +>>> +>>> c = C() +>>> print c +A C instance! +>>> C_str(c) +A C instance! ++
+ Yes, Python rox.
+
+ We can do the same with classes that were wrapped with Boost.Python. Suppose
+ we have a class point in C++:
+
class point {...}; + +BOOST_PYTHON_MODULE(_geom) +{ + class_<point>("point")...; +} ++
+ If we are using the technique from the previous session, Creating
+ Packages, we can code directly into geom/__init__.py:
+
from _geom import * + +# a regular function +def point_str(self): + return str((self.x, self.y)) + +# now we turn it into a member function +point.__str__ = point_str ++
+ All point instances created from C++ will + also have this member function! This technique has several advantages: +
++ You can even add a little syntactic sugar with the use of metaclasses. Let's + create a special metaclass that "injects" methods in other classes. +
+# The one Boost.Python uses for all wrapped classes. +# You can use here any class exported by Boost instead of "point" +BoostPythonMetaclass = point.__class__ + +class injector(object): + class __metaclass__(BoostPythonMetaclass): + def __init__(self, name, bases, dict): + for b in bases: + if type(b) not in (self, type): + for k,v in dict.items(): + setattr(b,k,v) + return type.__init__(self, name, bases, dict) + +# inject some methods in the point foo +class more_point(injector, point): + def __repr__(self): + return 'Point(x=%s, y=%s)' % (self.x, self.y) + def foo(self): + print 'foo!' ++
+ Now let's see how it got: +
+>>> print point() +Point(x=10, y=10) +>>> point().foo() +foo! ++
+ Another useful idea is to replace constructors with factory functions: +
+_point = point + +def point(x=0, y=0): + return _point(x, y) ++
+ In this simple case there is not much gained, but for constructurs with many + overloads and/or arguments this is often a great simplification, again with + virtually zero memory footprint and zero compile-time overhead for the keyword + support. +
++ If you have ever exported a lot of classes, you know that it takes quite + a good time to compile the Boost.Python wrappers. Plus the memory consumption + can easily become too high. If this is causing you problems, you can split + the class_ definitions in multiple files: +
+/* file point.cpp */ +#include <point.h> +#include <boost/python.hpp> + +void export_point() +{ + class_<point>("point")...; +} + +/* file triangle.cpp */ +#include <triangle.h> +#include <boost/python.hpp> + +void export_triangle() +{ + class_<triangle>("triangle")...; +} ++
+ Now you create a file main.cpp, which contains the BOOST_PYTHON_MODULE
+ macro, and call the various export functions inside it.
+
void export_point(); +void export_triangle(); + +BOOST_PYTHON_MODULE(_geom) +{ + export_point(); + export_triangle(); +} ++
+ Compiling and linking together all this files produces the same result as + the usual approach: +
+#include <boost/python.hpp> +#include <point.h> +#include <triangle.h> + +BOOST_PYTHON_MODULE(_geom) +{ + class_<point>("point")...; + class_<triangle>("triangle")...; +} ++
+ but the memory is kept under control. +
++ This method is recommended too if you are developing the C++ library and + exporting it to Python at the same time: changes in a class will only demand + the compilation of a single cpp, instead of the entire wrapper code. +
+![]() |
+Note | +
|---|---|
+ This method is useful too if you are getting the error message "fatal + error C1204:Compiler limit:internal structure overflow" + when compiling a large source file, as explained in the FAQ. + |
| + | + |
© Copyright Stefan Seefeld, 2015
+Distributed under the Boost Software License, Version 1.0. (See accompanying + file http://www.boost.org/LICENSE_1_0.txt)
+ +