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 @@
Class Properties
Inheritance
Class Virtual Functions
-
Deriving a Python Class
Virtual Functions with Default Implementations
Class Operators/Special Functions
@@ -226,10 +225,13 @@ Now we can inform Boost.Python of the inheritance relationship between Doing so, we get some things for free:

  1. -Derived automatically inherits all of Base's Python methods (wrapped C++ member functions) +Derived automatically inherits all of Base's Python methods + (wrapped C++ member functions)
  2. -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. +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.

@@ -252,41 +254,55 @@ Boost.Python

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:

+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 ~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 methodsmember 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.

- -
-

-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:

-
    -
  1. -call_f(derived) is called in Python -
  2. -
  3. -This corresponds to def("call_f", call_f);. Boost.Python dispatches this call. -
  4. -
  5. -int call_f(Base& b) { return b.f(); } accepts the call. -
  6. -
  7. -The overridden virtual function f of BaseWrap is called. -
  8. -
  9. -call_method<int>(self, "f"); dispatches the call back to Python. -
  10. -
  11. -def f(self): return 42 is finally called. -
  12. -

Virtual Functions with Default Implementations

-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

Class Operators/Special Functions

-Python Operators

+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 @@ -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 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