From 2a530bb9d28993196405a2eb3f300ab3f296f99e Mon Sep 17 00:00:00 2001 From: Joel de Guzman Date: Sun, 23 Feb 2003 02:23:15 +0000 Subject: [PATCH] Tutorial updates. Added virtual functions with default implementations and reshufled the overloading topics to discuss manual overloading first. [SVN r17597] --- doc/tutorial/doc/auto_overloading.html | 112 ++++++++++++++ doc/tutorial/doc/call_policies.html | 6 +- .../class_operators_special_functions.html | 6 +- doc/tutorial/doc/class_virtual_functions.html | 113 +------------- doc/tutorial/doc/default_arguments.html | 18 ++- doc/tutorial/doc/deriving_a_python_class.html | 82 ++++++++++ doc/tutorial/doc/object_interface.html | 6 +- doc/tutorial/doc/overloading.html | 87 ++--------- doc/tutorial/doc/quickstart.txt | 142 ++++++++++-------- ...unctions_with_default_implementations.html | 122 +++++++++++++++ doc/tutorial/index.html | 17 ++- 11 files changed, 447 insertions(+), 264 deletions(-) create mode 100644 doc/tutorial/doc/auto_overloading.html create mode 100644 doc/tutorial/doc/deriving_a_python_class.html create mode 100644 doc/tutorial/doc/virtual_functions_with_default_implementations.html diff --git a/doc/tutorial/doc/auto_overloading.html b/doc/tutorial/doc/auto_overloading.html new file mode 100644 index 00000000..7f1208d4 --- /dev/null +++ b/doc/tutorial/doc/auto_overloading.html @@ -0,0 +1,112 @@ + + + +Auto-Overloading + + + + + + + + + + +
+ + Auto-Overloading +
+
+ + + + + + +
+

+It was mentioned in passing in the previous section that +BOOST_PYTHON_FUNCTION_OVERLOADS and BOOST_PYTHON_FUNCTION_OVERLOADS +can also be used for overloaded functions and member functions with a +common sequence of initial arguments. Here is an example:

+
+     void foo()
+     {
+        /*...*/
+     }
+
+     void foo(bool a)
+     {
+        /*...*/
+     }
+
+     void foo(bool a, int b)
+     {
+        /*...*/
+     }
+
+     void foo(bool a, int b, char c)
+     {
+        /*...*/
+     }
+
+

+Like in the previous section, we can generate thin wrappers for these +overloaded functions in one-shot:

+
+    BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads, foo, 0, 3)
+
+

+Then...

+
+    .def("foo", foo, foo_overloads());
+
+

+Notice though that we have a situation now where we have a minimum of zero +(0) arguments and a maximum of 3 arguments.

+

Manual Wrapping

+It is important to emphasize however that the overloaded functions must +have a common sequence of initial arguments. Otherwise, our scheme above +will not work. If this is not the case, we have to wrap our functions + +manually.

+

+Actually, we can mix and match manual wrapping of overloaded functions and +automatic wrapping through BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS and +its sister, BOOST_PYTHON_FUNCTION_OVERLOADS. Following up on our example +presented in the section +on overloading, since the +first overload has default arguments, we can use +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS to automatically wrap the first +three of the defs above and manually wrap just the last. Here's how +we'll do this:

+
+    BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(xf_overloads, f, 1, 4)
+
+

+Create a member function pointers as above for both X::f overloads:

+
+    bool    (X::*fx1)(int, double, char)    = &X::f;
+    int     (X::*fx2)(int, int, int)        = &X::f;
+
+

+Then...

+
+    .def("f", fx1, xf_overloads());
+    .def("f", fx2)
+
+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/call_policies.html b/doc/tutorial/doc/call_policies.html index 47871700..dc94ea5b 100644 --- a/doc/tutorial/doc/call_policies.html +++ b/doc/tutorial/doc/call_policies.html @@ -4,7 +4,7 @@ Call Policies - + @@ -21,7 +21,7 @@ - +

@@ -156,7 +156,7 @@ here.

- +
diff --git a/doc/tutorial/doc/class_operators_special_functions.html b/doc/tutorial/doc/class_operators_special_functions.html index e22bd2ff..ce7aab66 100644 --- a/doc/tutorial/doc/class_operators_special_functions.html +++ b/doc/tutorial/doc/class_operators_special_functions.html @@ -3,7 +3,7 @@ Class Operators/Special Functions - + @@ -20,7 +20,7 @@ - +
@@ -95,7 +95,7 @@ Well, the method str requires the operator<< to do its w - +
diff --git a/doc/tutorial/doc/class_virtual_functions.html b/doc/tutorial/doc/class_virtual_functions.html index 46cb7837..37ce60ce 100644 --- a/doc/tutorial/doc/class_virtual_functions.html +++ b/doc/tutorial/doc/class_virtual_functions.html @@ -4,7 +4,7 @@ Class Virtual Functions - + @@ -21,7 +21,7 @@ - +

@@ -112,118 +112,11 @@ In Python, let us try to instantiate our Base class:

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

-Now, at last, we can even derive from our base class Base 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. This corresponds to def("call_f", call_f);. Boost.Python dispatches this call.
  3. int call_f(Base& b) { return b.f(); } accepts the call.
  4. The overridden virtual function f of BaseWrap is called.
  5. call_method<int>(self, "f"); dispatches the call back to Python.
  6. def f(self): return 42 is finally called.

-Rewind back to our Base class, if its member function f was not -declared as pure virtual:

-
-    struct Base
-    {
-        virtual int f() { return 0; }
-    };
-
-

-And instead is implemented to return 0, as shown above.

-
-    struct BaseWrap : Base
-    {
-        BaseWrap(PyObject* self_)
-            : self(self_) {}
-        int f() { return call_method<int>(self, "f"); }
-        static int default_f(Base* b) { return b->Base::f(); } // <<=== added
-        PyObject* self;
-    };
-
-

-then, our Boost.Python wrapper:

-
-    class_<Base, BaseWrap>("Base")
-        .def("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
-
- +

diff --git a/doc/tutorial/doc/default_arguments.html b/doc/tutorial/doc/default_arguments.html index 8eaf8476..d0c1f426 100644 --- a/doc/tutorial/doc/default_arguments.html +++ b/doc/tutorial/doc/default_arguments.html @@ -3,8 +3,8 @@ Default Arguments - - + + @@ -20,8 +20,8 @@
- - + +

@@ -44,8 +44,10 @@ to retrieve the default arguments:

def("f", f); // defaults lost!

-Because of this, when wrapping C++ code in earlier versions of -Boost.Python, we had to resort to writing thin wrappers:

+Because of this, when wrapping C++ code, we had to resort to manual +wrapping as outlined in the +previous section, or +writing thin wrappers:

     // write "thin wrappers"
     int f1(int x) { f(x); }
@@ -142,8 +144,8 @@ Notice the use of init<...> and optional<...> to s
 
-    
-    
+    
+    

diff --git a/doc/tutorial/doc/deriving_a_python_class.html b/doc/tutorial/doc/deriving_a_python_class.html new file mode 100644 index 00000000..d5a8034e --- /dev/null +++ b/doc/tutorial/doc/deriving_a_python_class.html @@ -0,0 +1,82 @@ + + + +Deriving a Python Class + + + + + + + + + + +
+ + Deriving a Python Class +
+
+ + + + + + +
+

+Continuing, now, at last, we can even derive from our base class Base 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. This corresponds to def("call_f", call_f);. Boost.Python dispatches this call.
  3. int call_f(Base& b) { return b.f(); } accepts the call.
  4. The overridden virtual function f of BaseWrap is called.
  5. call_method<int>(self, "f"); dispatches the call back to Python.
  6. def f(self): return 42 is finally called.
+ + + + + +
+
+
+ + diff --git a/doc/tutorial/doc/object_interface.html b/doc/tutorial/doc/object_interface.html index 1fd3a04d..1988dcd7 100644 --- a/doc/tutorial/doc/object_interface.html +++ b/doc/tutorial/doc/object_interface.html @@ -3,7 +3,7 @@ Object Interface - + @@ -20,7 +20,7 @@ - +
@@ -40,7 +40,7 @@ should minimize the learning curve significantly.

- +
diff --git a/doc/tutorial/doc/overloading.html b/doc/tutorial/doc/overloading.html index da0ad8ac..06baaa3a 100644 --- a/doc/tutorial/doc/overloading.html +++ b/doc/tutorial/doc/overloading.html @@ -3,8 +3,8 @@ Overloading - - + + @@ -20,59 +20,16 @@
- - + +

-It was mentioned in passing in the previous section that -BOOST_PYTHON_FUNCTION_OVERLOADS and BOOST_PYTHON_FUNCTION_OVERLOADS -can also be used for overloaded functions and member functions with a -common sequence of initial arguments. Here is an example:

-
-     void foo()
-     {
-        /*...*/
-     }
-
-     void foo(bool a)
-     {
-        /*...*/
-     }
-
-     void foo(bool a, int b)
-     {
-        /*...*/
-     }
-
-     void foo(bool a, int b, char c)
-     {
-        /*...*/
-     }
-
-

-Like in the previous section, we can generate thin wrappers for these -overloaded functions in one-shot:

-
-    BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads, foo, 0, 3)
-
-

-Then...

-
-    .def("foo", foo, foo_overloads());
-
-

-Notice though that we have a situation now where we have a minimum of zero -(0) arguments and a maximum of 3 arguments.

-

Manual Wrapping

-It is important to emphasize however that the overloaded functions must -have a common sequence of initial arguments. Otherwise, our scheme above -will not work.

-

-The following illustrates an alternate scheme for manually wrapping an -overloaded member function instead of -BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS. Obviously, the same technique -can be applied to wrapping overloaded non- member functions.

+The following illustrates a scheme for manually wrapping an overloaded +member function or. Obviously, the same technique can be applied to +wrapping overloaded non- member functions. Take note that this scheme +applies to actual overloaded (member, non-member) functions as well as +(member, non-member) functions with default arguments.

We have here our C++ classes:

@@ -113,33 +70,11 @@ With these in hand, we can proceed to define and wrap this for Python:

.def("f", fx3) .def("f", fx4)
-

-Actually, we can mix and match manual wrapping of overloaded functions and -automatic wrapping through BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS and -its sister, BOOST_PYTHON_FUNCTION_OVERLOADS. Since the first overload -has default arguments, we can use BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS -to automatically wrap the first three of the defs above and manually -wrap just the last. Here's how we'll do this:

-
-    BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(xf_overloads, f, 1, 4)
-
-

-Create a member function pointers as above for both X::f overloads:

-
-    bool    (X::*fx1)(int, double, char)    = &X::f;
-    int     (X::*fx2)(int, int, int)        = &X::f;
-
-

-Then...

-
-    .def("f", fx1, xf_overloads());
-    .def("f", fx2)
-
- - + +

diff --git a/doc/tutorial/doc/quickstart.txt b/doc/tutorial/doc/quickstart.txt index 3d4eb7ec..a071949a 100644 --- a/doc/tutorial/doc/quickstart.txt +++ b/doc/tutorial/doc/quickstart.txt @@ -527,10 +527,10 @@ 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. -[h2 Deriving a Python class] +[page:1 Deriving a Python Class] -Now, at last, we can even derive from our base class Base in Python. Before -we can do that, we have to set up our class_ wrapper as: +Continuing, now, at last, we can even derive from our base class Base in +Python. Before we can do that, we have to set up our [^class_] wrapper as: class_("Base") ; @@ -572,30 +572,45 @@ Here's what's happening: # [^call_method(self, "f");] dispatches the call back to Python. # [^def f(self): return 42] is finally called. -Rewind back to our [^Base] class, if its member function [^f] was not -declared as pure virtual: +[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 is implemented to return [^0], as shown above. +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(self, "f"); } - static int default_f(Base* b) { return b->Base::f(); } // <<=== added + int default_f() { return Base::f(); } // <<=== ***ADDED*** PyObject* self; }; -then, our Boost.Python wrapper: +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") - .def("f", &BaseWrap::default_f) - ; + .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_] with @@ -874,6 +889,51 @@ these can be found [@../../v2/reference.html#models_of_call_policies here]. "Explicit is better than implicit"[br] "In the face of ambiguity, refuse the temptation to guess"[br]] +[page:1 Overloading] + +The following illustrates a scheme for manually wrapping an overloaded +member function or. Obviously, the same technique can be applied to +wrapping overloaded non- member functions. Take note that this scheme +applies to actual overloaded (member, non-member) functions as well as +(member, non-member) functions with default arguments. + +We have here our C++ classes: + + struct X + { + bool f(int a, double b = 0, char c = 'x') + { + return true; + } + + int f(int a, int b, int c) + { + return a + b + c; + }; + }; + +Notice that class X has two overloaded functions with different signatures. +The types of the arguments, and the return are totally different, unlike +above where we have a common sequence of initial arguments. + +We shall start by introducing some member function pointer variables: + + bool (X::*fx1)(int) = &X::f; + bool (X::*fx2)(int, double) = &X::f; + bool (X::*fx3)(int, double, char)= &X::f; + int (X::*fx4)(int, int, int) = &X::f; + +The first three member function pointers take care of the first X::f +overload. The one with default arguments. The last member function pointer +takes care of the second X::f overload. + +With these in hand, we can proceed to define and wrap this for Python: + + .def("f", fx1) + .def("f", fx2) + .def("f", fx3) + .def("f", fx4) + [page:1 Default Arguments] Boost.Python wraps (member) function pointers. Unfortunately, C++ function @@ -892,8 +952,9 @@ to retrieve the default arguments: def("f", f); // defaults lost! -Because of this, when wrapping C++ code in earlier versions of -Boost.Python, we had to resort to writing thin wrappers: +Because of this, when wrapping C++ code, we had to resort to manual +wrapping as outlined in the [@overloading.html previous section], or +writing thin wrappers: // write "thin wrappers" int f1(int x) { f(x); } @@ -987,7 +1048,7 @@ You can easily add this constructor to Boost.Python in one shot: Notice the use of [^init<...>] and [^optional<...>] to signify the default (optional arguments). -[page:1 Overloading] +[page:1 Auto-Overloading] It was mentioned in passing in the previous section that [^BOOST_PYTHON_FUNCTION_OVERLOADS] and [^BOOST_PYTHON_FUNCTION_OVERLOADS] @@ -1030,56 +1091,17 @@ Notice though that we have a situation now where we have a minimum of zero It is important to emphasize however that [*the overloaded functions must have a common sequence of initial arguments]. Otherwise, our scheme above -will not work. - -The following illustrates an alternate scheme for manually wrapping an -overloaded member function instead of -[^BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS]. Obviously, the same technique -can be applied to wrapping overloaded non- member functions. - -We have here our C++ classes: - - struct X - { - bool f(int a, double b = 0, char c = 'x') - { - return true; - } - - int f(int a, int b, int c) - { - return a + b + c; - }; - }; - -Notice that class X has two overloaded functions with different signatures. -The types of the arguments, and the return are totally different, unlike -above where we have a common sequence of initial arguments. - -We shall start by introducing some member function pointer variables: - - bool (X::*fx1)(int) = &X::f; - bool (X::*fx2)(int, double) = &X::f; - bool (X::*fx3)(int, double, char)= &X::f; - int (X::*fx4)(int, int, int) = &X::f; - -The first three member function pointers take care of the first X::f -overload. The one with default arguments. The last member function pointer -takes care of the second X::f overload. - -With these in hand, we can proceed to define and wrap this for Python: - - .def("f", fx1) - .def("f", fx2) - .def("f", fx3) - .def("f", fx4) +will not work. If this is not the case, we have to wrap our functions +[@overloading.html manually]. Actually, we can mix and match manual wrapping of overloaded functions and automatic wrapping through [^BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS] and -its sister, [^BOOST_PYTHON_FUNCTION_OVERLOADS]. Since the first overload -has default arguments, we can use [^BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS] -to automatically wrap the first three of the [^def]s above and manually -wrap just the last. Here's how we'll do this: +its sister, [^BOOST_PYTHON_FUNCTION_OVERLOADS]. Following up on our example +presented in the section [@overloading.html on overloading], since the +first overload has default arguments, we can use +[^BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS] to automatically wrap the first +three of the [^def]s above and manually wrap just the last. Here's how +we'll do this: BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(xf_overloads, f, 1, 4) diff --git a/doc/tutorial/doc/virtual_functions_with_default_implementations.html b/doc/tutorial/doc/virtual_functions_with_default_implementations.html new file mode 100644 index 00000000..f7b15115 --- /dev/null +++ b/doc/tutorial/doc/virtual_functions_with_default_implementations.html @@ -0,0 +1,122 @@ + + + +Virtual Functions with Default Implementations + + + + + + + + + + +
+ + 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:

+
+    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
+
+ + + + + + +
+
+
+ + diff --git a/doc/tutorial/index.html b/doc/tutorial/index.html index 108d25f6..9439b300 100644 --- a/doc/tutorial/index.html +++ b/doc/tutorial/index.html @@ -60,6 +60,16 @@ Class Virtual Functions + + + Deriving a Python Class + + + + + Virtual Functions with Default Implementations + + Class Operators/Special Functions @@ -75,6 +85,11 @@ Call Policies + + + Overloading + + Default Arguments @@ -82,7 +97,7 @@ - Overloading + Auto-Overloading