diff --git a/doc/tutorial/doc/html/python/exposing.html b/doc/tutorial/doc/html/python/exposing.html index 4c841e3e..e15df084 100644 --- a/doc/tutorial/doc/html/python/exposing.html +++ b/doc/tutorial/doc/html/python/exposing.html @@ -31,7 +31,6 @@
struct Base { + virtual ~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; -}; - - -struct BaseWrap : Base -{ - BaseWrap(PyObject* self_) - : self(self_) {} - BaseWrap(PyObject* self_, Base const& copy) - : Base(copy), self(self_) {} - int f() { return call_method<int>(self, "f"); } - int default_f() { return Base::f(); } // <<=== ***ADDED*** - PyObject* self; +One of the goals of Boost.Python is to be minimally intrusive on an existing C++ +design. In principle, it should be possible to expose the interface for a 3rd +party library without changing it. It is not ideal to add anything to our class +Base. Yet, when you have a virtual function that's going to be overridden in +Python and called polymorphically from C++, we'll need to add some +scaffoldings to make things work properly. What we'll do is write a class +wrapper that derives from Base that will unintrusively hook into the virtual +functions so that a Python override may be called: +struct BaseWrap : Base, wrapper<Base> +{ + int f() + { + return this->get_override("f")(); + } };++Notice too that in addition to inheriting from Base, we also multiply- +inherited wrapper<Base> (See Wrapper). The +wrapper template makes the job of wrapping classes that are meant to +overridden in Python, easier.
+++
+ + + MSVC6/7 Workaround + + +If you are using Microsoft Visual C++ 6 or 7, you have to write f as: + +return call<int>(this->get_override("f").ptr());.
+BaseWrap's overridden virtual member function f in effect calls the +corresponding method of the Python object through get_override.
++Finally, exposing Base:
+class_<BaseWrap, boost::noncopyable>("Base") + .def("f", pure_virtual(&Base::f)) + ;+pure_virtual signals Boost.Python that the function f is a pure virtual +function.
-
@@ -297,120 +313,19 @@ many object oriented languages uses the term 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.
---
- - - Why do we need BaseWrap? - -
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."
- - --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.
- - --Note however that you don't need to do this to get methods overridden -in Python to behave virtually when called fromPython. 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 fromC++.]
--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.
- -- --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. -
--Recall that in the previous section, we -wrapped a class with a pure virtual function that we then implemented in -C++ or Python classes derived from it. Our base class:
+We've seen in the previous section how classes with pure virtual functions are +wrapped using Boost.Python's class wrapper +facilities. If we wish to wrap non-pure-virtual functions instead, the +mechanism is a bit different. ++Recall that in the previous section, we +wrapped a class with a pure virtual function that we then implemented in C++, or +Python classes derived from it. Our base class:
struct Base { virtual int f() = 0; @@ -420,31 +335,46 @@ had a pure virtual function f. If, however, its member not declared as pure virtual:struct Base { + virtual ~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; +We wrap it this way: +struct BaseWrap : Base, wrapper<Base> +{ + int f() + { + if (override f = this->get_override("f")) + return f(); // *note* + return Base::f(); + } + + int default_f() { return this->Base::f(); } };-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, BaseWrap, boost::noncopyable>("Base") - .def("f", &Base::f, &BaseWrap::default_f)+Notice how we implemented BaseWrap::f. Now, we have to check if there is an +override for f. If none, then we call Base::f(). ++
+ + + MSVC6/7 Workaround + + +If you are using Microsoft Visual C++ 6 or 7, you have to rewrite the line +with the *note* as: + +return call<char const*>(f.ptr());.
-Note that we are allowing Base objects to be instantiated this time, -unlike before where we specifically defined the class_<Base> with -no_init.
+Finally, exposing: +class_<BaseWrap, boost::noncopyable>("Base") + .def("f", &Base::f, &BaseWrap::default_f) + ;++Take note that we expose both &Base::f and &BaseWrap::default_f. +Boost.Python needs to keep track of 1) the dispatch function f and 2) the +forwarding function to its default implementation default_f. There's a +special def function for this purpose.
In Python, the results would be as expected:
>>> base = Base() @@ -461,20 +391,12 @@ Calling base.f(): 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-Python Operators
+Python OperatorsC is well known for the abundance of operators. C++ extends this to the extremes by allowing operator overloading. Boost.Python takes advantage of @@ -511,7 +433,7 @@ 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".
-Special Methods
+Special MethodsPython has a few more Special Methods. Boost.Python supports all of the standard special method names supported by real Python class instances. A