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
+ 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:
+
++You can also add these to an extension class definition: ++mymodule.add(py::to_python(enum_value_1), "enum_value_1"); +mymodule.add(py::to_python(enum_value_2), "enum_value_2"); +... +
++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 +
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:
diff --git a/overriding.html b/overriding.html index ecf7efb7..b013a055 100644 --- a/overriding.html +++ b/overriding.html @@ -1,96 +1,105 @@ --#includeobjects of wrapped class Derived may be passed where Base is expected// 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 + >>> 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
- - 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 + ++ +
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_callbackto 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: + +
+ +
+ +- A
PyObject*data member that holds a reference to the + corresponding Python object. + +- A constructor for each exposed constructor of the base class which stores an + additional initial
PyObject*argument in the data + member described above. + +- 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. + +- 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_callbackto 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.worldbehaves as expected: --
-+ ++world_class.def(&world::get, "get", &world_callback::default_get); ++ Now our subclass of
hello.worldbehaves 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. -
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
+
+
+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:
+
+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:
+ 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) 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:
+
+ If you have lots of functions returning a 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:
+
+ 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:
+ 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:
+ Now, in Python:
+
+ © 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
+
+
+
Pointers
+ The Problem With Pointers
+
+
+
+
+const char* are interpreted as
+null-terminated 'C' strings and when passed to or returned from C++ functions are
+converted from/to Python strings.
+
+Can you avoid the problem?
+
+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
+
+Returning a pointer to a wrapped type
+
+Returning a const pointer
+
+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
+
+
+
+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
+#ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // work around gcc 2.95.2 bug
+namespace py {
+#endif
+ PyObject* to_python(Foo* p)
+ {
+ return py::PyExtensionClassConvertersconst_cast<> above: Const-correctness is completely lost
+to Python anyway!
+
+[In/]Out Parameters and Immutable Types
+
+
+
+
+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");
+
+
+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");
+
+
+
+
+
+>>> str,out_x = f(3)
+
py_cpp*
+
- Py_cpp is a system for quickly and easily interfacing C++ code with here. + +
+ 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. + +
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). + +
- 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. -
- 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. -
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"
+