bpl_mods.txt

来自「Boost provides free peer-reviewed portab」· 文本 代码 · 共 913 行 · 第 1/3 页

TXT
913
字号
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, peoplesometimes find the syntax bit confusing because it doesn't look likemost of the C++ code they're used to. All the same, this is juststandard C++.  Because of their flexible syntax and operatoroverloading, C++ and Python are great for defining domain-specific(sub)languages(DSLs), and that's what we've done in BPL.  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 classcalled ``World`` in the extension module, and associates it with theC++ type ``World`` in the BPL type conversion registry.  We might havealso 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 memberaccess in the original example: C++ allows any amount of whitespace oneither side of a token, and placing the dot at the beginning of eachline allows us to chain as many successive calls to member functionsas we like with a uniform syntax.  The other key fact that allowschaining is that ``class_<>`` member functions all return a referenceto ``*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 aBoost.Python class wrapper in this way, but the rest of this paperwill tend to 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'Constructors============Since our ``World`` class is just a plain ``struct``, it has animplicit no-argument (nullary) constructor.  Boost.Python exposes thenullary constructor by default, which is why we were able to write:>>> planet = hello.World()However, well-designed classes in any language may require constructorarguments 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.  Inparticular, we can't take their address: ``&World::World`` is anerror.  The library provides a different interface for specifyingconstructors.  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 canexpose 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, andconstructors to be overloaded to mirror C++ overloading.Data Members and Properties===========================Any publicly-accessible data members in a C++ class can be easilyexposed 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 whenwrapping large data structures.  In fact, no instance ``__dict__``will be created at all unless attributes are explicitly added fromPython.  BPL 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 poordesign because they break encapsulation, and style guides usuallydictate the use of "getter" and "setter" functions instead.  InPython, however, ``__getattr__``, ``__setattr__``, and since 2.2,``property`` mean that attribute access is just one morewell-encapsulated syntactic tool at the programmer's disposal.  BPLbridges this idiomatic gap by making Python ``property`` creationdirectly available to users.  So if ``msg`` were private, we couldstill 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 Python2.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)Operators and Special Functions===============================The ability to write arithmetic operators for user-defined types thatC++ and Python both allow the definition of has been a major factor inthe popularity of both languages for scientific computing.  Thesuccess of packages like NumPy attests to the power of exposingoperators in extension modules.  In this example we'll wrap a classrepresenting a position in a large file::    class FilePos { /*...*/ };    // Linear offset    FilePos     operator+(FilePos, int);    FilePos     operator+(int, FilePos);    FilePos     operator-(FilePos, int);        // Distance between two FilePos objects    int         operator-(FilePos, FilePos);    // Offset with assignment    FilePos&    operator+=(FilePos&, int);    FilePos&    operator-=(FilePos&, int);    // Comparison    bool        operator<(FilePos, FilePos);The wrapping code looks like this::    class_<FilePos>("FilePos")        .def(self + int())     // __add__        .def(int() + self)     // __radd__        .def(self - int())     // __sub__        .def(self - self)      // __sub__        .def(self += int())    // __iadd__        .def(self -= int())    // __isub__        .def(self < self);     // __lt__        ;The magic is performed using a simplified application of "expressiontemplates" [VELD1995]_, a technique originally developed by foroptimization of high-performance matrix algebra expressions.  Theessence is that instead of performing the computation immediately,operators are overloaded to construct a type *representing* thecomputation.  In matrix algebra, dramatic optimizations are oftenavailable when the structure of an entire expression can be taken intoaccount, rather than processing each operation "greedily".Boost.Python uses the same technique to build an appropriate Pythoncallable object based on an expression involving ``self``, which isthen added to the class.Inheritance===========C++ inheritance relationships can be represented to Boost.Python by addingan optional ``bases<...>`` argument to the ``class_<...>`` templateparameter list as follows::     class_<Derived, bases<Base1,Base2> >("Derived")          ...This has two effects:  1. When the ``class_<...>`` is created, Python type objects   corresponding to ``Base1`` and ``Base2`` are looked up in the BPL   registry, and are used as bases for the new Python ``Derived`` type   object [#mi]_, so methods exposed for the Python ``Base1`` and   ``Base2`` types are automatically members of the ``Derived`` type.   Because the registry is global, this works correctly even if   ``Derived`` is exposed in a different module from either of its   bases.2. C++ conversions from ``Derived`` to its bases are added to the   Boost.Python registry.  Thus wrapped C++ methods expecting (a   pointer or reference to) an object of either base type can be   called with an object wrapping a ``Derived`` instance.  Wrapped   member functions of class ``T`` are treated as though they have an   implicit first argument of ``T&``, so these conversions are   necessary to allow the base class methods to be called for derived   objects.Of course it's possible to derive new Python classes from wrapped C++class instances.  Because Boost.Python uses the new-style classsystem, that works very much as for the Python built-in types.  Thereis one significant detail in which it differs: the built-in typesgenerally establish their invariants in their ``__new__`` function, sothat derived classes do not need to call ``__init__`` on the baseclass before invoking its methods :>>> class L(list):...      def __init__(self):...          pass...>>> L().reverse()>>> Because C++ object construction is a one-step operation, C++ instancedata cannot be constructed until the arguments are available, in the``__init__`` function:>>> class D(SomeBPLClass):...      def __init__(self):...          pass...>>> D().some_bpl_method()Traceback (most recent call last):  File "<stdin>", line 1, in ?TypeError: bad argument type for built-in operationThis happened because Boost.Python couldn't find instance data of type``SomeBPLClass`` within the ``D`` instance; ``D``'s ``__init__``function masked construction of the base class.  It could be correctedby either removing ``D``'s ``__init__`` function or having it call``SomeBPLClass.__init__(...)`` explicitly.Virtual Functions=================Deriving new types in Python from extension classes is not veryinteresting unless they can be used polymorphically from C++.  Inother words, Python method implementations should appear to overridethe implementation of C++ virtual functions when called *through baseclass pointers/references from C++*.  Since the only way to alter thebehavior of a virtual function is to override it in a derived class,the user must build a special derived class to dispatch a polymorphicclass' 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

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?