📄 quickstart.txt
字号:
struct Num
{
Num();
float get() const;
void set(float value);
...
};
However, in Python attribute access is fine; it doesn't neccessarily break
encapsulation to let users handle attributes directly, because the
attributes can just be a different syntax for a method call. Wrapping our
[^Num] class using Boost.Python:
class_<Num>("Num")
.add_property("rovalue", &Num::get)
.add_property("value", &Num::get, &Num::set);
And at last, in Python:
>>> x = Num()
>>> x.value = 3.14
>>> x.value, x.rovalue
(3.14, 3.14)
>>> x.rovalue = 2.17 # error!
Take note that the class property [^rovalue] is exposed as [*read-only]
since the [^rovalue] setter member function is not passed in:
.add_property("rovalue", &Num::get)
[page:1 Inheritance]
In the previous examples, we dealt with classes that are not polymorphic.
This is not often the case. Much of the time, we will be wrapping
polymorphic classes and class hierarchies related by inheritance. We will
often have to write Boost.Python wrappers for classes that are derived from
abstract base classes.
Consider this trivial inheritance structure:
struct Base { virtual ~Base(); };
struct Derived : Base {};
And a set of C++ functions operating on [^Base] and [^Derived] object
instances:
void b(Base*);
void d(Derived*);
Base* factory() { return new Derived; }
We've seen how we can wrap the base class [^Base]:
class_<Base>("Base")
/*...*/
;
Now we can inform Boost.Python of the inheritance relationship between
[^Derived] and its base class [^Base]. Thus:
class_<Derived, bases<Base> >("Derived")
/*...*/
;
Doing so, we get some things for free:
# Derived automatically inherits all of Base's Python methods (wrapped C++ member functions)
# [*If] Base is polymorphic, [^Derived] objects which have been passed to Python via a pointer or reference to [^Base] can be passed where a pointer or reference to [^Derived] is expected.
Now, we shall expose the C++ free functions [^b] and [^d] and [^factory]:
def("b", b);
def("d", d);
def("factory", factory);
Note that free function [^factory] is being used to generate new
instances of class [^Derived]. In such cases, we use
[^return_value_policy<manage_new_object>] to instruct Python to adopt
the pointer to [^Base] and hold the instance in a new Python [^Base]
object until the the Python object is destroyed. We shall see more of
Boost.Python [@call_policies.html call policies] later.
// Tell Python to take ownership of factory's result
def("factory", factory,
return_value_policy<manage_new_object>());
[page:1 Class Virtual Functions]
In this section, we shall learn how to make functions behave
polymorphically through virtual functions. Continuing our example, let us
add a virtual function to our [^Base] class:
struct Base
{
virtual int f() = 0;
};
Since [^f] is a pure virtual function, [^Base] is now an abstract
class. Given an instance of our class, the free function [^call_f]
calls some implementation of this virtual function in a concrete
derived class:
int call_f(Base& b) { return b.f(); }
To allow this function to be implemented in a Python derived class, we
need to create a class wrapper:
struct BaseWrap : Base
{
BaseWrap(PyObject* self_)
: self(self_) {}
int f() { return call_method<int>(self, "f"); }
PyObject* self;
};
[blurb __detail__ [*member function and methods][br][br] Python, like
many object oriented languages uses the term [*methods]. Methods
correspond roughly to C++'s [*member functions]]
Our class wrapper [^BaseWrap] is derived from [^Base]. Its overridden
virtual member function [^f] in effect calls the corresponding method
of the Python object [^self], which is a pointer back to the Python
[^Base] object holding our [^BaseWrap] instance.
[blurb __note__ [*Why do we need BaseWrap?][br][br]
['You may ask], "Why do we need the [^BaseWrap] derived class? This could
have been designed so that everything gets done right inside of
Base."[br][br]
One of the goals of Boost.Python is to be minimally intrusive on an
existing C++ design. In principle, it should be possible to expose the
interface for a 3rd party library without changing it. To unintrusively
hook into the virtual functions so that a Python override may be called, we
must use a derived class.[br][br]
Note however that you don't need to do this to get methods overridden
in Python to behave virtually when called ['from] [*Python]. The only
time you need to do the [^BaseWrap] dance is when you have a virtual
function that's going to be overridden in Python and called
polymorphically ['from] [*C++].]
Wrapping [^Base] and the free function [^call_f]:
class_<Base, BaseWrap, boost::noncopyable>("Base", no_init)
;
def("call_f", call_f);
Notice that we parameterized the [^class_] template with [^BaseWrap] as the
second parameter. What is [^noncopyable]? Without it, the library will try
to create code for converting Base return values of wrapped functions to
Python. To do that, it needs Base's copy constructor... which isn't
available, since Base is an abstract class.
In Python, let us try to instantiate our [^Base] class:
>>> base = Base()
RuntimeError: This class cannot be instantiated from Python
Why is it an error? [^Base] is an abstract class. As such it is advisable
to define the Python wrapper with [^no_init] as we have done above. Doing
so will disallow abstract base classes such as [^Base] to be instantiated.
[page:1 Deriving a Python Class]
Continuing, we can derive from our base class Base in Python and override
the virtual function in Python. Before we can do that, we have to set up
our [^class_] wrapper as:
class_<Base, BaseWrap, boost::noncopyable>("Base")
;
Otherwise, we have to suppress the Base class' [^no_init] by adding an
[^__init__()] method to all our derived classes. [^no_init] actually adds
an [^__init__] method that raises a Python RuntimeError exception.
>>> class Derived(Base):
... def f(self):
... return 42
...
Cool eh? A Python class deriving from a C++ class!
Let's now make an instance of our Python class [^Derived]:
>>> derived = Derived()
Calling [^derived.f()]:
>>> derived.f()
42
Will yield the expected result. Finally, calling calling the free function
[^call_f] with [^derived] as argument:
>>> call_f(derived)
42
Will also yield the expected result.
Here's what's happening:
# [^call_f(derived)] is called in Python
# This corresponds to [^def("call_f", call_f);]. Boost.Python dispatches this call.
# [^int call_f(Base& b) { return b.f(); }] accepts the call.
# The overridden virtual function [^f] of [^BaseWrap] is called.
# [^call_method<int>(self, "f");] dispatches the call back to Python.
# [^def f(self): return 42] is finally called.
[page:1 Virtual Functions with Default Implementations]
Recall that in the [@class_virtual_functions.html previous section], we
wrapped a class with a pure virtual function that we then implemented in
C++ or Python classes derived from it. Our base class:
struct Base
{
virtual int f() = 0;
};
had a pure virtual function [^f]. If, however, its member function [^f] was
not declared as pure virtual:
struct Base
{
virtual int f() { return 0; }
};
and instead had a default implementation that returns [^0], as shown above,
we need to add a forwarding function that calls the [^Base] default virtual
function [^f] implementation:
struct BaseWrap : Base
{
BaseWrap(PyObject* self_)
: self(self_) {}
int f() { return call_method<int>(self, "f"); }
int default_f() { return Base::f(); } // <<=== ***ADDED***
PyObject* self;
};
Then, Boost.Python needs to keep track of 1) the dispatch function [^f] and
2) the forwarding function to its default implementation [^default_f].
There's a special [^def] function for this purpose. Here's how it is
applied to our example above:
class_<Base, BaseWrap>("Base")
.def("f", &Base::f, &BaseWrap::default_f)
Note that we are allowing [^Base] objects to be instantiated this time,
unlike before where we specifically defined the [^class_<Base>] with
[^no_init].
In Python, the results would be as expected:
>>> base = Base()
>>> class Derived(Base):
... def f(self):
... return 42
...
>>> derived = Derived()
Calling [^base.f()]:
>>> base.f()
0
Calling [^derived.f()]:
>>> derived.f()
42
Calling [^call_f], passing in a [^base] object:
>>> call_f(base)
0
Calling [^call_f], passing in a [^derived] object:
>>> call_f(derived)
42
[page:1 Class Operators/Special Functions]
[h2 Python Operators]
C is well known for the abundance of operators. C++ extends this to the
extremes by allowing operator overloading. Boost.Python takes advantage of
this and makes it easy to wrap C++ operator-powered classes.
Consider a file position class [^FilePos] and a set of operators that take
on FilePos instances:
class FilePos { /*...*/ };
FilePos operator+(FilePos, int);
FilePos operator+(int, FilePos);
int operator-(FilePos, FilePos);
FilePos operator-(FilePos, int);
FilePos& operator+=(FilePos&, int);
FilePos& operator-=(FilePos&, int);
bool operator<(FilePos, FilePos);
The class and the various operators can be mapped to Python rather easily
and intuitively:
class_<FilePos>("FilePos")
.def(self + int()) // __add__
.def(int() + self) // __radd__
.def(self - self) // __sub__
.def(self - int()) // __sub__
.def(self += int()) // __iadd__
.def(self -= other<int>())
.def(self < self); // __lt__
The code snippet above is very clear and needs almost no explanation at
all. It is virtually the same as the operators' signatures. Just take
note that [^self] refers to FilePos object. Also, not every class [^T] that
you might need to interact with in an operator expression is (cheaply)
default-constructible. You can use [^other<T>()] in place of an actual
[^T] instance when writing "self expressions".
[h2 Special Methods]
Python has a few more ['Special Methods]. Boost.Python supports all of the
standard special method names supported by real Python class instances. A
similar set of intuitive interfaces can also be used to wrap C++ functions
that correspond to these Python ['special functions]. Example:
class Rational
{ operator double() const; };
Rational pow(Rational, Rational);
Rational abs(Rational);
ostream& operator<<(ostream&,Rational);
class_<Rational>()
.def(float_(self)) // __float__
.def(pow(self, other<Rational>)) // __pow__
.def(abs(self)) // __abs__
.def(str(self)) // __str__
;
Need we say more?
[blurb __detail__ What is the business of [^operator<<] [^.def(str(self))]?
Well, the method [^str] requires the [^operator<<] to do its work (i.e.
[^operator<<] is used by the method defined by def(str(self)).]
[page Functions]
In this chapter, we'll look at Boost.Python powered functions in closer
detail. We shall see some facilities to make exposing C++ functions to
Python safe from potential pifalls such as dangling pointers and
references. We shall also see facilities that will make it even easier for
us to expose C++ functions that take advantage of C++ features such as
overloading and default arguments.
[:['Read on...]]
But before you do, you might want to fire up Python 2.2 or later and type
[^>>> import this].
[pre
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -