bpl_mods.txt
来自「Boost provides free peer-reviewed portab」· 文本 代码 · 共 913 行 · 第 1/3 页
TXT
913 行
{ // 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')9Things to notice about the dispatcher class:* The key element which allows overriding in Python is the ``call_method`` invocation, which uses the same global type conversion registry as the C++ function wrapping does to convert its arguments from C++ to Python and its return type from Python to C++.* Any constructor signatures you wish to wrap must be replicated with an initial ``PyObject*`` argument* The dispatcher must store this argument so that it can be used to invoke ``call_method``* The ``f_default`` member function is needed when the function being exposed is not pure virtual; there's no other way ``Base::f`` can be called on an object of type ``BaseWrap``, since it overrides ``f``.Admittedly, this formula is tedious to repeat, especially on a projectwith many polymorphic classes; that it is necessary reflectslimitations in C++'s compile-time reflection capabilities. Severalefforts are underway to write front-ends for Boost.Python which cangenerate these dispatchers (and other wrapping code) automatically.If these are successful it will mark a move away from wrappingeverything directly in pure C++ for many of our users.--------------- Serialization---------------*Serialization* is the process of converting objects in memory to aform that can be stored on disk or sent over a network connection. Theserialized object (most often a plain string) can be retrieved andconverted back to the original object. A good serialization system willautomatically convert entire object hierarchies. Python's standard``pickle`` module is such a system. It leverages the language's strongruntime introspection facilities for serializing practically arbitraryuser-defined objects. With a few simple and unintrusive provisions thispowerful 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 *differentcomputer* 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 fasterprocessing.Boost.Python's ``pickle_suite`` fully supports the ``pickle`` protocoldefined in the standard Python documentation. There is a one-to-onecorrespondence between the standard pickling methods (``__getinitargs__``,``__getstate__``, ``__setstate__``) and the functions defined by theuser in the class derived from ``pickle_suite`` (``getinitargs``,``getstate``, ``setstate``). The ``class_::def_pickle()`` member functionis used to establish the Python bindings for all user-defined functionssimultaneously. Correct signatures for these functions are enforced atcompile time. Non-sensical combinations of the three pickle functionsare also rejected at compile time. These measures are designed tohelp the user in avoiding obvious errors.Enabling serialization of more complex C++ objects requires a littlemore work than is shown in the example above. Fortunately the``object`` interface (see next section) greatly helps in keeping thecode manageable.------------------ Object interface------------------Experienced extension module authors will be familiar with the 'C' viewof Python objects, the ubiquitous ``PyObject*``. Most if not all Python'C' API functions involve ``PyObject*`` as arguments or return type. Amajor complication is the raw reference counting interface presented tothe 'C' programmer. E.g. some API functions return *new references* andothers return *borrowed references*. It is up to the extension modulewriter to properly increment and decrement reference counts. Thisquickly becomes cumbersome and error prone, especially if there aremultiple execution paths.Boost.Python provides a type ``object`` which is essentially a highlevel wrapper around ``PyObject*``. ``object`` automates referencecounting as much as possible. It also provides the facilities forconverting arbitrary C++ types to Python objects and vice versa.This significantly reduces the learning effort for prospectiveextension module writers.Creating an ``object`` from any other type is extremely simple:: object o(3);``object`` has templated interactions with all other types, withautomatic to-python conversions. It happens so naturally that it'seasily overlooked.The ``extract<T>`` class template can be used to convert Python objectsto C++ types:: double x = extract<double>(o);All registered user-defined conversions are automatically accessiblethrough the ``object`` interface. With reference to the ``World`` classdefined in previous examples:: object as_python_object(World("howdy")); World back_as_c_plus_plus_object = extract<World>(as_python_object);If a C++ type cannot be converted to a Python object an appropriateexception is thrown at runtime. Similarly, an appropriate exception isthrown if a C++ type cannot be extracted from a Python object.``extract<T>`` provides facilities for avoiding exceptions if this isdesired.The ``object::attr()`` member function is available for accessingand manipulating attributes of Python objects. For example:: object planet(World()); planet.attr("set")("howdy");``planet.attr("set")`` returns a callable ``object``. ``"howdy"`` isconverted to a Python string object which is then passed as an argumentto the ``set`` method.The ``object`` type is accompanied by a set of derived typesthat mirror the Python built-in types such as ``list``, ``dict``,``tuple``, etc. as much as possible. This enables convenientmanipulation 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++.================= Thinking hybrid=================For many applications runtime performance considerations are veryimportant. This is particularly true for most scientific applications.Often the performance considerations dictate the use of a compiledlanguage for the core algorithms. Traditionally the decision to use aparticular programming language is an exclusive one. Because of thepractical and mental difficulties of combining different languages manysystems are written in just one language. This is quite unfortunatebecause the price payed for runtime performance is typically asignificant overhead due to static typing. For example, our experienceshows that developing maintainable C++ code is typically much moretime-consuming and requires much more hard-earned working experiencethan developing useful Python code. A related observation is that manycompiled packages are augmented by some type of rudimentary scriptinglayer. These ad hoc solutions clearly show that many times a compiledlanguage alone does not get the job done. On the other hand it is alsoclear that a pure Python implementation is too slow for numericallyintensive production code.Boost.Python enables us to *think hybrid* when developing newapplications. Python can be used for rapidly prototyping anew application. Python's ease of use and the large pool of standardlibraries give us a head start on the way to a first working system. Ifnecessary, the working procedure can be used to discover therate-limiting algorithms. To maximize performance these can bereimplemented in C++, together with the Boost.Python bindings needed totie them back into the existing higher-level procedure.Of course, this *top-down* approach is less attractive if it is clearfrom the start that many algorithms will eventually have to beimplemented in a compiled language. Fortunately Boost.Python alsoenables us to pursue a *bottom-up* approach. We have used this approachvery successfully in the development of a toolbox for scientificapplications (scitbx) that we will describe elsewhere. The toolboxstarted out mainly as a library of C++ classes with Boost.Pythonbindings, and for a while the growth was mainly concentrated on the C++parts. However, as the toolbox is becoming more complete, more and morenewly added functionality can be implemented in Python. We expect thistrend to continue, as illustrated qualitatively in this figure:.. image:: python_cpp_mix.pngThis figure shows the ratio of newly added C++ and Python code overtime as new algorithms are implemented. We expect this ratio to levelout near 70% Python. The increasing ability to solve new problemsmostly with the easy-to-use Python language rather than a necessarilymore arcane statically typed language is the return on the investmentof learning how to use Boost.Python. The ability to solve some problemsentirely using only Python will enable a larger group of people toparticipate in the rapid development of new applications.============= Conclusions=============The examples in this paper illustrate that Boost.Python enablesseamless interoperability between C++ and Python. Importantly, this isachieved without introducing a third syntax: the Python/C++ interfacedefinitions are written in pure C++. This avoids any problems withparsing the C++ code to be interfaced to Python, yet the interfacedefinitions are concise and maintainable. Freed from most of thedevelopment-time penalties of crossing a language boundary, softwaredesigners can take full advantage of two rich and complimentarylanguage environments. In practice it turns out that some things arevery difficult to do with pure Python/C (e.g. an efficient arraylibrary with an intuitive interface in the compiled language) andothers are very difficult to do with pure C++ (e.g. serialization).If one has the luxury of being able to design a software system as ahybrid system from the ground up there are many new ways of avoidingroad blocks in one language or the other... I'm not ready to give up on all of this quite yet.. Perhaps one day we'll have a language with the simplicity and expressive power of Python and the compile-time muscle of C++. Being able to take advantage of all of these facilities without paying the mental and development-time penalties of crossing a language barrier would bring enormous benefits. Until then, interoperability tools like Boost.Python can help lower the barrier and make the benefits of both languages more accessible to both communities.=========== Footnotes===========.. [#mi] For hard-core new-style class/extension module writers it is worth noting that the normal requirement that all extension classes with data form a layout-compatible single-inheritance chain is lifted for Boost.Python extension classes. Clearly, either ``Base1`` or ``Base2`` has to occupy a different offset in the ``Derived`` class instance. This is possible because the wrapped part of BPL extension class instances is never assumed to have a fixed offset within the wrapper.=========== Citations===========.. [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
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?