diff --git a/enums.html b/enums.html new file mode 100644 index 00000000..53c88d41 --- /dev/null +++ b/enums.html @@ -0,0 +1,68 @@ + + + Wrapping enums + +
+

+ c++boost.gif (8819 bytes)Wrapping enums +

+

Because there is in general no way to deduce that a value of arbitrary type T +is an enumeration constant, py_cpp cannot automatically convert enum values to +and from Python. To handle this case, you need to decide how you want the enum +to show up in Python (since Python doesn't have enums). If you are satisfied +with a Python int as a way to represent your enum values, you can write some +simple from_python() and to_python() functions in +namespace py:: + +

+#ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // workaround GCC 2.95.2 bug
+namespace py {
+#endif
+
+  MyEnumType from_python(PyObject* x,
+       py::Type<MyEnumType>)
+  {
+      return static_cast<MyEnum>(
+        from_python(x, py::Type<long>()));
+  }
+
+  PyObject* to_python(MyEnumType x)
+  {
+      return to_python(static_cast<long>(x));
+  }
+
+#ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
+}
+#endif
+
+
+ This technique defines the conversions of +MyEnumType in terms of the conversions for the built-in + long type. + +You may also want to add a bunch of lines like this to your module +initialization: + +
+mymodule.add(py::to_python(enum_value_1), "enum_value_1");
+mymodule.add(py::to_python(enum_value_2), "enum_value_2");
+...
+
+You can also add these to an extension class definition: +
+my_class.add(py::to_python(enum_value_1), "enum_value_1");
+my_class.add(py::to_python(enum_value_2), "enum_value_2");
+...
+
+

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +

+ Updated: Nov 5, 2000 +

+ diff --git a/inheritance.html b/inheritance.html index 138d58a9..e991065d 100644 --- a/inheritance.html +++ b/inheritance.html @@ -35,31 +35,32 @@ -

Reflecting C++ Inheritance Relationships

+

Reflecting C++ Inheritance Relationships

Py_cpp also allows us to represent C++ inheritance relationships so that wrapped derived classes may be passed where values, pointers, or - references to a base class are expected as arguments: + references to a base class are expected as arguments. The + declare_base member function of + ClassWrapper<> is used to establish the relationship + between base and derived classes:

-#include  // for std::auto_ptr<>
+#include <memory> // for std::auto_ptr<>
 
-struct Base
-{
+struct Base {
     virtual ~Base() {}
     virtual const char* name() const { return "Base"; }
 };
 
-struct Derived
-{
+struct Derived : Base {
     Derived() : x(-1) {}
     virtual const char* name() const { return "Derived"; }
     int x;
 };
 
-std::auto_ptr base_factory() {
-    return std::auto_ptr(new Derived);
+std::auto_ptr<Base> derived_as_base() {
+    return std::auto_ptr<Base>(new Derived);
 }
 
 const char* get_name(const Base& b) {
@@ -86,10 +87,10 @@ void initmy_module()
 
        py::ClassWrapper<Derived> derived_class(my_module, "Derived");
        derived_class.def(py::Constructor<void>());
-       // This establishes the inheritance relationship between Base and Derived
-       derived_class.declare_base(base_class);
+       // Establish the inheritance relationship between Base and Derived
+       derived_class.declare_base(base_class);
 
-       my_module.def(base_factory, "base_factory");
+       my_module.def(derived_as_base, "derived_as_base");
        my_module.def(get_name, "get_name");
        my_module.def(get_derived_x, "get_derived_x");
     }
@@ -110,12 +111,12 @@ void initmy_module()
 >>> derived = Derived()
 >>> get_name(base)
 'Base'
->>> # objects of wrapped class Derived may be passed where Base is expected
+
objects of wrapped class Derived may be passed where Base is expected
 >>> get_name(derived) 
 'Derived'
->>> # objects of wrapped class Derived can be passed where Derived is 
->>> # expected but where type information has been lost.
->>> get_derived_x(base_factory()) 
+
objects of wrapped class Derived can be passed where Derived is +expected but where type information has been lost.
+>>> get_derived_x(derived_as_base()) 
 -1
 
diff --git a/overriding.html b/overriding.html index ecf7efb7..b013a055 100644 --- a/overriding.html +++ b/overriding.html @@ -1,96 +1,105 @@ - - Overridable Virtual Functions - -

- c++boost.gif (8819 bytes) -

-

- Overridable Virtual Functions -

-

- In the previous example we exposed a simple - C++ class in Python and showed that we could write a subclass. We even - redefined one of the functions in our derived class. Now we will learn - how to make the function behave virtually. Of course, the first - step if we want it to act like a virtual function when called from our - C++ code, is to make it virtual: -

-
-  class world
-  {
-    ...
-      virtual const char* get() const { return "hi, world"; }
-    ...
-  };
-
-
-

- Then we'll need a derived class* to help us - dispatch the call to Python: -

-
-struct world_callback : hello::world
+
+    Overridable Virtual Functions
+
+    c++boost.gif (8819 bytes) 
+
+    

Overridable Virtual Functions

+ +

+ In the previous example we exposed a simple + C++ class in Python and showed that we could write a subclass. We even + redefined one of the functions in our derived class. Now we will learn + how to make the function behave virtually. + + +

Example

+ +

In this example, it is assumed that world::get() is a virtual +member function: + +

+class world
 {
-    // The first argument must by a PyObject* (the corresponding Python object)
-    // The rest of the argument list should match the base class constructor
-    world_callback(PyObject* self, int x)
-        : world(x),            // dispatch to base object
-          m_self(self) {}      // hang onto the Python object
-
-    // Dispatch the call to Python
-    const char* get() const
-    {
-        // Any function arguments would go at the end of the argument list
-        // The return type is a template parameter
-        return py::Callback<const char*>::call_method(m_self, "get");
-    }
-
-    // Something Python can call in case there is no override of get()
-    const char* default_get(hello::world* self) const
-        { return self->hello::world::get(); }
- private:
-    PyObject* m_self; // A way to hold onto the Python object
+ public:
+    world(int);
+    virtual ~world();
+    virtual const char* get() const { return "hi, world"; }
 };
-
-
-

- Finally, we add world_callback to the - ClassWrapper<> declaration in our module initialization - function: -

-
+
+ +

+ We'll need a derived class* to help us + dispatch the call to Python. In our derived class, we need the following + elements: + +

    + +
  1. A PyObject* data member that holds a reference to the + corresponding Python object. + +
  2. A constructor for each exposed constructor of the base class which stores an + additional initial PyObject* argument in the data + member described above. + +
  3. An implementation of each virtual function you may wish to override in + Python which uses + py::Callback<return-type>::call_method() to call the + Python override. + +
  4. For each non-pure virtual function meant to be overridable from Python, a + static member function (or a free function) taking a reference or pointer to the + base type as the first parameter and which forwards any additional parameters + neccessary to the default implementation of the virtual function. See also + this note if the base class virtual function is private. + +
+ +
+struct world_callback : world
+{
+    world_callback(PyObject* self, int x) // 2
+        : world(x),
+          m_self(self) {}
+
+    const char* get() const // 3
+        { return py::Callback<const char*>::call_method(m_self, "get"); }
+
+    static const char* default_get(const hello::world& self) const // 4
+        { return self.world::get(); }
+ private:
+    PyObject* m_self; // 1
+};
+
+ +

+ Finally, we add world_callback to the + ClassWrapper<> declaration in our module initialization + function, and when we define the function, we must tell py_cpp about the default + implementation: + +

 // Create the Python type object for our extension class
 py::ClassWrapper<hello::world,world_callback> world_class(hello, "world");
-       ...
-
-
-

- ...and when we define the function, we must tell py_cpp about the default - implementation: -

-
 // Add a virtual member function
-world_class.def(&hello::world::get, "get", &world_callback::default_get);
-
-
-

- Now our subclass of hello.world behaves as expected: -

-

-
+world_class.def(&world::get, "get", &world_callback::default_get);
+
+ +

+ Now our subclass of hello.world behaves as expected: + +

 >>> class my_subclass(hello.world):
 ...     def get(self):
 ...         return 'hello, world'
 ...
 >>> hello.length(my_subclass())
 12
-
-
+
+

*You may ask, "Why do we need this derived class? This could have been designed so that everything gets done right @@ -99,9 +108,9 @@ world_class.def(&hello::world::get, "get", &world_callback::default_get) 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. -

- Pure Virtual Functions -

+ +

Pure Virtual Functions

+

A pure virtual function with no implementation is actually a lot easier to deal with than a virtual function with a default implementation. First @@ -114,14 +123,12 @@ world_class.def(&hello::world::get, "get", &world_callback::default_get) function, indicating that it should have been implemented. For example:

-struct baz
-{
-    virtual void pure(int) = 0;
+struct baz {
+    virtual int pure(int) = 0;
 };
 
-struct baz_callback
-{
-    void pure(int x) { py::Callback<void>::call_method(m_self, "pure", x); }
+struct baz_callback {
+    int pure(int x) { py::Callback<int>::call_method(m_self, "pure", x); }
 };
 
 extern "C"
@@ -149,18 +156,29 @@ initfoobar()
 
 >>> from foobar import baz
 >>> x = baz()
->>> x.pure()
+>>> x.pure(1)
 Traceback (innermost last):
   File "<stdin>", line 1, in ?
 AttributeError: pure
 >>> class mumble(baz):
-...    def pure(self, z): pass
+...    def pure(self, x): return x + 1
 ...
 >>> y = mumble()
->>> y.pure()
->>> 
-
-
+>>> y.pure(99) +100 + + +

Private Non-Pure Virtual Functions

+ +

This is one area where some minor intrusiveness on the wrapped library is +required. Once it has been overridden, the only way to call the base class +implementation of a private virtual function is to make the derived class a +friend of the base class. You didn't hear it from me, but most C++ +implementations will allow you to change the declaration of the base class in +this limited way without breaking binary compatibility (though it will certainly +break the ODR). +

Prev: A Simple Example Using py_cpp Next: Function Overloading Up: + + Pointers + +

+

+ c++boost.gif (8819 bytes)Pointers +

+

The Problem With Pointers

+

+In general, raw pointers passed to or returned from functions are problematic +for py_cpp because pointers have too many potential meanings. Is it an iterator? +A pointer to a single element? An array? When used as a return value, is the +caller expected to manage (delete) the pointed-to object or is the pointer +really just a reference? If the latter, what happens to Python references to the +referent when some C++ code deletes it? +

+There are a few cases in which pointers are converted automatically: +

+ +

Can you avoid the problem?

+ +

My first piece of advice to anyone with a case not covered above is +"find a way to avoid the problem." For example, if you have just one +or two functions returning a pointer to a single (not an array of) const +T* for some wrapped T, you may be able to write a "thin +converting wrapper" over those two functions as follows (Since py_cpp +converts const T& values to_python by copying the T +into a new extension instance, Foo must have a public copy constructor): + +

+const Foo* f(); // original function
+const Foo& f_wrapper() { return *f(); }
+  ...
+my_module.def(f_wrapper, "f");
+
+ +

Dealing with the problem

+ +

The first step in handling the remaining cases is to figure out what the pointer +means. Several potential solutions are provided in the examples that follow: + +

Returning a pointer to a wrapped type

+ +

Returning a const pointer

+ +

If you have lots of functions returning a const T* for some +wrapped T, you may want to provide an automatic +to_python conversion function so you don't have to write lots of +thin wrappers. You can do this simply as follows: + +

+#ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // work around gcc 2.95.2 bug
+namespace py {
+#endif
+  PyObject* to_python(const Foo* p) {
+     return to_python(*p); // convert const Foo* in terms of const Foo&
+  }
+#ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
+}
+#endif
+
+ +

If you can't (afford to) copy the referent, or the pointer is non-const

+ +

If the wrapped type doesn't have a public copy constructor, if copying is +extremely costly (remember, we're dealing with Python here), or if the +pointer is non-const and you really need to be able to modify the referent from +Python, you can use the following dangerous trick. Why dangerous? Because python +can not control the lifetime of the referent, so it may be destroyed by your C++ +code before the last Python reference to it disappears: + +

+#ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // work around gcc 2.95.2 bug
+namespace py {
+#endif
+  PyObject* to_python(Foo* p)
+  {
+      return py::PyExtensionClassConverters::ptr_to_python(p);
+  }
+
+  PyObject* to_python(const Foo* p)
+  {
+      return to_python(const_cast(p));
+  }
+#ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
+}
+#endif
+
+ +This will cause the Foo* to be treated as though it were an owning smart +pointer, even though it's not. Be sure you don't use the reference for anything +from Python once the pointer becomes invalid, though. Don't worry too much about +the const_cast<> above: Const-correctness is completely lost +to Python anyway! + +

[In/]Out Parameters and Immutable Types

+ +

If you have an interface that uses non-const pointers (or references) as +in/out parameters to types which in Python are immutable (e.g. int, string), +there simply is no way to get the same interface in Python. You must +resort to transforming your interface with simple thin wrappers as shown below: +

+const void f(int* in_out_x); // original function
+const int f_wrapper(int in_x) { f(in_x); return in_x; }
+  ...
+my_module.def(f_wrapper, "f");
+
+ +

Of course, [in/]out parameters commonly occur only when there is already a +return value. You can handle this case by returning a Python tuple: +

+typedef unsigned ErrorCode;
+const char* f(int* in_out_x); // original function
+ ...
+#include <py_cpp/objects.h>
+const py::Tuple f_wrapper(int in_x) { 
+  const char* s = f(in_x); 
+  return py::Tuple(s, in_x);
+}
+  ...
+my_module.def(f_wrapper, "f");
+
+

Now, in Python: +

+>>> str,out_x = f(3)
+
+ + + +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +

+ Updated: Nov 6, 2000 +

+ diff --git a/py_cpp.html b/py_cpp.html index d87d2067..c1381eae 100644 --- a/py_cpp.html +++ b/py_cpp.html @@ -8,77 +8,144 @@ c++boost.gif (8819 bytes) py_cpp* +

- Py_cpp is a system for quickly and easily interfacing C++ code with here. + +

Synopsis

+

+ py_cpp is a system for quickly and easily interfacing C++ code with Python such that the Python interface is very similar to the C++ interface. It is designed to be minimally intrusive on your C++ design. In most cases, you should not have to alter your C++ classes in any way in order to use them with py_cpp. The system should simply "reflect" your C++ classes and functions into Python. + +

Supported Platforms

+

py_cpp has been tested in the following configurations: + +

+ +

Py_cpp requires the boost libraries, and is + has been accepted for inclusion into the boost libraries pending "boostification" + (completion of the documentation, change in some naming conventions and + resolution of some namespace issues). + +

Credits

- The source code for py_cpp, including a MSVC demo project is available here. It has been tested against Python - 1.5.2 with GCC 2.95.2 and Microsoft Visual C++ 6 sp4 using both the STLport standard library implementation - and the library implementation which ships with the compiler. It - has also been tested against Python 2.0c1 with MSVC++ 6sp4 by Alex - Martelli. It will work with the next release of Metrowerks CodeWarrior - Pro6 (the first release has a bug that's fatal to py_cpp_). Py_cpp - requires the Boost libraries, and is - currently under formal review on the boost mailing list for - acceptance into boost. -

- py_cpp was originally written by David Abrahams. Ullrich Koethe supplied - an early version of the overloading support and wrote the support for - implicit conversions of arguments that have a C++ inheritance - relationship. Alex Martelli supplied the first tests against Python 2.0. - The members of the boost mailing list and the Python community supplied - invaluable early feedback. The development of py_cpp wouldn't have been - possible without the generous support of Dragon Systems/Lernout and - Hauspie, Inc. -

- Table of Contents -

+ + +

Table of Contents

+
    -
  1. - A Brief Introduction to writing Python +
  2. A Brief Introduction to writing Python extension modules -
  3. - Comparisons between py_cpp and other systems - for extending Python -
  4. - A Simple Example Using py_cpp -
  5. - Overridable Virtual Functions -
  6. - Function Overloading -
  7. - Inheritance -
  8. - Special Method Name Support -
  9. - A Peek Under the Hood -
  10. - Building a Module with Py_cpp -
+ +
  • Comparisons between py_cpp and other + systems for extending Python + +
  • A Simple Example Using py_cpp + +
  • Overridable Virtual Functions + +
  • Function Overloading + +
  • Inheritance + +
  • Special Method Name Support + +
  • A Peek Under the Hood + +
  • Building a Module with Py_cpp + +
  • Advanced Topics + +
      +
    1. ClassWrapper<> + +
    2. enums + +
    3. References + +
    4. Pointers and Smart Pointers + +
    5. Built-in Python Types + +
    6. Other Extension Types + +
    7. Templates +
    + + +

    - More sophisticated examples, including examples which demonstrate that - these ExtensionClasses support some of Python's "special" member - functions (e.g. __getattr__(self, name)), are given in + More sophisticated examples are given in extclass_demo.cpp, extclass_demo.h, and test_extclass.py in the source code archive. There's much more here, and much more documentation to come...

    Questions should be directed to the boost mailing list or to David Abrahams, the primary - author and maintainer. -

    - Naming Contest -

    + "http://www.egroups.com/list/boost">the boost mailing list. + +

    Naming Contest

    +

    Yes, I know py_cpp is a lousy name. Problem is, the best names my puny imagination can muster (IDLE and GRAIL) are taken, so I'm holding a diff --git a/release_notes.txt b/release_notes.txt index eadebf26..90003c55 100644 --- a/release_notes.txt +++ b/release_notes.txt @@ -1,3 +1,19 @@ +======= Release ======= +2000-11-06 0:22 + Lots of documentation updates + + added 4-argument template constructor to py::Tuple + + added "add" member function to ClassWrapper<> to allow arbitrary Python + objects to be added to an extension class. + + gen_all.py now generates support for n argument member functions and n+1 + argument member functions at the suggestion of "Ralf W. Grosse-Kunstleve" + + + Added regression tests and re-ordered declare_base calls to verify that the + phantom base class issue is resolved. + 2000-11-04 17:35 Integrated Ullrich Koethe's brilliant from_python_experiment for better @@ -49,6 +65,7 @@ Remove conflicting /Ot option from vc6_prj release build +======= Release ======= 2000-11-02 17:42 Added a fix for interactions between default virtual function