diff --git a/doc/PyConDC_2003/bpl.html b/doc/PyConDC_2003/bpl.html deleted file mode 100755 index a9ecebd5..00000000 --- a/doc/PyConDC_2003/bpl.html +++ /dev/null @@ -1,1127 +0,0 @@ - - - -
- - -| Author: | -David Abrahams |
|---|---|
| Contact: | -dave@boost-consulting.com |
| Organization: | -Boost Consulting |
| Date: | -2003-03-19 |
| Author: | -Ralf W. Grosse-Kunstleve |
| Copyright: | -Copyright David Abrahams and Ralf W. Grosse-Kunstleve 2003. All rights reserved |
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 footprint 1. The project also wanted to -test all of its C++ code using Python test scripts 2. 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-Kunstleve 3, 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 |
- def("foo", foo, foo_overloads());
+ .def("foo", foo, foo_overloads());
Objects here, objects there, objects here there everywhere. More frequently
diff --git a/doc/tutorial/doc/quickstart.txt b/doc/tutorial/doc/quickstart.txt
index 844ffa1b..7efc63bf 100644
--- a/doc/tutorial/doc/quickstart.txt
+++ b/doc/tutorial/doc/quickstart.txt
@@ -994,7 +994,7 @@ 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());
+ .def("foo", foo, foo_overloads());
[h2 BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS]
@@ -1330,258 +1330,6 @@ create a new scope around a class:
.value("blue", blue)
;
-[def Py_Initialize [@http://www.python.org/doc/current/api/initialization.html#l2h-652 Py_Initialize]]
-[def Py_Finalize [@http://www.python.org/doc/current/api/initialization.html#l2h-656 Py_Finalize]]
-[def PyRun_String [@http://www.python.org/doc/current/api/veryhigh.html#l2h-55 PyRun_String]]
-[def PyRun_File [@http://www.python.org/doc/current/api/veryhigh.html#l2h-56 PyRun_File]]
-[def Py_eval_input [@http://www.python.org/doc/current/api/veryhigh.html#l2h-58 Py_eval_input]]
-[def Py_file_input [@http://www.python.org/doc/current/api/veryhigh.html#l2h-59 Py_file_input]]
-[def Py_single_input [@http://www.python.org/doc/current/api/veryhigh.html#l2h-60 Py_single_input]]
-[def Py_XINCREF [@http://www.python.org/doc/current/api/countingRefs.html#l2h-65 Py_XINCREF]]
-[def Py_XDECREF [@http://www.python.org/doc/current/api/countingRefs.html#l2h-67 Py_XDECREF]]
-[def PyImport_AppendInittab [@http://www.python.org/doc/current/api/importing.html#l2h-137 PyImport_AppendInittab]]
-[def PyImport_AddModule [@http://www.python.org/doc/current/api/importing.html#l2h-125 PyImport_AddModule]]
-[def PyModule_New [@http://www.python.org/doc/current/api/moduleObjects.html#l2h-591 PyModule_New]]
-[def PyModule_GetDict [@http://www.python.org/doc/current/api/moduleObjects.html#l2h-594 PyModule_GetDict]]
-
-[page:0 Embedding]
-
-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
-[@http://www.python.org/doc/current/api/api.html 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... :-)
-
-[h2 Building embedded programs]
-
-To be able to use embedding in your programs, they have to be linked to
-both Boost.Python's and Python's static link library.
-
-Boost.Python's static link 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.html
-Building and Testing] on how to do this.
-
-Python's static link 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:
-
-[pre
- 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
-
-
+
handle<> main_module(borrowed( PyImport_AddModule("__main__") ));
- dict main_namespace(handle<>(borrowed( PyModule_GetDict(main_module.get()) )));
+ main_namespace dict(handle<>(borrowed( PyModule_GetDict(main_module.get()) )));
handle<>( PyRun_String("result = 5 ** 2", Py_file_input,
main_namespace.ptr(), main_namespace.ptr()) );
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 diff --git a/doc/v2/acknowledgments.html b/doc/v2/acknowledgments.html index 3cc24edb..82cea6a2 100644 --- a/doc/v2/acknowledgments.html +++ b/doc/v2/acknowledgments.html @@ -31,15 +31,6 @@
Dave Abrahams is the architect, designer, and implementor of Boost.Python.
-Brett Calcott - contributed and maintains the Visual Studio project files and - documentation.
- -Gottfried - Ganßauge supplied support for opaque pointer conversions, - complete with documentation and a regression test (and I didn't - even have to ask him for those)! -
Joel de Guzman implemented the default argument support and wrote the excellent tutorial documentation.
@@ -72,17 +63,6 @@ use the new preproccessor metaprogramming constructs and helping us to work around buggy and slow C++ preprocessors. -Bruno da Silva de - Oliveira contributed the ingenious Pyste ("Pie-Steh") - code generator. - -
Nikolay Mladenov contributed
- staticmethod support.
Martin Casado solved some sticky problems which allow us to build the - Boost.Python shared library for AIX's crazy dynamic linking model.
-Achim Domma contributed some of the Object Wrappers and HTML templates for this documentation. Dave Hawkes contributed @@ -91,6 +71,16 @@ definition syntax. Pearu Pearson wrote some of the test cases that are in the current test suite.
+Brett Calcott + contributed and maintains the Visual Studio project files and + documentation.
+ +Nikolay Mladenov contributed
+ staticmethod support.
Martin Casado solved some sticky problems which allow us to build the + Boost.Python shared library for AIX's crazy dynamic linking model.
+The development of this version of Boost.Python was funded in part by
the Lawrence Livermore National
Laboratories and by the Computational
diff --git a/doc/v2/faq.html b/doc/v2/faq.html
index 3c2b4c24..4a6448cb 100644
--- a/doc/v2/faq.html
+++ b/doc/v2/faq.html
@@ -29,10 +29,6 @@
If you have the luxury of changing the C++ code you're
- wrapping, pass it an For more perspective on the issue, see this
- posting.
-
- Revised
- 18 March, 2003
+ 23 January, 2003
-
-
- How can I wrap a function which takes a
- function pointer as an argument?
-
- 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.
-
- object instead and call that;
- the overloaded function call operator will invoke the Python
- function you pass it behind the object.
-
-
-
I'm getting the "attempt to return dangling
reference" error. What am I doing wrong?
That exception is protecting you from causing a nasty crash. It usually
@@ -547,7 +492,7 @@ void b_insert(B& b, std::auto_ptr<A> a)
|
- |
-
-
- Boost.Python- -Header - <boost/python/opaque_pointer_converter.hpp>- |
-
opaque_pointer_converter<P>opaque_pointer_converter synopsisBOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_IDopaque_pointer_converter<P>opaque_pointer_converter<> is derived from
-
- to_python_converter
- and registers itself as an
-
- lvalue_from_pytype converter from Python objects
- into pointers to undefined types.
- Thus it may be used as a converter from opaque pointers into
- Python objects and vice versa.
opaque_pointer_converter synopsis
-namespace boost { namespace python
-{
- template<class Pointer>
- struct opaque_pointer_converter
- : to_python_converter<
- Pointer, opaque_pointer_converter<Pointer> >
- {
- explicit opaque_pointer_converter(char const* name);
- };
-}}
-
-
- opaque_pointer_converter constructor-explicit opaque_pointer_converter(char const* name); -- -
Registers the instance as a
-
- lvalue_from_pytype converter from Python objects
- into opaque pointers.
The name is used for the type of the Python Objects created; - it should be printable but needn't be an - ntbs because the object type is - not supposed to be user constructible within python scripts.
-This macro must be used to define specializations of the - type_id function - which can't be instantiated for incomplete types.
-In order for this to work in a cross-module environment the macro must - be invoked in every translation unit which uses the - opaque_pointer_converter.
- -Revised - 10 March, 2003 -
- -© Copyright 2003 Haufe Mediengruppe. All Rights - Reserved.
- - - diff --git a/doc/v2/overloads.html b/doc/v2/overloads.html index edce8596..a0a37b34 100644 --- a/doc/v2/overloads.html +++ b/doc/v2/overloads.html @@ -194,10 +194,9 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(X_f_overloads, X::f, 1, 3) BOOST_PYTHON_MODULE(args_ext) { - def("f", f, - f_overloads( - args("x", "y", "z"), "This is f's docstring" - )); + def("f", f, args("x", "y", "z") + , "This is f's docstring" + ); class_<Y>("Y") @@ -205,17 +204,16 @@ BOOST_PYTHON_MODULE(args_ext) class_<X>("X", "This is X's docstring") .def("f1", &X::f, - X_f_overloads( - args("x", "y", "z"), "f's docstring" - )[return_internal_reference<>()] - ) + X_f_overloads(args("x", "y", "z"), + "f's docstring" + )[return_internal_reference<>()]) ; }Revised - 15 April, 2003 + 15 December, 2002
diff --git a/doc/v2/raw_function.html b/doc/v2/raw_function.html deleted file mode 100755 index ae1ad6c0..00000000 --- a/doc/v2/raw_function.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - -|
- |
-
-
- Boost.Python- -Header <boost/python/raw_function.hpp>- |
-
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.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.
-
-
-
-#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})
-
- - - 7 March, 2003 - -
- -© Copyright Dave Abrahams 2002. All Rights - Reserved.
- - - diff --git a/doc/v2/reference.html b/doc/v2/reference.html index 22245385..ae2ec560 100644 --- a/doc/v2/reference.html +++ b/doc/v2/reference.html @@ -749,96 +749,6 @@ - -|
- |
-
-
- Boost.Python- -Header - <boost/python/return_opaque_pointer.hpp>- |
-
return_opaque_pointerreturn_opaque_pointer synopsisreturn_opaque_pointer metafunctionsreturn_opaque_pointerreturn_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.
return_opaque_pointer synopsis
-namespace boost { namespace python
-{
- struct return_opaque_pointer
- {
- template <class R> struct apply;
- };
-}}
-
-
- return_opaque_pointer metafunctions-template <class R> struct apply -- - - -
-# 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);
-}
-
-
-
-"""
->>> 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])
-
-
- - - opaque_pointer_converter -
- -Revised - 28 January, 2003 -
- -© Copyright 2003 Haufe Mediengruppe. All Rights - Reserved.
- - - diff --git a/include/boost/python.hpp b/include/boost/python.hpp index bdca0b2f..db665d8d 100644 --- a/include/boost/python.hpp +++ b/include/boost/python.hpp @@ -44,14 +44,12 @@ # include![]() |
![]() |
- ![]() |
+ ![]() |
@@ -64,7 +63,7 @@ the members of the header object like this:




and that's it!
-The next step is invoke Pyste in the command-line:
+The next step is invoke pyste in the command-line:python pyste.py --module=hello world.pyste
this will create a file "hello.cpp" in the directory where the command was run.
Pyste supports the following features:
-
-
-
-Note that, for functions/methods that return const T&, the policy
-return_value_policy<copy_const_reference>() wil be used by default, because
-that's normally what you want. You can change it to something else if you need
-to, though.
+doesn't set one?+If a function/method needs a policy and one was not set, Pyste will issue a error. +The user should then go in the interface file and set the policy for it, +otherwise the generated cpp won't compile. |
-To access the operators of a class, access the member operator like this -(supposing that C is a class being exported):
-
- exclude(C.operator['+'])
- exclude(C.operator['*'])
- exclude(C.operator['<<'])
-
-
-The string inside the brackets is the same as the name of the operator in C++.
![]() |
diff --git a/pyste/doc/running_pyste.html b/pyste/doc/running_pyste.html
index db02af8a..42834000 100644
--- a/pyste/doc/running_pyste.html
+++ b/pyste/doc/running_pyste.html
@@ -25,19 +25,19 @@
-To run Pyste, you will need:
--Installation for the tools is available in their respective webpages.
+Installation for the tools is avaiable in their respective webpages.
GCCXML must be accessible in the PATH environment variable, so
-that Pyste can call it. How to do this varies from platform to platform.
+that pyste can call it. How to do this varies from platform to platform.
|
Options explained:
-The -I and -D are preprocessor flags, which are needed by -GCCXML to parse -the header files correctly and by Pyste to find the header files declared in the +The -I and -D are preprocessor flags, which are needed by gccxml to parse the header files correctly and by pyste to find the header files declared in the interface files.
---multiple tells Pyste to generate multiple cpps for this module (one for -each header parsed) in the directory named by --out, instead of the usual -single cpp file. This mode is useful during development of a binding, because -you are constantly changing source files, re-generating the bindings and -recompiling. This saves a lot of time in compiling.
----out names the output file (default: <module>.cpp), or in multiple mode, -names a output directory for the files (default: <module>).
----no-using tells Pyste to don't declare "using namespace boost;" in the +--no-using tells pyste to don't declare "using namespace boost;" in the generated cpp, using the namespace boost::python explicitly in all declarations. Use only if you're having a name conflict in one of the files.
Use --pyste-ns to change the namespace where new types are declared (for -instance, the virtual wrappers). Use only if you are having any problems. By -default, Pyste uses the empty namespace.
----debug will write in the current directory a xml file as outputted by -GCCXML -for each header parsed. Useful for bug reports.
---h, --help, -v, --version are self-explaining, I believe. ;)
+instance, the virtual wrappers). Use only if one of your header files declare a +namespace named "pyste" and this is causing conflicts.So, the usage is simple enough:
>python pyste.py --module=mymodule file.pyste file2.pyste ...
will generate a file mymodule.cpp in the same dir where the command was executed. Now you can compile the file using the same instructions of the -tutorial. Or, if you prefer:
->python pyste.py --module=mymodule --multiple file.pyste file2.pyste ...
-will create a directory named "mymodule" in the current directory, and will -generate a bunch of cpp files, one for each header exported. You can then -compile them all into a single shared library (or dll).
+tutorial.Don't worry: normally GCCXML is already configured correctly for your plataform, @@ -124,15 +93,6 @@ which for Visual C++ 6 is normally located at:
with that, you should have little trouble setting up the flags.
-
- A note about Psyco-Although you don't have to install -Psyco to use Pyste, if you do, Pyste will make use of it to speed up the wrapper generation. Speed ups of 30% can be achieved, so it's highly recommended. - |
-
-Template classes can easily be exported too, but you can't export the template +Template Classes can easily exported too, but you can't export the "Template" itself... you have to export instantiations of it! So, if you want to export a std::vector, you will have to export vectors of int, doubles, etc.
@@ -55,7 +55,7 @@ rename the instantiations:
rename(double_inst, "DPoint")-Note that you can rename, exclude, set policies, etc, in the Template object +Note that you can rename, exclude, set policies, etc, in the Template class like you would do with a Function or a Class. This changes affect all future instantiations:
@@ -80,7 +80,7 @@ If you want to change a option of a particular instantiation, you can do so:
What if my template accepts more than one type?
-When you want to instantiate a template with more than one type, you can pass
+When you want to instantiate a Template with more than one type, you can pass
either a string with the types separated by whitespace, or a list of strings
("int double" or ["int", "double"] would both work).
diff --git a/pyste/doc/the_interface_files.html b/pyste/doc/the_interface_files.html
index 0e78e4ec..77246af7 100644
--- a/pyste/doc/the_interface_files.html
+++ b/pyste/doc/the_interface_files.html
@@ -27,7 +27,7 @@
The interface files are the heart of Pyste. The user creates one or more
interface files declaring the classes and functions he wants to export, and then
-invokes Pyste passing the interface files to it. Pyste then generates a single
+invokes pyste passing the interface files to it. Pyste then generates a single
cpp file with
Boost.Python code, with all the classes and functions exported.
diff --git a/pyste/doc/wrappers.html b/pyste/doc/wrappers.html
index 63c9a9ce..61bda6f8 100644
--- a/pyste/doc/wrappers.html
+++ b/pyste/doc/wrappers.html
@@ -30,21 +30,15 @@ Suppose you have this function:
std::vector<std::string> names();
-But you don't want to export std::vector<std::string>, you want this function -to return a python list of strings. -Boost.Python has excellent support for -that:
+But you don't want to export a vector<string>, you want this function to return +a python list of strings. +Boost.Python has an excellent support for that:
list names_wrapper()
{
list result;
- // call original function
vector<string> v = names();
- // put all the strings inside the python list
- vector<string>::iterator it;
- for (it = v.begin(); it != v.end(); ++it){
- result.append(*it);
- }
+ // put each string in the vector in the list
return result;
}
@@ -54,8 +48,9 @@ that:
}
-Nice heh? Pyste supports this mechanism too. You declare the names_wrapper -function in a header named "test_wrappers.h" and in the interface file:
+Nice heh? +Pyste supports this mechanism too. You declare the names_wrapper function in a +header, like "test_wrappers.h", and in the interface file:
Include("test_wrappers.h")
names = Function("names", "test.h")
@@ -68,14 +63,14 @@ You can optionally declare the function in the interface file itself:
"""
list names_wrapper()
{
- // code to call name() and convert the vector to a list...
+ // call name() and convert the vector to a list...
}
""")
names = Function("names", "test.h")
set_wrapper(names, names_wrapper)
-The same mechanism can be used with methods too. Just remember that the first +The same mechanism can be done with methods too. Just remember that the first parameter of wrappers for methods is a pointer to the class, like in Boost.Python:
@@ -87,7 +82,7 @@ Boost.Python: list names_wrapper(C* c) { - // same as before, calling c->names() and converting result to a list + // same as before, calling c->names() and converting result to a list }@@ -96,18 +91,6 @@ And then in the interface file:
C = Class("C", "test.h") set_wrapper(C.names, "names_wrapper") -
-
- Even though
-Boost.Python accepts either a pointer or a
-reference to the class in wrappers for member functions as the first parameter,
-Pyste expects them to be a pointer. Doing otherwise will prevent your
-code to compile when you set a wrapper for a virtual method.
- |
-
![]() |
diff --git a/pyste/example/.cvsignore b/pyste/example/.cvsignore
index 7ee0168b..ce1da4c5 100644
--- a/pyste/example/.cvsignore
+++ b/pyste/example/.cvsignore
@@ -1,2 +1 @@
-.sconsign
-*.obj
+*.cpp
diff --git a/pyste/example/basic.cpp b/pyste/example/basic.cpp
deleted file mode 100644
index 9e6ed996..00000000
--- a/pyste/example/basic.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "basic.h"
-
-namespace basic {
-
- int C::static_value = 3;
- const int C::const_static_value = 100;
-
-}
diff --git a/pyste/example/basic.h b/pyste/example/basic.h
index 5ab6ca58..a618c192 100644
--- a/pyste/example/basic.h
+++ b/pyste/example/basic.h
@@ -1,15 +1,7 @@
-#ifndef BASIC_H
-#define BASIC_H
-
-
-#include
| - Smart Pointers - | -
Copyright © 2003 Bruno da Silva de Oliveira
Copyright © 2002-2003 Joel de Guzman
diff --git a/pyste/src/ClassExporter.py b/pyste/src/ClassExporter.py
index 56f89313..44be258e 100644
--- a/pyste/src/ClassExporter.py
+++ b/pyste/src/ClassExporter.py
@@ -1,14 +1,11 @@
import exporters
from Exporter import Exporter
from declarations import *
+from enumerate import enumerate
from settings import *
-from policies import *
-from SingleCodeUnit import SingleCodeUnit
+from CodeUnit import CodeUnit
from EnumExporter import EnumExporter
-from utils import makeid, enumerate
-from copy import deepcopy
-import exporterutils
-import re
+
#==============================================================================
# ClassExporter
@@ -36,22 +33,16 @@ class ClassExporter(Exporter):
self.sections['scope'] = []
# declarations: outside the BOOST_PYTHON_MODULE macro
self.sections['declaration'] = []
- self.sections['declaration-outside'] = []
self.sections['include'] = []
# a list of Constructor instances
self.constructors = []
self.wrapper_generator = None
# a list of code units, generated by nested declarations
self.nested_codeunits = []
- self._exported_opaque_pointers = {}
def ScopeName(self):
- return makeid(self.class_.FullName()) + '_scope'
-
-
- def Unit(self):
- return makeid(self.class_.name)
+ return _ID(self.class_.FullName()) + '_scope'
def Name(self):
@@ -71,28 +62,24 @@ class ClassExporter(Exporter):
[x for x in self.class_.members if x.visibility == Scope.public]
- def ClassBases(self):
- bases = []
- def GetBases(class_):
- this_bases = [self.GetDeclaration(x.name) for x in class_.bases]
- bases.extend(this_bases)
- for base in this_bases:
- GetBases(base)
-
- GetBases(self.class_)
- return bases
-
-
def Order(self):
'''Return the TOTAL number of bases that this class has, including the
bases' bases. Do this because base classes must be instantialized
before the derived classes in the module definition.
'''
- return '%s_%s' % (len(self.ClassBases()), self.class_.FullName())
+ def BasesCount(classname):
+ decl = self.GetDeclaration(classname)
+ bases = [x.name for x in decl.bases]
+ total = 0
+ for base in bases:
+ total += BasesCount(base)
+ return len(bases) + total
+
+ return BasesCount(self.class_.FullName())
+
def Export(self, codeunit, exported_names):
- self.CheckForwardDeclarations()
self.ExportBasics()
self.ExportBases(exported_names)
self.ExportConstructors()
@@ -102,17 +89,9 @@ class ClassExporter(Exporter):
self.ExportOperators()
self.ExportNestedClasses(exported_names)
self.ExportNestedEnums()
- self.ExportSmartPointer()
- self.ExportOpaquePointerPolicies()
self.Write(codeunit)
- def CheckForwardDeclarations(self):
- for m in self.public_members:
- if isinstance(m, Function):
- exporterutils.WarnForwardDeclarations(m)
-
-
def Write(self, codeunit):
indent = self.INDENT
boost_ns = namespaces.python
@@ -157,9 +136,6 @@ class ClassExporter(Exporter):
declarations += nested_unit.Section('declaration')
if declarations:
codeunit.Write('declaration', declarations + '\n')
- declarations_outside = '\n'.join(self.sections['declaration-outside'])
- if declarations_outside:
- codeunit.Write('declaration-outside', declarations_outside + '\n')
# write the includes to the codeunit
includes = '\n'.join(self.sections['include'])
@@ -254,32 +230,50 @@ class ClassExporter(Exporter):
continue
name = self.info[var.name].rename or var.name
fullname = var.FullName()
- if var.type.const:
- def_ = '.def_readonly'
+ if var.static:
+ code = '%s->attr("%s") = %s;' % (self.ScopeName(), name, fullname)
+ self.Add('scope', code)
else:
- def_ = '.def_readwrite'
- code = '%s("%s", &%s)' % (def_, name, fullname)
- self.Add('inside', code)
+ if var.type.const:
+ def_ = '.def_readonly'
+ else:
+ def_ = '.def_readwrite'
+ code = '%s("%s", &%s)' % (def_, name, fullname)
+ self.Add('inside', code)
+
+ printed_policy_warnings = {}
+
+ def CheckPolicy(self, m):
+ 'Warns the user if this method needs a policy'
+ def IsString(type):
+ return type.const and type.name == 'char' and isinstance(type, PointerType)
+ needs_policy = isinstance(m.result, (ReferenceType, PointerType))
+ if IsString(m.result):
+ needs_policy = False
+ has_policy = self.info[m.name].policy is not None
+ if needs_policy and not has_policy:
+ warning = '---> Error: Method "%s" needs a policy.' % m.FullName()
+ if warning not in self.printed_policy_warnings:
+ print warning
+ print
+ self.printed_policy_warnings[warning] = 1
+
def ExportMethods(self):
'Export all the non-virtual methods of this class'
def OverloadName(m):
'Returns the name of the overloads struct for the given method'
- return makeid(m.FullName()) + ('_overloads_%i_%i' % (m.minArgs, m.maxArgs))
+ return _ID(m.FullName()) + ('_overloads_%i_%i' % (m.minArgs, m.maxArgs))
declared = {}
def DeclareOverloads(m):
'Declares the macro for the generation of the overloads'
if not m.virtual:
- if m.static:
- func = m.FullName()
- macro = 'BOOST_PYTHON_FUNCTION_OVERLOADS'
- else:
- func = m.name
- macro = 'BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS'
- code = '%s(%s, %s, %i, %i)\n' % (macro, OverloadName(m), func, m.minArgs, m.maxArgs)
+ func = m.name
+ code = 'BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(%s, %s, %i, %i)\n'
+ code = code % (OverloadName(m), func, m.minArgs, m.maxArgs)
if code not in declared:
declared[code] = True
self.Add('declaration', code)
@@ -306,28 +300,24 @@ class ClassExporter(Exporter):
methods = [x for x in self.public_members if IsExportable(x)]
for method in methods:
- method_info = self.info[method.name]
-
- # skip this method if it was excluded by the user
- if method_info.exclude:
- continue
+ if self.info[method.name].exclude:
+ continue # skip this method
- # rename the method if the user requested
- name = method_info.rename or method.name
+ name = self.info[method.name].rename or method.name
# warn the user if this method needs a policy and doesn't have one
- method_info.policy = exporterutils.HandlePolicy(method, method_info.policy)
+ self.CheckPolicy(method)
# check for policies
- policy = method_info.policy or ''
+ policy = self.info[method.name].policy or ''
if policy:
policy = ', %s%s()' % (namespaces.python, policy.Code())
# check for overloads
overload = ''
if method.minArgs != method.maxArgs:
# add the overloads for this method
- DeclareOverloads(method)
overload_name = OverloadName(method)
+ DeclareOverloads(method)
overload = ', %s%s()' % (namespaces.pyste, overload_name)
# build the .def string to export the method
@@ -342,7 +332,7 @@ class ClassExporter(Exporter):
code = '.staticmethod("%s")' % name
self.Add('inside', code)
# add wrapper code if this method has one
- wrapper = method_info.wrapper
+ wrapper = self.info[method.name].wrapper
if wrapper and wrapper.code:
self.Add('declaration', wrapper.code)
@@ -356,7 +346,7 @@ class ClassExporter(Exporter):
break
if has_virtual_methods:
- generator = _VirtualWrapperGenerator(self.class_, self.ClassBases(), self.info)
+ generator = _VirtualWrapperGenerator(self.class_, self.info)
self.Add('template', generator.FullName())
for definition in generator.GenerateDefinitions():
self.Add('inside', definition)
@@ -375,16 +365,12 @@ class ClassExporter(Exporter):
'()' : '__call__',
}
- # converters which have a special name in python
- # it's a map of a regular expression of the converter's result to the
- # appropriate python name
+ # converters which has a special name in python
SPECIAL_CONVERTERS = {
- re.compile(r'(const)?\s*double$') : '__float__',
- re.compile(r'(const)?\s*float$') : '__float__',
- re.compile(r'(const)?\s*int$') : '__int__',
- re.compile(r'(const)?\s*long$') : '__long__',
- re.compile(r'(const)?\s*char\s*\*?$') : '__str__',
- re.compile(r'(const)?.*::basic_string<.*>\s*(\*|\&)?$') : '__str__',
+ 'double' : '__float__',
+ 'float' : '__float__',
+ 'int' : '__int__',
+ 'long' : '__long__',
}
@@ -497,18 +483,16 @@ class ClassExporter(Exporter):
converters = [x for x in self.public_members if type(x) == ConverterOperator]
def ConverterMethodName(converter):
- result_fullname = converter.result.FullName()
- result_name = converter.result.name
- for regex, method_name in self.SPECIAL_CONVERTERS.items():
- if regex.match(result_fullname):
- return method_name
+ result_fullname = converter.result.name
+ if result_fullname in self.SPECIAL_CONVERTERS:
+ return self.SPECIAL_CONVERTERS[result_fullname]
else:
# extract the last name from the full name
- result_name = makeid(result_name)
+ result_name = _ID(result_fullname.split('::')[-1])
return 'to_' + result_name
for converter in converters:
- info = self.info['operator'][converter.result.FullName()]
+ info = self.info['operator'][converter.result.name]
# check if this operator should be excluded
if info.exclude:
continue
@@ -539,7 +523,7 @@ class ClassExporter(Exporter):
nested_info.name = nested_class.FullName()
exporter = ClassExporter(nested_info)
exporter.SetDeclarations(self.declarations + [nested_class])
- codeunit = SingleCodeUnit(None, None)
+ codeunit = CodeUnit(None)
exporter.Export(codeunit, exported_names)
self.nested_codeunits.append(codeunit)
@@ -552,30 +536,22 @@ class ClassExporter(Exporter):
enum_info.name = enum.FullName()
exporter = EnumExporter(enum_info)
exporter.SetDeclarations(self.declarations + [enum])
- codeunit = SingleCodeUnit(None, None)
+ codeunit = CodeUnit(None)
exporter.Export(codeunit, None)
self.nested_codeunits.append(codeunit)
-
-
- def ExportSmartPointer(self):
- smart_ptr = self.info.smart_ptr
- if smart_ptr:
- self.Add('template', smart_ptr % self.class_.FullName())
-
-
- def ExportOpaquePointerPolicies(self):
- # check all methods for 'return_opaque_pointer' policies
- methods = [x for x in self.public_members if isinstance(x, Method)]
- for method in methods:
- return_opaque_policy = return_value_policy(return_opaque_pointer)
- if self.info[method.name].policy == return_opaque_policy:
- macro = 'BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID(%s)' % method.result.name
- if macro not in self._exported_opaque_pointers:
- self.Add('declaration-outside', macro)
- self._exported_opaque_pointers[macro] = 1
+
+def _ID(name):
+ 'Returns the name as a valid identifier'
+ for invalidchar in ('::', '<', '>', ' ', ','):
+ name = name.replace(invalidchar, '_')
+ # avoid duplications of '_' chars
+ names = [x for x in name.split('_') if x]
+ return '_'.join(names)
+
+
#==============================================================================
# Virtual Wrapper utils
#==============================================================================
@@ -597,13 +573,10 @@ def _ParamsInfo(m, count=None):
class _VirtualWrapperGenerator(object):
'Generates code to export the virtual methods of the given class'
- def __init__(self, class_, bases, info):
+ def __init__(self, class_, info):
self.class_ = class_
- self.bases = deepcopy(bases)
self.info = info
- self.wrapper_name = makeid(class_.FullName()) + '_Wrapper'
- self.virtual_methods = None
- self.GenerateVirtualMethods()
+ self.wrapper_name = _ID(class_.FullName()) + '_Wrapper'
def DefaultImplementationNames(self, method):
@@ -648,18 +621,6 @@ class _VirtualWrapperGenerator(object):
# default implementations (with overloading)
# only for classes that are not abstract, and public methods
- def DefaultImpl(method, param_names):
- 'Return the body of a default implementation wrapper'
- wrapper = self.info[method.name].wrapper
- if not wrapper:
- # return the default implementation of the class
- return '%s%s::%s(%s);\n' % \
- (return_str, self.class_.FullName(), method.name, ', '.join(param_names))
- else:
- # return a call for the wrapper
- params = ', '.join(['this'] + param_names)
- return '%s%s(%s);\n' % (return_str, wrapper.FullName(), params)
-
if not method.abstract and method.visibility == Scope.public:
minArgs = method.minArgs
maxArgs = method.maxArgs
@@ -668,7 +629,8 @@ class _VirtualWrapperGenerator(object):
params, param_names, param_types = _ParamsInfo(method, argNum)
decl += '\n'
decl += indent + '%s %s(%s)%s {\n' % (result, impl_name, params, constantness)
- decl += indent*2 + DefaultImpl(method, param_names)
+ decl += indent*2 + '%s%s::%s(%s);\n' % \
+ (return_str, self.class_.FullName(), method.name, ', '.join(param_names))
decl += indent + '}\n'
return decl
@@ -683,7 +645,7 @@ class _VirtualWrapperGenerator(object):
class_name = self.class_.FullName()
wrapper_name = pyste + self.wrapper_name
result = method.result.FullName()
- is_method_unique = self.IsMethodUnique(method.name)
+ is_method_unique = self.class_.IsUnique(method.name)
constantness = ''
if method.const:
constantness = ' const'
@@ -699,7 +661,7 @@ class _VirtualWrapperGenerator(object):
param_list = [x.FullName() for x in method.parameters[:argNum]]
params = ', '.join(param_list)
signature = '%s (%s::*)(%s)%s' % (result, wrapper_name, params, constantness)
- default_pointer = '(%s)&%s::%s' % (signature, wrapper_name, impl_name)
+ default_pointer = '(%s)%s::%s' % (signature, wrapper_name, impl_name)
default_pointers.append(default_pointer)
# get the pointer of the method
@@ -721,36 +683,13 @@ class _VirtualWrapperGenerator(object):
return namespaces.pyste + self.wrapper_name
- def GenerateVirtualMethods(self):
- '''To correctly export all virtual methods, we must also make wrappers
- for the virtual methods of the bases of this class, as if the methods
- were from this class itself.
- This method creates the instance variable self.virtual_methods.
- '''
+ def VirtualMethods(self):
def IsVirtual(m):
return type(m) == Method and m.virtual
-
- all_members = self.class_.members[:]
- for base in self.bases:
- for base_member in base.members:
- base_member.class_ = self.class_.FullName()
- all_members.append(base_member)
- # extract the virtual methods, avoiding duplications
- self.virtual_methods = []
- already_added = {}
- for member in all_members:
- if IsVirtual(member) and not member.FullName() in already_added:
- self.virtual_methods.append(member)
- already_added[member.FullName()] = 0
+ return [m for m in self.class_.members if IsVirtual(m)]
+
- def IsMethodUnique(self, method):
- count = {}
- for m in self.virtual_methods:
- count[m.name] = count.get(m.name, 0) + 1
- return count[m.name] == 1
-
-
def Constructors(self):
def IsValid(m):
return isinstance(m, Constructor) and m.visibility == Scope.public
@@ -759,7 +698,7 @@ class _VirtualWrapperGenerator(object):
def GenerateDefinitions(self):
defs = []
- for method in self.virtual_methods:
+ for method in self.VirtualMethods():
exclude = self.info[method.name].exclude
# generate definitions only for public methods and non-abstract methods
if method.visibility == Scope.public and not method.abstract and not exclude:
@@ -792,7 +731,7 @@ class _VirtualWrapperGenerator(object):
code += cons_code
# generate the body
body = []
- for method in self.virtual_methods:
+ for method in self.VirtualMethods():
if not self.info[method.name].exclude:
body.append(self.Declaration(method, indent))
body = '\n'.join(body)
diff --git a/pyste/src/CodeUnit.py b/pyste/src/CodeUnit.py
new file mode 100644
index 00000000..ac123f99
--- /dev/null
+++ b/pyste/src/CodeUnit.py
@@ -0,0 +1,78 @@
+from settings import *
+
+#==============================================================================
+# RemoveDuplicatedLines
+#==============================================================================
+def RemoveDuplicatedLines(text):
+ includes = text.splitlines()
+ d = dict([(include, 0) for include in includes])
+ return '\n'.join(d.keys())
+
+
+#==============================================================================
+# CodeUnit
+#==============================================================================
+class CodeUnit:
+ '''
+ Represents a cpp file, where other objects can write in one of the
+ predefined sections.
+ The avaiable sections are:
+ include - The include area of the cpp file
+ declaration - The part before the module definition
+ module - Inside the BOOST_PYTHON_MODULE macro
+ '''
+
+ USING_BOOST_NS = True
+
+ def __init__(self, modulename):
+ self.modulename = modulename
+ # define the avaiable sections
+ self.code = {}
+ self.code['include'] = ''
+ self.code['declaration'] = ''
+ self.code['module'] = ''
+
+
+ def Write(self, section, code):
+ 'write the given code in the section of the code unit'
+ if section not in self.code:
+ raise RuntimeError, 'Invalid CodeUnit section: %s' % section
+ self.code[section] += code
+
+
+ def Section(self, section):
+ return self.code[section]
+
+
+ def Save(self, filename):
+ 'Writes this code unit to the filename'
+ space = '\n\n'
+ fout = file(filename, 'w')
+ # includes
+ includes = RemoveDuplicatedLines(self.code['include'])
+ fout.write('\n' + self._leftEquals('Includes'))
+ fout.write('#include