2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-24 18:12:43 +00:00

This commit was manufactured by cvs2svn to create branch

'coercion_experiments'.

[SVN r8021]
This commit is contained in:
nobody
2000-10-20 02:25:42 +00:00
48 changed files with 3429 additions and 507 deletions

View File

@@ -59,4 +59,4 @@ inline BaseObject<PythonType>::~BaseObject()
}
#endif BASE_OBJECT_DWA051600_H_
#endif // BASE_OBJECT_DWA051600_H_

View File

@@ -20,9 +20,6 @@ class ClassWrapper
public:
ClassWrapper(Module& module, const char* name)
: m_class(new ExtensionClass<T, U>(name))
#if 0 // def PY_MSVC6_OR_EARLIER
, m_msvc_hack(name)
#endif
{
module.add(Ptr(as_object(m_class.get()), Ptr::new_ref), name);
}
@@ -68,11 +65,47 @@ class ClassWrapper
template <class MemberType>
void def_read_write(MemberType T::*pm, const char* name)
{ m_class->def_read_write(pm, name); }
// declare the given class a base class of this one and register
// conversion functions
template <class S, class V>
void declare_base(ClassWrapper<S, V> const & base)
{
m_class->declare_base(base.get_extension_class());
}
// declare the given class a base class of this one and register
// upcast conversion function
template <class S, class V>
void declare_base(ClassWrapper<S, V> const & base, WithoutDowncast)
{
m_class->declare_base(base.get_extension_class(), without_downcast);
}
// declare the given class a base class of this one and register
// conversion functions
template <class S, class V>
void declare_base(ExtensionClass<S, V> * base)
{
m_class->declare_base(base);
}
// declare the given class a base class of this one and register
// upcast conversion function
template <class S, class V>
void declare_base(ExtensionClass<S, V> * base, WithoutDowncast)
{
m_class->declare_base(base, without_downcast);
}
// get the embedded ExtensioClass object
ExtensionClass<T, U> * get_extension_class() const
{
return m_class.get();
}
private:
PyPtr<ExtensionClass<T, U> > m_class;
#if 0 // def PY_MSVC6_OR_EARLIER
PyExtensionClassConverters<T, U> m_msvc_hack;
#endif
};
// The bug mentioned at the top of this file is that on certain compilers static

176
comparisons.html Normal file
View File

@@ -0,0 +1,176 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
"http://www.w3.org/TR/REC-html40/strict.dtd">
<title>
Comparisons with Other Systems
</title>
<div>
<h1>
<img width="277" height="86" id="_x0000_i1025" align="center"
src="c++boost.gif" alt= "c++boost.gif (8819 bytes)">Comparisons with
Other Systems
</h1>
<h2>CXX</h2>
<p>
Like py_cpp, <a href="http://cxx.sourceforge.net/">CXX</a> attempts to
provide a C++-oriented interface to Python. In most cases, like py_cpp,
it relieves the user from worrying about reference-counts. As far as I
can tell, there is no support for subclassing C++ extension types in
Python. An even more-significant difference is that a user's C++ code is
still basically "dealing with Python objects", though they are wrapped
in C++ classes. This means such jobs as argument parsing and conversion
are still left to be done explicitly by the user. This is not entirely a
bad thing, as you can do some Pythonic things with CXX (e.g. variable
and keyword arguments) that I haven't yet figured out how to enable with
py_cpp. As far as I can tell, also CXX enables one to write what is
essentially idiomatic Python code in C++, manipulating Python objects
through the same fully-generic interfaces we use in Python. That use is
also supported (less-well) by the py_cpp object wrappers. <a
href="mailto:dubois1@llnl.gov">Paul F. Dubois</a>, the CXX maintainer,
has told me that what I've described is only half of the picture with
CXX, but I never understood his explanation well-enough to fill in the
other half. I hope that you, dear reader, may be able to help me
complete my comparitive analysis of CXX.
<h2>SWIG</h2>
<p>
<a href= "http://www.swig.org/">SWIG</a> is an impressively mature tool
for exporting an existing ANSI 'C' interface into various scripting
languages. Swig relies on a parser to read your source code and produce
additional source code files which can be compiled into a Python (or
Perl or Tcl) extension module. It has been successfully used to create
many Python extension modules. Like py_cpp, SWIG is trying to allow an
existing interface to be wrapped with little or no change to the
existing code. The documentation says "SWIG parses a form of ANSI C
syntax that has been extended with a number of special directives. As a
result, interfaces are usually built by grabbing a header file and
tweaking it a little bit." For C++ interfaces, the tweaking has often
proven to amount to more than just a little bit. One user
writes:<blockquote> "The problem with swig (when I used it) is that it
couldnt handle templates, didnt do func overloading properly etc. For
ANSI C libraries this was fine. But for usual C++ code this was a
problem. Simple things work. But for anything very complicated (or
realistic), one had to write code by hand. I believe py_cpp doesnt have
this problem[sic]... IMHO overloaded functions are very important to
wrap correctly."<br><i>-Prabhu Ramachandran</i>
</blockquote>
<p>
By contrast, py_cpp doesn't attempt to parse C++ - the problem is simply
too complex to do correctly. Technically, one does write code by hand to
use py_cpp. The goal, however, has been to make that code nearly as
simple as listing the names of the classes and member functions you want
to expose in Python.
<h2>SIP</h2>
<p>
<a
href="http://www.thekompany.com/projects/pykde/background.php3?dhtml_ok=1">SIP</a>
is a system similar to SWIG, though seemingly more
C++-oriented. The author says that like py_cpp, SIP supports overriding
extension class member functions in Python subclasses. It appears to
have been designed specifically to directly support some features of
PyQt/PyKDE, which is its primary client. Documentation is almost
entirely missing at the time of this writing, so a detailed comparison
is difficult.
<h2>ILU</h2>
<p>
<a
href="http://www.cl.cam.ac.uk/Research/Rainbow/projects/origami/ilu-1.8-manual">ILU</a>
is a very ambitious project which tries to describe a module's interface
(types and functions) in terms of an <a
href="http://www.cl.cam.ac.uk/Research/Rainbow/projects/origami/ilu-1.8-manual/manual_2.html">Interface
Specification Language</a> (ISL) so that it can be uniformly interfaced
to a wide range of computer languages, including Common Lisp, C++, C,
Modula-3, and Python. ILU can parse the ISL to generate a C++ language
header file describing the interface, of which the user is expected to
provide an implementation. Unlike py_cpp, this means that the system
imposes implementation details on your C++ code at the deepest level. It
is worth noting that some of the C++ names generated by ILU are supposed
to be reserved to the C++ implementation. It is unclear from the
documentation whether ILU supports overriding C++ virtual functions in Python.
<h2>Zope ExtensionClasses</h2>
<p>
<a href="http:http://www.digicool.com/releases/ExtensionClass">
ExtensionClasses in Zope</a> use the same underlying mechanism as py_cpp
to support subclassing of extension types in Python, including
multiple-inheritance. Both systems rely on the same "<a
href="http://www.python.org/workshops/1994-11/BuiltInClasses/Welcome.html">Don
Beaudry Hack</a>" that also inspired Don's MESS System.
<p>
The major differences are:
<ul>
<li>
py_cpp lifts the burden on the user to parse and convert function
argument types. Zope provides no such facility.
<li>
py_cpp lifts the burden on the user to maintain Python
reference-counts.
<li>
py_cpp supports function overloading; Zope does not.
<li>
py_cpp supplies a simple mechanism for exposing read-only and
read/write access to data members of the wrapped C++ type as Python
attributes.
<li>
Writing a Zope ExtensionClass is significantly more complex than
exposing a C++ class to python using py_cpp (mostly a summary of the
previous 4 items). <a href=
"http://www.digicool.com/releases/ExtensionClass/MultiMapping.html">A
Zope Example</a> illustrates the differences.
<li>
Zope's ExtensionClasses are specifically motivated by "the need for a
C-based persistence mechanism". Py_cpp's are motivated by the desire
to simply reflect a C++ API into Python with as little modification as
possible.
<li>
Thus, Zope's ExtensionClasses support pickling. Currently py_cpp
ExtensionClasses do not.
<li>
The following Zope restriction does not apply to py_cpp: "At most one
base extension direct or indirect super class may define C data
members. If an extension subclass inherits from multiple base
extension classes, then all but one must be mix-in classes that
provide extension methods but no data."
<li>
Zope's Extension SubClasses admit multiple-inheritance from both Zope
ExtensionClasses <i>and</i> Python classes. This capability is currently
not available in py_cpp.
<li>
Zope supplies the standard special Python class attributes __doc__,
__name__, __bases__, __dict__, and __module__ on its
ExtensionClasses; py_cpp does not (coming soon)
<li>
Zope supplies some creative but esoteric idioms such as <a href=
"http://www.digicool.com/releases/ExtensionClass/Acquisition.html">
Acquisition</a>.
<li>
Zope's ComputedAttribute support is designed to be used from Python.
<a href="special.html#getter_setter">The analogous feature of
py_cpp</a> can be used from C++ or Python. The feature is arguably
easier to use in py_cpp.
</ul>
<p>
Also, the Zope docs say: "The first superclass listed in the class
statement defining an extension subclass must be either a base
extension class or an extension subclass. This restriction will be
removed in Python-1.5." I believe that this promise was made
prematurely. I have looked at the Python 1.5.2 source code and I don't
believe it is possible to deliver on it.
<p>
Previous: <a href="extending.html">A Brief Introduction to writing Python Extension Modules</a>
Next: <a href="example1.html">A Simple Example Using py_cpp</a>
Up: <a href="py_cpp.html">Top</a>
<p>
&copy; 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.
<p>
Updated: Oct 18, 2000
</div>

View File

@@ -1,4 +0,0 @@
TargetName=demo
TargetType=dll
SourceFiles=extclass_demo.cpp
Libs=py_cpp python utils

View File

@@ -1,4 +0,0 @@
TargetName=demo_d
TargetType=dll
SourceFiles=extclass_demo_d.cpp
Libs=py_cpp_d python_d utils

View File

@@ -1,4 +0,0 @@
TargetName=example1
TargetType=dll
SourceFiles=example1.cpp
Libs=py_cpp python utils

127
example1.html Normal file
View File

@@ -0,0 +1,127 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
"http://www.w3.org/TR/REC-html40/strict.dtd">
<title>
A Simple Example Using py_cpp
</title>
<div>
<h1>
<img width="277" height="86" id="_x0000_i1025" src="c++boost.gif" alt=
"c++boost.gif (8819 bytes)">
</h1>
<h1>
A Simple Example Using py_cpp
</h1>
<p>
Suppose we have the following C++ API which we want to expose in
Python:
<blockquote>
<pre>
namespace hello {
  class world
  {
   public:
      world(int);
      ~world();
      const char* get() const { return "hi, world"; }
    ...
  };
  void length(const world&amp; x) { return std::strlen(x.get()); }
}
</pre>
</blockquote>
<p>
Here is the C++ code for a python module called <code>hello</code>
which exposes the API using py_cpp:
<blockquote>
<pre>
#include &lt;py_cpp/class_wrapper.h&gt;
// Python requires an exported function called init&lt;module-name&gt; in every
// extension module. This is where we build the module contents.
extern "C"
#ifdef _WIN32
__declspec(dllexport)
#endif
void inithello()
{
    try
    {
       // create an object representing this extension module
       py::Module hello("hello");
       // Create the Python type object for our extension class
       py::ClassWrapper&lt;hello::world&gt; world_class(hello, "world");
       // Add the __init__ function
       world_class.def(py::Constructor&lt;int&gt;());
       // Add a regular member function
       world_class.def(&amp;hello::world::get, "get");
       // Add a regular function to the module
       hello.def(hello::length, "length");
    }
    catch(...)
    {
       py::handle_exception();    // Deal with the exception for Python
    }
}
// Win32 DLL boilerplate
#if defined(_WIN32)
#include &lt;windows.h&gt;
extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID)
{
    return 1;
}
#endif // _WIN32
</pre>
</blockquote>
<p>
That's it! If we build this shared library and put it on our <code>
PYTHONPATH</code> we can now access our C++ class and function from
Python.
<blockquote>
<pre>
&gt;&gt;&gt; import hello
&gt;&gt;&gt; hi_world = hello.world(3)
&gt;&gt;&gt; hi_world.get()
'hi, world'
&gt;&gt;&gt; hello.length(hi_world)
9
</pre>
</blockquote>
<p>
We can even make a subclass of <code>hello.world</code>:
<blockquote>
<pre>
&gt;&gt;&gt; class my_subclass(hello.world):
...     def get(self):
...         return 'hello, world'
...
&gt;&gt;&gt; y = my_subclass(4)
&gt;&gt;&gt; y.get()
'hello, world'
</pre>
</blockquote>
<p>
Pretty cool! You can't do that with an ordinary Python extension type!
<blockquote>
<pre>
&gt;&gt;&gt; hello.length(y)
9
</pre>
</blockquote>
<p>
Of course, you may now have a slightly empty feeling in the pit of
your little pythonic stomach. Perhaps you feel your subclass deserves
to have a <code>length()</code> of <code>12</code>? If so, <a href=
"overriding.html">read on</a>...
<p>
Previous: <a href="comparisons.html">Comparisons with other systems</a> Next: <a href="overriding.html">Overridable virtual functions</a> Up:
<a href="py_cpp.html">Top</a>
<p>
&copy; 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.
<p>
Updated: Oct 15, 2000
</div>

View File

@@ -157,7 +157,7 @@ void report_missing_instance_data(
}
else
{
two_string_error(PyExc_TypeError, "extension class '%.*s' is not derived from '%.*s'.",
two_string_error(PyExc_TypeError, "extension class '%.*s' is not convertible into '%.*s'.",
instance->ob_type->tp_name, target_class->tp_name);
}
}
@@ -218,6 +218,66 @@ ExtensionClassBase::ExtensionClassBase(const char* name)
{
}
// This function is used in from_python() to convert wrapped classes that are
// related by inheritance. The problem is this: although C++ provides all necessary
// conversion operators, source and target of a conversion must be known at compile
// time. However, in Python we want to convert classes at runtime. The solution is to
// generate conversion functions at compile time, register them within the appropriate
// class objects and call them when a particular runtime conversion is required.
// If functions for any possible conversion have to be stored, their number will grow
// qudratically. To reduce this number, we actually store only conversion functions
// between adjacent levels in the inheritance tree. By traversing the tree recursively,
// we can build any allowed conversion as a concatenation of simple conversions. This
// traversal is done in the functions try_base_class_conversions() and
// try_derived_class_conversions(). If a particular conversion is impossible, all
// conversion functions will return a NULL pointer.
// The function extract_object_from_holder() attempts to actually extract the pointer
// to the contained object from an InstanceHolderBase (a wrapper class). A conversion
// of the held object to 'T *' is allowed when the conversion
// 'dynamic_cast<InstanceHolder<T> *>(an_instance_holder_base)' succeeds.
void* ExtensionClassBase::try_class_conversions(InstanceHolderBase* object) const
{
void* result = try_derived_class_conversions(object);
if(result)
return result;
return try_base_class_conversions(object);
}
void* ExtensionClassBase::try_base_class_conversions(InstanceHolderBase* object) const
{
for (std::size_t i = 0; i < base_classes().size(); ++i)
{
if(base_classes()[i].convert == 0)
continue;
void* result1 = base_classes()[i].class_object->extract_object_from_holder(object);
if (result1)
return (*base_classes()[i].convert)(result1);
void* result2 = base_classes()[i].class_object->try_base_class_conversions(object);
if (result2)
return (*base_classes()[i].convert)(result2);
}
return 0;
}
void* ExtensionClassBase::try_derived_class_conversions(InstanceHolderBase* object) const
{
for (std::size_t i = 0; i < derived_classes().size(); ++i)
{
void* result1 = derived_classes()[i].class_object->extract_object_from_holder(object);
if (result1)
return (*derived_classes()[i].convert)(result1);
void* result2 = derived_classes()[i].class_object->try_derived_class_conversions(object);
if (result2)
return (*derived_classes()[i].convert)(result2);
}
return 0;
}
void ExtensionClassBase::add_method(Function* method, const char* name)
{
add_method(PyPtr<Function>(method), name);

View File

@@ -5,6 +5,9 @@
//
// The author gratefully acknowleges the support of Dragon Systems, Inc., in
// producing this work.
//
// This file automatically generated for 5-argument constructors by
// gen_extclass.py
#ifndef EXTENSION_CLASS_DWA052000_H_
# define EXTENSION_CLASS_DWA052000_H_
@@ -24,6 +27,7 @@ namespace py {
// forward declarations
class ExtensionInstance;
class ExtensionClassBase;
template <class T> class InstanceHolder;
template <class T, class U> class InstanceValueHolder;
template <class Ptr, class T> class InstancePtrHolder;
@@ -50,10 +54,39 @@ T* check_non_null(T* p)
template <class T> class HeldInstance;
namespace detail {
typedef void* (*ConversionFunction)(void*);
struct BaseClassInfo
{
BaseClassInfo(ExtensionClassBase* t, ConversionFunction f)
:class_object(t), convert(f)
{}
ExtensionClassBase* class_object;
ConversionFunction convert;
};
typedef BaseClassInfo DerivedClassInfo;
}
class ExtensionClassBase : public Class<ExtensionInstance>
{
public:
ExtensionClassBase(const char* name);
public:
// the purpose of try_class_conversions() and its related functions
// is explained in extclass.cpp
void* try_class_conversions(InstanceHolderBase*) const;
void* try_base_class_conversions(InstanceHolderBase*) const;
void* try_derived_class_conversions(InstanceHolderBase*) const;
private:
virtual void* extract_object_from_holder(InstanceHolderBase* v) const = 0;
virtual std::vector<py::detail::BaseClassInfo> const& base_classes() const = 0;
virtual std::vector<py::detail::DerivedClassInfo> const& derived_classes() const = 0;
protected:
void add_method(PyPtr<Function> method, const char* name);
void add_default_method(PyPtr<Function> method, const char* name);
@@ -69,12 +102,24 @@ template <class T>
class ClassRegistry
{
public:
static Class<ExtensionInstance>* class_object()
static ExtensionClassBase* class_object()
{ return static_class_object; }
static void register_class(py::Class<py::ExtensionInstance>*);
static void unregister_class(py::Class<py::ExtensionInstance>*);
// Register/unregister the Python class object corresponding to T
static void register_class(ExtensionClassBase*);
static void unregister_class(ExtensionClassBase*);
// Establish C++ inheritance relationships
static void register_base_class(py::detail::BaseClassInfo const&);
static void register_derived_class(py::detail::DerivedClassInfo const&);
// Query the C++ inheritance relationships
static std::vector<py::detail::BaseClassInfo> const& base_classes();
static std::vector<py::detail::DerivedClassInfo> const& derived_classes();
private:
static py::Class<py::ExtensionInstance>* static_class_object;
static ExtensionClassBase* static_class_object;
static std::vector<py::detail::BaseClassInfo> static_base_class_info;
static std::vector<py::detail::DerivedClassInfo> static_derived_class_info;
};
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back to global namespace for this GCC bug
@@ -106,8 +151,6 @@ class PyExtensionClassConverters
{ return py::Type<U>(); }
#endif
PyExtensionClassConverters() {}
// Convert to T*
friend T* from_python(PyObject* obj, py::Type<T*>)
{
@@ -120,6 +163,11 @@ class PyExtensionClassConverters
py::InstanceHolder<T>* held = dynamic_cast<py::InstanceHolder<T>*>(*p);
if (held != 0)
return held->target();
// see extclass.cpp for an explanation of try_class_conversions()
void * target = py::ClassRegistry<T>::class_object()->try_class_conversions(*p);
if(target)
return static_cast<T *>(target);
}
py::report_missing_instance_data(self, py::ClassRegistry<T>::class_object(), typeid(T));
throw py::ArgumentError();
@@ -168,7 +216,6 @@ class PyExtensionClassConverters
return py::PyPtr<py::ExtensionInstance>(new py::ExtensionInstance(class_));
}
// Convert to const T*
friend const T* from_python(PyObject* p, py::Type<const T*>)
{ return from_python(p, py::Type<T*>()); }
@@ -247,6 +294,28 @@ class ReadOnlySetattrFunction : public Function
String m_name;
};
namespace detail
{
template <class From, class To>
struct DefineConversion
{
static void * upcast_ptr(void * v)
{
return static_cast<To *>(static_cast<From *>(v));
}
static void * downcast_ptr(void * v)
{
return dynamic_cast<To *>(static_cast<From *>(v));
}
};
}
enum WithoutDowncast { without_downcast };
// An easy way to make an extension base class which wraps T. Note that Python
// subclasses of this class will simply be Class<ExtensionInstance> objects.
//
@@ -279,10 +348,12 @@ class ExtensionClass
// define constructors
template <class A1, class A2, class A3, class A4, class A5>
void def(Constructor<A1, A2, A3, A4, A5>)
{ add_constructor( // the following incantation builds a Signature1,
prepend(Type<A1>::Id(), // Signature2, ... constructor. It _should_ all
prepend(Type<A2>::Id(), // get optimized away. Just another workaround
prepend(Type<A3>::Id(), // for the lack of partial specialization in MSVC
// The following incantation builds a Signature1, Signature2,... object. It
// should _all_ get optimized away.
{ add_constructor(
prepend(Type<A1>::Id(),
prepend(Type<A2>::Id(),
prepend(Type<A3>::Id(),
prepend(Type<A4>::Id(),
prepend(Type<A5>::Id(),
Signature0()))))));
@@ -339,10 +410,49 @@ class ExtensionClass
this->def_getter(pm, name);
this->def_setter(pm, name);
}
// declare the given class a base class of this one and register
// up and down conversion functions
template <class S, class V>
void declare_base(ExtensionClass<S, V> * base)
{
// see extclass.cpp for an explanation of why we need to register
// conversion functions
detail::BaseClassInfo baseInfo(base,
&detail::DefineConversion<S, T>::downcast_ptr);
ClassRegistry<T>::register_base_class(baseInfo);
add_base(Ptr(as_object(base), Ptr::new_ref));
detail::DerivedClassInfo derivedInfo(this,
&detail::DefineConversion<T, S>::upcast_ptr);
ClassRegistry<S>::register_derived_class(derivedInfo);
}
// declare the given class a base class of this one and register
// only up conversion function
template <class S, class V>
void declare_base(ExtensionClass<S, V> * base, WithoutDowncast)
{
// see extclass.cpp for an explanation of why we need to register
// conversion functions
detail::BaseClassInfo baseInfo(base, 0);
ClassRegistry<T>::register_base_class(baseInfo);
add_base(Ptr(as_object(base), Ptr::new_ref));
detail::DerivedClassInfo derivedInfo(this,
&detail::DefineConversion<T, S>::upcast_ptr);
ClassRegistry<S>::register_derived_class(derivedInfo);
}
private:
private: // types
typedef InstanceValueHolder<T,U> Holder;
private: // ExtensionClassBase virtual function implementations
std::vector<detail::BaseClassInfo> const& base_classes() const;
std::vector<detail::DerivedClassInfo> const& derived_classes() const;
void* extract_object_from_holder(InstanceHolderBase* v) const;
private: // Utility functions
template <class Signature>
void add_constructor(Signature sig)
{
@@ -350,7 +460,69 @@ class ExtensionClass
}
};
#include "extclass_pygen.h"
// A simple wrapper over a T which allows us to use ExtensionClass<T> with a
// single template parameter only. See ExtensionClass<T>, above.
template <class T>
class HeldInstance : public T
{
// There are no member functions: we want to avoid inadvertently overriding
// any virtual functions in T.
public:
HeldInstance(PyObject* p) : T(), m_self(p) {}
template <class A1>
HeldInstance(PyObject* p, const A1& a1) : T(a1), m_self(p) {}
template <class A1, class A2>
HeldInstance(PyObject* p, const A1& a1, const A2& a2) : T(a1, a2), m_self(p) {}
template <class A1, class A2, class A3>
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3) : T(a1, a2, a3), m_self(p) {}
template <class A1, class A2, class A3, class A4>
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4) : T(a1, a2, a3, a4), m_self(p) {}
template <class A1, class A2, class A3, class A4, class A5>
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) : T(a1, a2, a3, a4, a5), m_self(p) {}
protected:
PyObject* m_self; // Not really needed; doesn't really hurt.
};
class InstanceHolderBase
{
public:
virtual ~InstanceHolderBase() {}
};
template <class Held>
class InstanceHolder : public InstanceHolderBase
{
public:
virtual Held *target() = 0;
};
template <class Held, class Wrapper>
class InstanceValueHolder : public InstanceHolder<Held>
{
public:
Held* target() { return &m_held; }
Wrapper* value_target() { return &m_held; }
InstanceValueHolder(ExtensionInstance* p) :
m_held(p) {}
template <class A1>
InstanceValueHolder(ExtensionInstance* p, const A1& a1) :
m_held(p, a1) {}
template <class A1, class A2>
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2) :
m_held(p, a1, a2) {}
template <class A1, class A2, class A3>
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3) :
m_held(p, a1, a2, a3) {}
template <class A1, class A2, class A3, class A4>
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4) :
m_held(p, a1, a2, a3, a4) {}
template <class A1, class A2, class A3, class A4, class A5>
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) :
m_held(p, a1, a2, a3, a4, a5) {}
private:
Wrapper m_held;
};
template <class PtrType, class HeldType>
class InstancePtrHolder : public InstanceHolder<HeldType>
@@ -358,7 +530,7 @@ class InstancePtrHolder : public InstanceHolder<HeldType>
public:
HeldType* target() { return &*m_ptr; }
PtrType& ptr() { return m_ptr; }
InstancePtrHolder(PtrType ptr) : m_ptr(ptr) {}
private:
PtrType m_ptr;
@@ -397,28 +569,38 @@ ExtensionClass<T, U>::ExtensionClass(const char* name)
ClassRegistry<T>::register_class(this);
}
template <class T, class U>
inline
std::vector<detail::BaseClassInfo> const &
ExtensionClass<T, U>::base_classes() const
{
return ClassRegistry<T>::base_classes();
}
template <class T, class U>
inline
std::vector<detail::DerivedClassInfo> const &
ExtensionClass<T, U>::derived_classes() const
{
return ClassRegistry<T>::derived_classes();
}
template <class T, class U>
void* ExtensionClass<T, U>::extract_object_from_holder(InstanceHolderBase* v) const
{
py::InstanceHolder<T>* held = dynamic_cast<py::InstanceHolder<T>*>(v);
if(held) return held->target();
return 0;
}
template <class T, class U>
ExtensionClass<T, U>::~ExtensionClass()
{
ClassRegistry<T>::unregister_class(this);
}
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // Back to the global namespace for this GCC bug
}
#endif
//
// Static data member declaration.
//
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // Back from the global namespace for this GCC bug
namespace py {
#endif
template <class T>
Class<py::ExtensionInstance>* ClassRegistry<T>::static_class_object;
template <class T>
inline void ClassRegistry<T>::register_class(Class<ExtensionInstance>* p)
inline void ClassRegistry<T>::register_class(ExtensionClassBase* p)
{
// You're not expected to create more than one of these!
assert(static_class_object == 0);
@@ -426,7 +608,7 @@ inline void ClassRegistry<T>::register_class(Class<ExtensionInstance>* p)
}
template <class T>
inline void ClassRegistry<T>::unregister_class(Class<ExtensionInstance>* p)
inline void ClassRegistry<T>::unregister_class(ExtensionClassBase* p)
{
// The user should be destroying the same object they created.
assert(static_class_object == p);
@@ -434,6 +616,41 @@ inline void ClassRegistry<T>::unregister_class(Class<ExtensionInstance>* p)
static_class_object = 0;
}
template <class T>
void ClassRegistry<T>::register_base_class(py::detail::BaseClassInfo const & i)
{
static_base_class_info.push_back(i);
}
template <class T>
void ClassRegistry<T>::register_derived_class(py::detail::DerivedClassInfo const & i)
{
static_derived_class_info.push_back(i);
}
template <class T>
std::vector<py::detail::BaseClassInfo> const& ClassRegistry<T>::base_classes()
{
return static_base_class_info;
}
template <class T>
std::vector<py::detail::DerivedClassInfo> const& ClassRegistry<T>::derived_classes()
{
return static_derived_class_info;
}
//
// Static data member declaration.
//
template <class T>
ExtensionClassBase* ClassRegistry<T>::static_class_object;
template <class T>
std::vector<py::detail::BaseClassInfo> ClassRegistry<T>::static_base_class_info;
template <class T>
std::vector<py::detail::DerivedClassInfo> ClassRegistry<T>::static_derived_class_info;
} // namespace py
#endif // EXTENSION_CLASS_DWA052000_H_

View File

@@ -1,3 +0,0 @@
#define DEBUG_PYTHON
#include "extclass.cpp"

View File

@@ -8,6 +8,7 @@
#include "extclass_demo.h"
#include "class_wrapper.h"
#include <stdio.h> // used for portability on broken compilers
#include <boost/rational.hpp>
namespace extclass_demo {
@@ -290,6 +291,245 @@ long range_hash(const Range& r)
return r.m_start * 123 + r.m_finish;
}
/************************************************************/
/* */
/* some functions to test overloading */
/* */
/************************************************************/
static std::string testVoid()
{
return std::string("Hello world!");
}
static int testInt(int i)
{
return i;
}
static std::string testString(std::string i)
{
return i;
}
static int test2(int i1, int i2)
{
return i1+i2;
}
static int test3(int i1, int i2, int i3)
{
return i1+i2+i3;
}
static int test4(int i1, int i2, int i3, int i4)
{
return i1+i2+i3+i4;
}
static int test5(int i1, int i2, int i3, int i4, int i5)
{
return i1+i2+i3+i4+i5;
}
/************************************************************/
/* */
/* a class to test overloading */
/* */
/************************************************************/
struct OverloadTest
{
OverloadTest(): x_(1000) {}
OverloadTest(int x): x_(x) {}
OverloadTest(int x,int y): x_(x+y) { }
OverloadTest(int x,int y,int z): x_(x+y+z) {}
OverloadTest(int x,int y,int z, int a): x_(x+y+z+a) {}
OverloadTest(int x,int y,int z, int a, int b): x_(x+y+z+a+b) {}
int x() const { return x_; }
void setX(int x) { x_ = x; }
int p1(int x) { return x; }
int p2(int x, int y) { return x + y; }
int p3(int x, int y, int z) { return x + y + z; }
int p4(int x, int y, int z, int a) { return x + y + z + a; }
int p5(int x, int y, int z, int a, int b) { return x + y + z + a + b; }
private:
int x_;
};
static int getX(OverloadTest * u)
{
return u->x();
}
/************************************************************/
/* */
/* classes to test base declarations and conversions */
/* */
/************************************************************/
struct Dummy
{
virtual ~Dummy() {}
int dummy_;
};
struct Base
{
virtual int x() const { return 999; };
virtual ~Base() {}
};
// inherit Dummy so that the Base part of Concrete starts at an offset
// otherwise, typecast tests wouldn't be very meaningful
struct Derived1 : public Dummy, public Base
{
Derived1(int x): x_(x) {}
virtual int x() const { return x_; }
private:
int x_;
};
struct Derived2 : public Dummy, public Base
{
Derived2(int x): x_(x) {}
virtual int x() const { return x_; }
private:
int x_;
};
static int testUpcast(Base * b)
{
return b->x();
}
static std::auto_ptr<Base> derived1Factory(int i)
{
return std::auto_ptr<Base>(new Derived1(i));
}
static std::auto_ptr<Base> derived2Factory(int i)
{
return std::auto_ptr<Base>(new Derived2(i));
}
static int testDowncast1(Derived1 * d)
{
return d->x();
}
static int testDowncast2(Derived2 * d)
{
return d->x();
}
/************************************************************/
/* */
/* test classes for interaction of overloading, */
/* base declarations, and callbacks */
/* */
/************************************************************/
struct CallbackTestBase
{
virtual int testCallback(int i) { return callback(i); }
virtual int callback(int i) = 0;
virtual ~CallbackTestBase() {}
};
struct CallbackTest : public CallbackTestBase
{
virtual int callback(int i) { return i + 1; }
virtual std::string callbackString(std::string const & i) { return i + " 1"; }
};
struct CallbackTestCallback : public CallbackTest
{
CallbackTestCallback(PyObject* self)
: m_self(self)
{}
int callback(int x)
{
return py::Callback<int>::call_method(m_self, "callback", x);
}
std::string callbackString(std::string const & x)
{
return py::Callback<std::string>::call_method(m_self, "callback", x);
}
static int default_callback(CallbackTest * self, int x)
{
return self->CallbackTest::callback(x);
}
static std::string default_callbackString(CallbackTest * self, std::string x)
{
return self->CallbackTest::callbackString(x);
}
PyObject * m_self;
};
int testCallback(CallbackTestBase * b, int i)
{
return b->testCallback(i);
}
typedef boost::rational<int> Ratio;
py::String ratio_str(const Ratio& r)
{
char buf[200];
if (r.denominator() == 1)
sprintf(buf, "%d", r.numerator());
else
sprintf(buf, "%d/%d", r.numerator(), r.denominator());
return py::String(buf);
}
py::String ratio_repr(const Ratio& r)
{
char buf[200];
sprintf(buf, "Rational(%d, %d)", r.numerator(), r.denominator());
return py::String(buf);
}
py::Tuple ratio_coerce(const Ratio& r1, int r2)
{
return py::Tuple(r1, Ratio(r2));
}
// The most reliable way, across compilers, to grab the particular abs function
// we're interested in.
Ratio ratio_abs(const Ratio& r)
{
return boost::abs(r);
}
// An experiment, to be integrated into the py_cpp library at some point.
template <class T>
struct StandardOps
{
static T add(const T& x, const T& y) { return x + y; }
static T sub(const T& x, const T& y) { return x - y; }
static T mul(const T& x, const T& y) { return x * y; }
static T div(const T& x, const T& y) { return x / y; }
static T cmp(const T& x, const T& y) { return std::less<T>()(x, y) ? -1 : std::less<T>()(y, x) ? 1 : 0; }
};
/************************************************************/
/* */
/* init the module */
/* */
/************************************************************/
void init_module(py::Module& m)
{
m.add(new Foo::PythonClass);
@@ -309,6 +549,21 @@ void init_module(py::Module& m)
m.def(first_string, "first_string");
m.def(second_string, "second_string");
// This shows the wrapping of a 3rd-party numeric type.
py::ClassWrapper<boost::rational<int> > rational(m, "Rational");
rational.def(py::Constructor<int, int>());
rational.def(py::Constructor<int>());
rational.def(py::Constructor<>());
rational.def(StandardOps<Ratio>::add, "__add__");
rational.def(StandardOps<Ratio>::sub, "__sub__");
rational.def(StandardOps<Ratio>::mul, "__mul__");
rational.def(StandardOps<Ratio>::div, "__div__");
rational.def(StandardOps<Ratio>::cmp, "__cmp__");
rational.def(ratio_coerce, "__coerce__");
rational.def(ratio_str, "__str__");
rational.def(ratio_repr, "__repr__");
rational.def(ratio_abs, "__abs__");
py::ClassWrapper<Range> range(m, "Range");
range.def(py::Constructor<int>());
range.def(py::Constructor<int, int>());
@@ -321,6 +576,64 @@ void init_module(py::Module& m)
range.def(&range_hash, "__hash__");
range.def_readonly(&Range::m_start, "start");
range.def_readonly(&Range::m_finish, "finish");
m.def(&testVoid, "overloaded");
m.def(&testInt, "overloaded");
m.def(&testString, "overloaded");
m.def(&test2, "overloaded");
m.def(&test3, "overloaded");
m.def(&test4, "overloaded");
m.def(&test5, "overloaded");
py::ClassWrapper<OverloadTest> over(m, "OverloadTest");
over.def(py::Constructor<py::Void>());
over.def(py::Constructor<OverloadTest const &>());
over.def(py::Constructor<int>());
over.def(py::Constructor<int, int>());
over.def(py::Constructor<int, int, int>());
over.def(py::Constructor<int, int, int, int>());
over.def(py::Constructor<int, int, int, int, int>());
over.def(&getX, "getX");
over.def(&OverloadTest::setX, "setX");
over.def(&OverloadTest::x, "overloaded");
over.def(&OverloadTest::p1, "overloaded");
over.def(&OverloadTest::p2, "overloaded");
over.def(&OverloadTest::p3, "overloaded");
over.def(&OverloadTest::p4, "overloaded");
over.def(&OverloadTest::p5, "overloaded");
py::ClassWrapper<Base> base(m, "Base");
base.def(&Base::x, "x");
py::ClassWrapper<Derived1> derived1(m, "Derived1");
// this enables conversions between Base and Derived1
// and makes wrapped methods of Base available
derived1.declare_base(base);
derived1.def(py::Constructor<int>());
py::ClassWrapper<Derived2> derived2(m, "Derived2");
// don't enable downcast from Base to Derived2
derived2.declare_base(base, py::without_downcast);
derived2.def(py::Constructor<int>());
m.def(&testUpcast, "testUpcast");
m.def(&derived1Factory, "derived1Factory");
m.def(&derived2Factory, "derived2Factory");
m.def(&testDowncast1, "testDowncast1");
m.def(&testDowncast2, "testDowncast2");
py::ClassWrapper<CallbackTestBase> callbackTestBase(m, "CallbackTestBase");
callbackTestBase.def(&CallbackTestBase::testCallback, "testCallback");
m.def(&testCallback, "testCallback");
py::ClassWrapper<CallbackTest, CallbackTestCallback> callbackTest(m, "CallbackTest");
callbackTest.def(py::Constructor<py::Void>());
callbackTest.def(&CallbackTest::callback, "callback",
&CallbackTestCallback::default_callback);
callbackTest.def(&CallbackTest::callbackString, "callback",
&CallbackTestCallback::default_callbackString);
callbackTest.declare_base(callbackTestBase);
}
void init_module()

View File

@@ -1,2 +0,0 @@
#define DEBUG_PYTHON
#include "extclass_demo.cpp"

View File

@@ -1,80 +0,0 @@
// (C) Copyright David Abrahams 2000. Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
// The author gratefully acknowleges the support of Dragon Systems, Inc., in
// producing this work.
//
// This file automatically generated for 5-argument constructors by
// gen_extclass.py
#ifndef EXTCLASS_PYGEN_DWA070900_H_
# define EXTCLASS_PYGEN_DWA070900_H_
// A simple wrapper over a T which allows us to use ExtensionClass<T> with a
// single template parameter only. See ExtensionClass<T>, above.
template <class T>
class HeldInstance : public T
{
// There are no member functions: we want to avoid inadvertently overriding
// any virtual functions in T.
public:
HeldInstance(PyObject* p) : T(), m_self(p) {}
template <class A1>
HeldInstance(PyObject* p, const A1& a1) : T(a1), m_self(p) {}
template <class A1, class A2>
HeldInstance(PyObject* p, const A1& a1, const A2& a2) : T(a1, a2), m_self(p) {}
template <class A1, class A2, class A3>
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3) : T(a1, a2, a3), m_self(p) {}
template <class A1, class A2, class A3, class A4>
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4) : T(a1, a2, a3, a4), m_self(p) {}
template <class A1, class A2, class A3, class A4, class A5>
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) : T(a1, a2, a3, a4, a5), m_self(p) {}
protected:
PyObject* m_self; // Not really needed; doesn't really hurt.
};
class InstanceHolderBase
{
public:
virtual ~InstanceHolderBase() {}
};
template <class Held>
class InstanceHolder : public InstanceHolderBase
{
public:
virtual Held *target() = 0;
};
template <class Held, class Wrapper>
class InstanceValueHolder : public InstanceHolder<Held>
{
public:
Held* target() { return &m_held; }
Wrapper* value_target() { return &m_held; }
InstanceValueHolder(ExtensionInstance* p) :
m_held(p) {}
template <class A1>
InstanceValueHolder(ExtensionInstance* p, const A1& a1) :
m_held(p, a1) {}
template <class A1, class A2>
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2) :
m_held(p, a1, a2) {}
template <class A1, class A2, class A3>
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3) :
m_held(p, a1, a2, a3) {}
template <class A1, class A2, class A3, class A4>
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4) :
m_held(p, a1, a2, a3, a4) {}
template <class A1, class A2, class A3, class A4, class A5>
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) :
m_held(p, a1, a2, a3, a4, a5) {}
private:
Wrapper m_held;
};
#endif

73
extending.html Normal file
View File

@@ -0,0 +1,73 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<title>
A Brief Introduction to writing Python extension modules
</title>
<h1>
<img src="c++boost.gif" alt="c++boost.gif (8819 bytes)" align="center"
width="277" height="86">
</h1>
<h1>
A Brief Introduction to writing Python extension modules
</h1>
<p>
Interfacing any language to Python involves building a module which can
be loaded by the Python interpreter, but which isn't written in Python.
This is known as an <em>extension module</em>. Many of the <a href=
"http://www.python.org/doc/current/lib/lib.html">built-in Python
libraries</a> are constructed in 'C' this way; Python even supplies its
<a href="http://www.python.org/doc/current/lib/types.html">fundamental
types</a> using the same mechanism. An extension module can be statically
linked with the Python interpreter, but it more commonly resides in a
shared library or DLL.
<p>
As you can see from <a href=
"http://www.python.org/doc/current/ext/ext.html"> The Python Extending
and Embedding Tutorial</a>, writing an extension module normally means
worrying about
<ul>
<li>
<a href="http://www.python.org/doc/current/ext/refcounts.html">
maintaining reference counts</a>
<li>
<a href="http://www.python.org/doc/current/ext/callingPython.html"> how
to call back into Python</a>
<li>
<a href="http://www.python.org/doc/current/ext/parseTuple.html">
function argument parsing and typechecking</a>
</ul>
This last item typically occupies a great deal of code in an extension
module. Remember that Python is a completely dynamic language. A callable
object receives its arguments in a tuple; it is up to that object to
extract those arguments from the tuple, check their types, and raise
appropriate exceptions. There are numerous other tedious details that need
to be managed; too many to mention here. Py_cpp is designed to lift most of
that burden.<br>
<br>
<p>
Another obstacle that most people run into eventually when extending
Python is that there's no way to make a true Python class in an extension
module. The typical solution is to create a new Python type in the
extension module, and then write an additional module in 100% Python. The
Python module defines a Python class which dispatches to an instance of
the extension type, which it contains. This allows users to write
subclasses of the class in the Python module, almost as though they were
sublcassing the extension type. Aside from being tedious, it's not really
the same as having a true class, because there's no way for the user to
override a method of the extension type which is called from the
extension module. Py_cpp solves this problem by taking advantage of <a
href="http://www.python.org/doc/essays/metaclasses/">Python's metaclass
feature</a> to provide objects which look, walk, and hiss almost exactly
like regular Python classes. Py_cpp classes are actually cleaner than
Python classes in some subtle ways; a more detailed discussion will
follow (someday).</p>
<p>Next: <a href="comparisons.html">Comparisons with Other Systems</a> Up: <a
href="py_cpp.html">Top</a> </p>
<p>
&copy; 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.</p>

View File

@@ -1,2 +0,0 @@
#define DEBUG_PYTHON
#include "functions.cpp"

12
gcc.mak
View File

@@ -11,7 +11,15 @@ LIBSRC = \
LIBOBJ = $(LIBSRC:.cpp=.o)
OBJ = $(LIBOBJ) extclass_demo.o
ifeq "$(OS)" "Windows_NT"
PYTHON_LIB=c:/tools/python/libs/python15.lib
INC = -Ic:/cygnus/usr/include/g++-3 -Ic:/cygnus/usr/include -Ic:/boost -Ic:/tools/python/include
MODULE_EXTENSION=dll
else
INC = -I/home/koethe/include -I/home/koethe/C++/boost -I/home/koethe/python/include/python1.5
MODULE_EXTENSION=so
endif
%.o: %.cpp
g++ -fPIC $(INC) -c $*.cpp
@@ -23,11 +31,11 @@ INC = -I/home/koethe/include -I/home/koethe/C++/boost -I/home/koethe/python/incl
[ -s $@ ] || rm -f $@
demo: extclass_demo.o libpycpp.a
g++ -shared -o demomodule.so extclass_demo.o -L. -lpycpp
g++ -shared -o demomodule.$(MODULE_EXTENSION) $(PYTHON_LIB) extclass_demo.o -L. -lpycpp
python test_extclass.py
clean:
rm -rf *.o *.so *.a *.d *.pyc *.bak a.out
rm -rf *.o *.$(MODULE_EXTENSION) *.a *.d *.pyc *.bak a.out
libpycpp.a: $(LIBOBJ)
rm -f libpycpp.a

View File

@@ -11,7 +11,7 @@ def gen_all(args):
open('init_function.h', 'w').write(gen_init_function(args))
open('signatures.h', 'w').write(gen_signatures(args))
open('singleton.h', 'w').write(gen_singleton(args))
open('extclass_pygen.h', 'w').write(gen_extclass(args))
open('extclass.h', 'w').write(gen_extclass(args))
if __name__ == '__main__':
import sys

View File

@@ -23,10 +23,352 @@ def gen_extclass(args):
// This file automatically generated for %d-argument constructors by
// gen_extclass.py
#ifndef EXTCLASS_PYGEN_DWA070900_H_
# define EXTCLASS_PYGEN_DWA070900_H_
#ifndef EXTENSION_CLASS_DWA052000_H_
# define EXTENSION_CLASS_DWA052000_H_
# include "pyconfig.h"
# include "subclass.h"
# include <vector>
# include "none.h"
# include "objects.h"
# include "functions.h"
# include <memory>
# include "init_function.h"
# include <typeinfo>
# include <boost/smart_ptr.hpp>
namespace py {
// forward declarations
class ExtensionInstance;
template <class T> class InstanceHolder;
template <class T, class U> class InstanceValueHolder;
template <class Ptr, class T> class InstancePtrHolder;
MetaClass<ExtensionInstance>* extension_meta_class();
ExtensionInstance* get_extension_instance(PyObject* p);
void report_missing_instance_data(ExtensionInstance*, Class<ExtensionInstance>*, const std::type_info&);
void report_missing_ptr_data(ExtensionInstance*, Class<ExtensionInstance>*, const std::type_info&);
void report_missing_class_object(const std::type_info&);
void report_released_smart_pointer(const std::type_info&);
template <class T, class U>
struct ExtensionClassFromPython
{
};
template <class T>
T* check_non_null(T* p)
{
if (p == 0)
report_released_smart_pointer(typeid(T));
return p;
}
template <class T> class HeldInstance;
class ExtensionClassBase : public Class<ExtensionInstance>
{
public:
ExtensionClassBase(const char* name);
protected:
void add_method(PyPtr<Function> method, const char* name);
void add_default_method(PyPtr<Function> method, const char* name);
void add_method(Function* method, const char* name);
void add_default_method(Function* method, const char* name);
void add_constructor_object(Function*);
void add_setter_method(Function*, const char* name);
void add_getter_method(Function*, const char* name);
};
template <class T>
class ClassRegistry
{
public:
static Class<ExtensionInstance>* class_object()
{ return static_class_object; }
static void register_class(py::Class<py::ExtensionInstance>*);
static void unregister_class(py::Class<py::ExtensionInstance>*);
private:
static py::Class<py::ExtensionInstance>* static_class_object;
};
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back to global namespace for this GCC bug
}
#endif
// This class' only job is to define from_python and to_python converters for T
// and U. T is the class the user really intends to wrap. U is a class derived
// from T with some virtual function overriding boilerplate, or if there are no
// virtual functions, U = HeldInstance<T>.
template <class T, class U = py::HeldInstance<T> >
class PyExtensionClassConverters
{
public:
#ifdef BOOST_MSVC
// Convert return values of type T to python objects. What happens if T is
// not copyable? Apparently there is no problem with g++ or MSVC unless this
// is actually used. With a conforming compiler we will have a problem.
friend PyObject* to_python(const T& x)
{
py::PyPtr<py::ExtensionInstance> result(create_instance(false));
result->add_implementation(
std::auto_ptr<py::InstanceHolderBase>(
new py::InstanceValueHolder<T,U>(result.get(), x)));
return result.release();
}
#else
friend py::Type<U> py_holder_type(const T&)
{ return py::Type<U>(); }
#endif
PyExtensionClassConverters() {}
// Convert to T*
friend T* from_python(PyObject* obj, py::Type<T*>)
{
// Downcast to an ExtensionInstance, then find the actual T
py::ExtensionInstance* self = py::get_extension_instance(obj);
typedef std::vector<py::InstanceHolderBase*>::const_iterator Iterator;
for (Iterator p = self->wrapped_objects().begin();
p != self->wrapped_objects().end(); ++p)
{
py::InstanceHolder<T>* held = dynamic_cast<py::InstanceHolder<T>*>(*p);
if (held != 0)
return held->target();
}
py::report_missing_instance_data(self, py::ClassRegistry<T>::class_object(), typeid(T));
throw py::ArgumentError();
}
// Convert to PtrType, where PtrType can be dereferenced to obtain a T.
template <class PtrType>
static PtrType& ptr_from_python(PyObject* obj, py::Type<PtrType>)
{
// Downcast to an ExtensionInstance, then find the actual T
py::ExtensionInstance* self = py::get_extension_instance(obj);
typedef std::vector<py::InstanceHolderBase*>::const_iterator Iterator;
for (Iterator p = self->wrapped_objects().begin();
p != self->wrapped_objects().end(); ++p)
{
py::InstancePtrHolder<PtrType, T>* held =
dynamic_cast<py::InstancePtrHolder<PtrType, T>*>(*p);
if (held != 0)
return held->ptr();
}
py::report_missing_ptr_data(self, py::ClassRegistry<T>::class_object(), typeid(T));
throw py::ArgumentError();
}
template <class PtrType>
static PyObject* ptr_to_python(PtrType x)
{
py::PyPtr<py::ExtensionInstance> result(create_instance(true));
result->add_implementation(
std::auto_ptr<py::InstanceHolderBase>(
new py::InstancePtrHolder<PtrType,T>(x)));
return result.release();
}
static py::PyPtr<py::ExtensionInstance> create_instance(bool seek_base)
{
if (py::ClassRegistry<T>::class_object() == 0)
py::report_missing_class_object(typeid(T));
py::Class<py::ExtensionInstance>* class_
= seek_base && py::ClassRegistry<T>::class_object()->bases().size() > 0
? py::Downcast<py::Class<py::ExtensionInstance> >(
py::ClassRegistry<T>::class_object()->bases()[0].get()).get()
: py::ClassRegistry<T>::class_object();
return py::PyPtr<py::ExtensionInstance>(new py::ExtensionInstance(class_));
}
// Convert to const T*
friend const T* from_python(PyObject* p, py::Type<const T*>)
{ return from_python(p, py::Type<T*>()); }
// Convert to T&
friend T& from_python(PyObject* p, py::Type<T&>)
{ return *py::check_non_null(from_python(p, py::Type<T*>())); }
// Convert to const T&
friend const T& from_python(PyObject* p, py::Type<const T&>)
{ return from_python(p, py::Type<T&>()); }
// Convert to T
friend const T& from_python(PyObject* p, py::Type<T>)
{ return from_python(p, py::Type<T&>()); }
friend std::auto_ptr<T>& from_python(PyObject* p, py::Type<std::auto_ptr<T>&>)
{ return ptr_from_python(p, py::Type<std::auto_ptr<T> >()); }
friend std::auto_ptr<T>& from_python(PyObject* p, py::Type<std::auto_ptr<T> >)
{ return ptr_from_python(p, py::Type<std::auto_ptr<T> >()); }
friend const std::auto_ptr<T>& from_python(PyObject* p, py::Type<const std::auto_ptr<T>&>)
{ return ptr_from_python(p, py::Type<std::auto_ptr<T> >()); }
friend PyObject* to_python(std::auto_ptr<T> x)
{ return ptr_to_python(x); }
friend boost::shared_ptr<T>& from_python(PyObject* p, py::Type<boost::shared_ptr<T>&>)
{ return ptr_from_python(p, py::Type<boost::shared_ptr<T> >()); }
friend boost::shared_ptr<T>& from_python(PyObject* p, py::Type<boost::shared_ptr<T> >)
{ return ptr_from_python(p, py::Type<boost::shared_ptr<T> >()); }
friend const boost::shared_ptr<T>& from_python(PyObject* p, py::Type<const boost::shared_ptr<T>&>)
{ return ptr_from_python(p, py::Type<boost::shared_ptr<T> >()); }
friend PyObject* to_python(boost::shared_ptr<T> x)
{ return ptr_to_python(x); }
};
#ifndef BOOST_MSVC
template <class T, class U>
py::InstanceHolderBase*
py_copy_to_new_value_holder(py::ExtensionInstance* p, const T& x, py::Type<U>)
{
return new py::InstanceValueHolder<T,U>(p, x);
}
template <class T>
PyObject* to_python(const T& x)
{
py::PyPtr<py::ExtensionInstance> result(
PyExtensionClassConverters<T>::create_instance(false));
result->add_implementation(
std::auto_ptr<py::InstanceHolderBase>(
py_copy_to_new_value_holder(result.get(), x, py_holder_type(x))));
return result.release();
}
#endif
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back from global namespace for this GCC bug
namespace py {
using ::PyExtensionClassConverters;
#endif
template <class T> class InstanceHolder;
class ReadOnlySetattrFunction : public Function
{
public:
ReadOnlySetattrFunction(const char* name);
PyObject* do_call(PyObject* args, PyObject* keywords) const;
const char* description() const;
private:
String m_name;
};
// An easy way to make an extension base class which wraps T. Note that Python
// subclasses of this class will simply be Class<ExtensionInstance> objects.
//
// U should be a class derived from T which overrides virtual functions with
// boilerplate code to call back into Python. See extclass_demo.h for examples.
//
// U is optional, but you won't be able to override any member functions in
// Python which are called from C++ if you don't supply it. If you just want to
// be able to use T in python without overriding member functions, you can omit
// U.
template <class T, class U = HeldInstance<T> >
class ExtensionClass
: public PyExtensionClassConverters<T, U>, // This generates the to_python/from_python functions
public ExtensionClassBase
{
public:
typedef T WrappedType;
typedef U CallbackType;
// Construct with a name that comes from typeid(T).name(). The name only
// affects the objects of this class are represented through repr()
ExtensionClass();
// Construct with the given name. The name only affects the objects of this
// class are represented through repr()
ExtensionClass(const char* name);
~ExtensionClass();
// define constructors
""" % args
+ gen_function(
""" template <%(class A%n%:, %)>
void def(Constructor<%(A%n%:, %)>)
// The following incantation builds a Signature1, Signature2,... object. It
// should _all_ get optimized away.
{ add_constructor(
%(prepend(Type<A%n>::Id(),
%) Signature0()%()%));
}
""", args)
+
"""
// define member functions. In fact this works for free functions, too -
// they act like static member functions, or if they start with the
// appropriate self argument (as a pointer), they can be used just like
// ordinary member functions -- just like Python!
template <class Fn>
void def(Fn fn, const char* name)
{
this->add_method(new_wrapped_function(fn), name);
}
// Define a virtual member function with a default implementation.
// default_fn should be a function which provides the default implementation.
// Be careful that default_fn does not in fact call fn virtually!
template <class Fn, class DefaultFn>
void def(Fn fn, const char* name, DefaultFn default_fn)
{
this->add_default_method(new_wrapped_function(default_fn), name);
this->add_method(new_wrapped_function(fn), name);
}
// Provide a function which implements x.<name>, reading from the given
// member (pm) of the T instance
template <class MemberType>
void def_getter(MemberType T::*pm, const char* name)
{
this->add_getter_method(new GetterFunction<T, MemberType>(pm), name);
}
// Provide a function which implements assignment to x.<name>, writing to
// the given member (pm) of the T instance
template <class MemberType>
void def_setter(MemberType T::*pm, const char* name)
{
this->add_setter_method(new SetterFunction<T, MemberType>(pm), name);
}
// Expose the given member (pm) of the T instance as a read-only attribute
template <class MemberType>
void def_readonly(MemberType T::*pm, const char* name)
{
this->add_setter_method(new ReadOnlySetattrFunction(name), name);
this->def_getter(pm, name);
}
// Expose the given member (pm) of the T instance as a read/write attribute
template <class MemberType>
void def_read_write(MemberType T::*pm, const char* name)
{
this->def_getter(pm, name);
this->def_setter(pm, name);
}
private:
typedef InstanceValueHolder<T,U> Holder;
template <class Signature>
void add_constructor(Signature sig)
{
this->add_constructor_object(InitFunction<Holder>::create(sig));
}
};
// A simple wrapper over a T which allows us to use ExtensionClass<T> with a
// single template parameter only. See ExtensionClass<T>, above.
template <class T>
@@ -34,7 +376,7 @@ class HeldInstance : public T
{
// There are no member functions: we want to avoid inadvertently overriding
// any virtual functions in T.
public:""" % args
public:"""
+ gen_functions(held_instance, args)
+ """
protected:
@@ -66,8 +408,85 @@ public:
private:
Wrapper m_held;
};
""" +
"""
template <class PtrType, class HeldType>
class InstancePtrHolder : public InstanceHolder<HeldType>
{
public:
HeldType* target() { return &*m_ptr; }
PtrType& ptr() { return m_ptr; }
#endif
InstancePtrHolder(PtrType ptr) : m_ptr(ptr) {}
private:
PtrType m_ptr;
};
class ExtensionInstance : public Instance
{
public:
ExtensionInstance(PyTypeObject* class_);
~ExtensionInstance();
void add_implementation(std::auto_ptr<InstanceHolderBase> holder);
typedef std::vector<InstanceHolderBase*> WrappedObjects;
const WrappedObjects& wrapped_objects() const
{ return m_wrapped_objects; }
private:
WrappedObjects m_wrapped_objects;
};
//
// Template function implementations
//
template <class T, class U>
ExtensionClass<T, U>::ExtensionClass()
: ExtensionClassBase(typeid(T).name())
{
ClassRegistry<T>::register_class(this);
}
template <class T, class U>
ExtensionClass<T, U>::ExtensionClass(const char* name)
: ExtensionClassBase(name)
{
ClassRegistry<T>::register_class(this);
}
template <class T, class U>
ExtensionClass<T, U>::~ExtensionClass()
{
ClassRegistry<T>::unregister_class(this);
}
template <class T>
inline void ClassRegistry<T>::register_class(Class<ExtensionInstance>* p)
{
// You're not expected to create more than one of these!
assert(static_class_object == 0);
static_class_object = p;
}
template <class T>
inline void ClassRegistry<T>::unregister_class(Class<ExtensionInstance>* p)
{
// The user should be destroying the same object they created.
assert(static_class_object == p);
(void)p; // unused in shipping version
static_class_object = 0;
}
//
// Static data member declaration.
//
template <class T>
Class<py::ExtensionInstance>* ClassRegistry<T>::static_class_object;
} // namespace py
#endif // EXTENSION_CLASS_DWA052000_H_
""")
if __name__ == '__main__':

View File

@@ -1,2 +0,0 @@
#define DEBUG_PYTHON
#include "init_function.cpp"

View File

@@ -1,48 +0,0 @@
#########################################################
#
# makefile - Manhattan py_cpp makefile
#
# Author: David Abrahams
# Date: 04-May-2000
# Copyright (c) 2000 Dragon Systems, Inc.
#
# Revision history at bottom.
#
#########################################################
# Set up $(ROOT_DIR)
include ../make/userdirs.mak
include $(ROOT_DIR)/make/common.mak
include $(ROOT_DIR)/python/pythonhelp.mak
include $(ROOT_DIR)/utils/utilhelp.mak
include $(ROOT_DIR)/py_cpp/py_cpphelp.mak
MAKEFILE_INCLUDES = $(PYTHON_INCLUDES)
# Look in this directory, then the output subdirectory (BIN_DIR) for modules to load without qualification.
export PYTHONPATH := $(THISDIR)$(PYTHONPATH_SEP)$(THISDIR)/$(BIN_DIR)$(PYTHONPATH_SEP)$(PYTHONPATH)
# In order to get the automatic dependency generation working correctly, it
# is necessary to list the source files here.
SRC_FILES = extclass.cpp init_function.cpp subclass.cpp functions.cpp module.cpp newtypes.cpp py.cpp objects.cpp
SRC_FILES += extclass_demo.cpp example1.cpp
SRC_FILES += extclass_d.cpp init_function_d.cpp subclass_d.cpp functions_d.cpp module_d.cpp newtypes_d.cpp py_d.cpp objects_d.cpp
SRC_FILES += extclass_demo_d.cpp
LIBS=$(PYTHON_LIB) $(PYTHON_D_LIB)
test : demo
$(PYTHON_EXE) test_extclass.py $(ARGS)
test_d : demo_d
$(DEBUGGER) $(PYTHON_D_EXE) test_extclass.py $(ARGS)
ifndef PYTHON_D_LIB
PYTHON_D_LIB = $(SPACE_CHAR)
endif
-include py_cpp.mk1
-include py_cpp_d.mk1
-include demo.mk1
-include demo_d.mk1
-include example1.mk1

View File

@@ -1,2 +0,0 @@
#define DEBUG_PYTHON
#include "module.cpp"

View File

@@ -86,21 +86,13 @@ static MethodStruct* enable_method(const MethodStruct* base, MemberPtr p, Fn f)
return const_cast<MethodStruct*>(detail::UniquePodSet::instance().get(new_value));
}
// TODO: is there a problem with calling convention here, or can I really pass a
// pointer to a C++ linkage function as a C-linkage function pointer? The
// compilers seem to swallow it, but is it legal? Symantec C++ for Mac didn't
// behave this way, FWIW.
// Using C++ linkage allows us to keep the virtual function members of
// TypeObjectBase private and use friendship to get them called.
namespace {
extern "C" {
static PyObject* do_instance_repr(PyObject* instance)
PyObject* call(PyObject* instance, PyObject* (TypeObjectBase::*f)(PyObject*) const)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_repr(instance);
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance);
}
catch(...)
{
@@ -109,12 +101,14 @@ static PyObject* do_instance_repr(PyObject* instance)
}
}
static int do_instance_compare(PyObject* instance, PyObject* other)
// Naming this differently allows us to use it for functions returning long on
// compilers without partial ordering
template <class R>
R int_call(PyObject* instance, R (TypeObjectBase::*f)(PyObject*) const)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_compare(instance, other);
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance);
}
catch(...)
{
@@ -123,12 +117,18 @@ static int do_instance_compare(PyObject* instance, PyObject* other)
}
}
static PyObject* do_instance_str(PyObject* instance)
// Implemented in terms of int_call, above
int call(PyObject* instance, int (TypeObjectBase::*f)(PyObject*) const)
{
return int_call(instance, f);
}
template <class A1>
PyObject* call(PyObject* instance, PyObject* (TypeObjectBase::*f)(PyObject*, A1) const, A1 a1)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_str(instance);
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance, a1);
}
catch(...)
{
@@ -137,12 +137,40 @@ static PyObject* do_instance_str(PyObject* instance)
}
}
static long do_instance_hash(PyObject* instance)
template <class A1>
int call(PyObject* instance, int (TypeObjectBase::*f)(PyObject*, A1) const, A1 a1)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_hash(instance);
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance, a1);
}
catch(...)
{
handle_exception();
return -1;
}
}
template <class A1, class A2>
PyObject* call(PyObject* instance, PyObject* (TypeObjectBase::*f)(PyObject*, A1, A2) const, A1 a1, A2 a2)
{
try
{
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance, a1, a2);
}
catch(...)
{
handle_exception();
return 0;
}
}
template <class A1, class A2>
int call(PyObject* instance, int (TypeObjectBase::*f)(PyObject*, A1, A2) const, A1 a1, A2 a2)
{
try
{
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance, a1, a2);
}
catch(...)
{
@@ -151,20 +179,70 @@ static long do_instance_hash(PyObject* instance)
}
}
static PyObject* do_instance_call(PyObject* instance, PyObject* args, PyObject* keywords)
template <class A1, class A2, class A3>
int call(PyObject* instance, int (TypeObjectBase::*f)(PyObject*, A1, A2, A3) const, A1 a1, A2 a2, A3 a3)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_call(instance, args, keywords);
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance, a1, a2, a3);
}
catch(...)
{
handle_exception();
return 0;
return -1;
}
}
int call_length_function(PyObject* instance, int (TypeObjectBase::*f)(PyObject*) const)
{
try
{
const int outcome =
(static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance);
if (outcome < 0)
{
PyErr_SetString(PyExc_ValueError, "__len__() should return >= 0");
return -1;
}
return outcome;
}
catch(...)
{
handle_exception();
return -1;
}
}
}
extern "C" {
static PyObject* do_instance_repr(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_repr);
}
static int do_instance_compare(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_compare, other);
}
static PyObject* do_instance_str(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_str);
}
static long do_instance_hash(PyObject* instance)
{
return int_call(instance, &TypeObjectBase::instance_hash);
}
static PyObject* do_instance_call(PyObject* instance, PyObject* args, PyObject* keywords)
{
return call(instance, &TypeObjectBase::instance_call, args, keywords);
}
static void do_instance_dealloc(PyObject* instance)
{
try
@@ -181,88 +259,29 @@ static void do_instance_dealloc(PyObject* instance)
static PyObject* do_instance_getattr(PyObject* instance, char* name)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_getattr(instance, name);
}
catch(...)
{
handle_exception();
return 0;
}
const char* name_ = name;
return call(instance, &TypeObjectBase::instance_getattr, name_);
}
static int do_instance_setattr(PyObject* instance, char* name, PyObject* value)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_setattr(instance, name, value);
}
catch(...)
{
handle_exception();
return -1;
}
const char* name_ = name;
return call(instance, &TypeObjectBase::instance_setattr, name_, value);
}
static int do_instance_mp_length(PyObject* instance)
{
try
{
const int outcome =
static_cast<TypeObjectBase*>(instance->ob_type)
->instance_mapping_length(instance);
if (outcome < 0)
{
PyErr_SetString(PyExc_ValueError, "__len__() should return >= 0");
return -1;
}
return outcome;
}
catch(...)
{
handle_exception();
return -1;
}
return call_length_function(instance, &TypeObjectBase::instance_mapping_length);
}
static int do_instance_sq_length(PyObject* instance)
{
try
{
const int outcome =
static_cast<TypeObjectBase*>(instance->ob_type)
->instance_sequence_length(instance);
if (outcome < 0)
{
PyErr_SetString(PyExc_ValueError, "__len__() should return >= 0");
return -1;
}
return outcome;
}
catch(...)
{
handle_exception();
return -1;
}
return call_length_function(instance, &TypeObjectBase::instance_sequence_length);
}
static PyObject* do_instance_mp_subscript(PyObject* instance, PyObject* index)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_mapping_subscript(instance, index);
}
catch(...)
{
handle_exception();
return 0;
}
return call(instance, &TypeObjectBase::instance_mapping_subscript, index);
}
static PyObject* do_instance_sq_item(PyObject* instance, int index)
@@ -293,88 +312,149 @@ static PyObject* do_instance_sq_item(PyObject* instance, int index)
static int do_instance_mp_ass_subscript(PyObject* instance, PyObject* index, PyObject* value)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_mapping_ass_subscript(instance, index, value);
}
catch(...)
{
handle_exception();
return -1;
}
return call(instance, &TypeObjectBase::instance_mapping_ass_subscript, index, value);
}
static int do_instance_sq_ass_item(PyObject* instance, int index, PyObject* value)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_sequence_ass_item(instance, index, value);
}
catch(...)
{
handle_exception();
return -1;
}
return call(instance, &TypeObjectBase::instance_sequence_ass_item, index, value);
}
static PyObject* do_instance_sq_concat(PyObject* instance, PyObject* other)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_sequence_concat(instance, other);
}
catch(...)
{
handle_exception();
return 0;
}
return call(instance, &TypeObjectBase::instance_sequence_concat, other);
}
static PyObject* do_instance_sq_repeat(PyObject* instance, int n)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_sequence_repeat(instance, n);
}
catch(...)
{
handle_exception();
return 0;
}
return call(instance, &TypeObjectBase::instance_sequence_repeat, n);
}
static PyObject* do_instance_sq_slice(
PyObject* instance, int start, int finish)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_sequence_slice(instance, start, finish);
}
catch(...)
{
handle_exception();
return 0;
}
return call(instance, &TypeObjectBase::instance_sequence_slice, start, finish);
}
static int do_instance_sq_ass_slice(
PyObject* instance, int start, int finish, PyObject* value)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_sequence_ass_slice(instance, start, finish, value);
}
catch(...)
{
handle_exception();
return -1;
}
return call(instance, &TypeObjectBase::instance_sequence_ass_slice, start, finish, value);
}
static PyObject* do_instance_nb_add(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_number_add, other);
}
static PyObject* do_instance_nb_subtract(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_number_subtract, other);
}
static PyObject* do_instance_nb_multiply(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_number_multiply, other);
}
static PyObject* do_instance_nb_divide(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_number_divide, other);
}
static PyObject* do_instance_nb_remainder(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_number_remainder, other);
}
static PyObject* do_instance_nb_divmod(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_number_divmod, other);
}
static PyObject* do_instance_nb_power(PyObject* instance, PyObject* exponent, PyObject* modulus)
{
return call(instance, &TypeObjectBase::instance_number_power, exponent, modulus);
}
static PyObject* do_instance_nb_negative(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_number_negative);
}
static PyObject* do_instance_nb_positive(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_number_positive);
}
static PyObject* do_instance_nb_absolute(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_number_absolute);
}
static int do_instance_nb_nonzero(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_number_nonzero);
}
static PyObject* do_instance_nb_invert(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_number_invert);
}
static PyObject* do_instance_nb_lshift(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_number_lshift, other);
}
static PyObject* do_instance_nb_rshift(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_number_rshift, other);
}
static PyObject* do_instance_nb_and(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_number_and, other);
}
static PyObject* do_instance_nb_xor(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_number_xor, other);
}
static PyObject* do_instance_nb_or(PyObject* instance, PyObject* other)
{
return call(instance, &TypeObjectBase::instance_number_or, other);
}
static int do_instance_nb_coerce(PyObject**instance, PyObject**other)
{
return call(*instance, &TypeObjectBase::instance_number_coerce, instance, other);
}
static PyObject* do_instance_nb_int(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_number_int);
}
static PyObject* do_instance_nb_long(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_number_long);
}
static PyObject* do_instance_nb_float(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_number_float);
}
static PyObject* do_instance_nb_oct(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_number_oct);
}
static PyObject* do_instance_nb_hex(PyObject* instance)
{
return call(instance, &TypeObjectBase::instance_number_hex);
}
}
@@ -384,24 +464,25 @@ template <std::size_t> struct category_type;
#define DECLARE_CAPABILITY_TYPE(field, sub_structure) \
template <> \
struct category_type<(offsetof(PyTypeObject, tp_as_##field))> \
struct category_type<(PY_OFFSETOF(PyTypeObject, tp_as_##field))> \
{ \
typedef sub_structure type; \
}
#define CAPABILITY(field) \
{ offsetof(PyTypeObject, tp_##field), 0, Dispatch(do_instance_##field), 0, -1 }
{ PY_OFFSETOF(PyTypeObject, tp_##field), 0, Dispatch(do_instance_##field), 0, -1 }
#define CAPABILITY2(category, field) \
{ offsetof(PyTypeObject, tp_as_##category), \
offsetof(category_type<offsetof(PyTypeObject, tp_as_##category)>::type, field), \
{ PY_OFFSETOF(PyTypeObject, tp_as_##category), \
PY_OFFSETOF(category_type<PY_OFFSETOF(PyTypeObject, tp_as_##category)>::type, field), \
Dispatch(do_instance_##field), \
sizeof(category_type<offsetof(PyTypeObject, tp_as_##category)>::type), \
offsetof(AllMethods, category) \
sizeof(category_type<PY_OFFSETOF(PyTypeObject, tp_as_##category)>::type), \
PY_OFFSETOF(AllMethods, category) \
}
DECLARE_CAPABILITY_TYPE(mapping, PyMappingMethods);
DECLARE_CAPABILITY_TYPE(sequence, PySequenceMethods);
DECLARE_CAPABILITY_TYPE(number, PyNumberMethods);
const CapabilityEntry capabilities[] = {
CAPABILITY(hash),
@@ -422,7 +503,31 @@ const CapabilityEntry capabilities[] = {
CAPABILITY2(sequence, sq_concat),
CAPABILITY2(sequence, sq_repeat),
CAPABILITY2(sequence, sq_slice),
CAPABILITY2(sequence, sq_ass_slice)
CAPABILITY2(sequence, sq_ass_slice),
CAPABILITY2(number, nb_add),
CAPABILITY2(number, nb_subtract),
CAPABILITY2(number, nb_multiply),
CAPABILITY2(number, nb_divide),
CAPABILITY2(number, nb_remainder),
CAPABILITY2(number, nb_divmod),
CAPABILITY2(number, nb_power),
CAPABILITY2(number, nb_negative),
CAPABILITY2(number, nb_positive),
CAPABILITY2(number, nb_absolute),
CAPABILITY2(number, nb_nonzero),
CAPABILITY2(number, nb_invert),
CAPABILITY2(number, nb_lshift),
CAPABILITY2(number, nb_rshift),
CAPABILITY2(number, nb_and),
CAPABILITY2(number, nb_xor),
CAPABILITY2(number, nb_or),
CAPABILITY2(number, nb_coerce),
CAPABILITY2(number, nb_int),
CAPABILITY2(number, nb_long),
CAPABILITY2(number, nb_float),
CAPABILITY2(number, nb_oct),
CAPABILITY2(number, nb_hex)
};
const std::size_t num_capabilities = PY_ARRAY_LENGTH(capabilities);
@@ -445,7 +550,7 @@ const CapabilityEntry capabilities[] = {
if (c.substructure_size == 0)
{
if (src == 0 ||
#if defined(__MWERKS__) && __MWERKS__ <= 0x4000
#if defined(__MWERKS__) && __MWERKS__ <= 0x4000 || defined(__alpha) && defined(__osf__)
((const Dispatch*)src)
#else
reinterpret_cast<const Dispatch*>(src)
@@ -621,6 +726,121 @@ int TypeObjectBase::instance_sequence_ass_slice(PyObject*, int, int, PyObject*)
return unimplemented("instance_sequence_ass_slice");
}
PyObject* TypeObjectBase::instance_number_add(PyObject*, PyObject*) const
{
return unimplemented("instance_number_add");
}
PyObject* TypeObjectBase::instance_number_subtract(PyObject*, PyObject*) const
{
return unimplemented("instance_number_subtract");
}
PyObject* TypeObjectBase::instance_number_multiply(PyObject*, PyObject*) const
{
return unimplemented("instance_number_multiply");
}
PyObject* TypeObjectBase::instance_number_divide(PyObject*, PyObject*) const
{
return unimplemented("instance_number_divide");
}
PyObject* TypeObjectBase::instance_number_remainder(PyObject*, PyObject*) const
{
return unimplemented("instance_number_remainder");
}
PyObject* TypeObjectBase::instance_number_divmod(PyObject*, PyObject*) const
{
return unimplemented("instance_number_divmod");
}
PyObject* TypeObjectBase::instance_number_power(PyObject*, PyObject*, PyObject*) const
{
return unimplemented("instance_number_divmod");
}
PyObject* TypeObjectBase::instance_number_negative(PyObject*) const
{
return unimplemented("instance_number_negative");
}
PyObject* TypeObjectBase::instance_number_positive(PyObject*) const
{
return unimplemented("instance_number_positive");
}
PyObject* TypeObjectBase::instance_number_absolute(PyObject*) const
{
return unimplemented("instance_number_absolute");
}
int TypeObjectBase::instance_number_nonzero(PyObject*) const
{
return unimplemented("instance_number_nonzero");
}
PyObject* TypeObjectBase::instance_number_invert(PyObject*) const
{
return unimplemented("instance_number_invert");
}
PyObject* TypeObjectBase::instance_number_lshift(PyObject*, PyObject*) const
{
return unimplemented("instance_number_lshift");
}
PyObject* TypeObjectBase::instance_number_rshift(PyObject*, PyObject*) const
{
return unimplemented("instance_number_rshift");
}
PyObject* TypeObjectBase::instance_number_and(PyObject*, PyObject*) const
{
return unimplemented("instance_number_and");
}
PyObject* TypeObjectBase::instance_number_xor(PyObject*, PyObject*) const
{
return unimplemented("instance_number_xor");
}
PyObject* TypeObjectBase::instance_number_or(PyObject*, PyObject*) const
{
return unimplemented("instance_number_or");
}
int TypeObjectBase::instance_number_coerce(PyObject*, PyObject**, PyObject**) const
{
return unimplemented("instance_number_coerce");
}
PyObject* TypeObjectBase::instance_number_int(PyObject*) const
{
return unimplemented("instance_number_int");
}
PyObject* TypeObjectBase::instance_number_long(PyObject*) const
{
return unimplemented("instance_number_long");
}
PyObject* TypeObjectBase::instance_number_float(PyObject*) const
{
return unimplemented("instance_number_float");
}
PyObject* TypeObjectBase::instance_number_oct(PyObject*) const
{
return unimplemented("instance_number_oct");
}
PyObject* TypeObjectBase::instance_number_hex(PyObject*) const
{
return unimplemented("instance_number_hex");
}
TypeObjectBase::~TypeObjectBase()
{
}

View File

@@ -30,6 +30,8 @@
namespace py {
class InstanceHolderBase;
class TypeObjectBase : public PythonType
{
public:
@@ -42,7 +44,14 @@ class TypeObjectBase : public PythonType
hash, call, str, getattr, setattr, compare, repr,
mapping_length, mapping_subscript, mapping_ass_subscript,
sequence_length, sequence_item, sequence_ass_item,
sequence_concat, sequence_repeat, sequence_slice, sequence_ass_slice
sequence_concat, sequence_repeat, sequence_slice, sequence_ass_slice,
number_add, number_subtract, number_multiply, number_divide,
number_remainder, number_divmod, number_power, number_negative,
number_positive, number_absolute, number_nonzero, number_invert,
number_lshift, number_rshift, number_and, number_xor, number_or,
number_coerce, number_int, number_long, number_float, number_oct,
number_hex
};
void enable(Capability);
@@ -75,7 +84,31 @@ class TypeObjectBase : public PythonType
virtual PyObject* instance_sequence_slice(PyObject* instance, int start, int finish) const;
virtual int instance_sequence_ass_item(PyObject* instance, int n, PyObject* value) const;
virtual int instance_sequence_ass_slice(PyObject* instance, int start, int finish, PyObject* value) const;
public: // Callbacks for number methods
virtual PyObject* instance_number_add(PyObject*, PyObject*) const;
virtual PyObject* instance_number_subtract(PyObject*, PyObject*) const;
virtual PyObject* instance_number_multiply(PyObject*, PyObject*) const;
virtual PyObject* instance_number_divide(PyObject*, PyObject*) const;
virtual PyObject* instance_number_remainder(PyObject*, PyObject*) const;
virtual PyObject* instance_number_divmod(PyObject*, PyObject*) const;
virtual PyObject* instance_number_power(PyObject*, PyObject*, PyObject*) const;
virtual PyObject* instance_number_negative(PyObject*) const;
virtual PyObject* instance_number_positive(PyObject*) const;
virtual PyObject* instance_number_absolute(PyObject*) const;
virtual int instance_number_nonzero(PyObject*) const;
virtual PyObject* instance_number_invert(PyObject*) const;
virtual PyObject* instance_number_lshift(PyObject*, PyObject*) const;
virtual PyObject* instance_number_rshift(PyObject*, PyObject*) const;
virtual PyObject* instance_number_and(PyObject*, PyObject*) const;
virtual PyObject* instance_number_xor(PyObject*, PyObject*) const;
virtual PyObject* instance_number_or(PyObject*, PyObject*) const;
virtual int instance_number_coerce(PyObject*, PyObject**, PyObject**) const;
virtual PyObject* instance_number_int(PyObject*) const;
virtual PyObject* instance_number_long(PyObject*) const;
virtual PyObject* instance_number_float(PyObject*) const;
virtual PyObject* instance_number_oct(PyObject*) const;
virtual PyObject* instance_number_hex(PyObject*) const;
};
template <class T>

View File

@@ -13,6 +13,7 @@
# include "pyconfig.h"
# include "pyptr.h"
# include "boost/operators.hpp"
# include <utility>
namespace py {
@@ -36,8 +37,31 @@ class Tuple : public Object
public:
Tuple(std::size_t n = 0);
explicit Tuple(Ptr p);
template <class First, class Second>
Tuple(const std::pair<First,Second>& x)
: Object(Ptr(PyTuple_New(2)))
{
set_item(0, Ptr(to_python(x.first)));
set_item(1, Ptr(to_python(x.second)));
}
Tuple(const Ptr* start, const Ptr* finish); // not yet implemented.
template <class First, class Second>
Tuple(const First& first, const Second& second)
: Object(Ptr(PyTuple_New(2)))
{
set_item(0, Ptr(to_python(first)));
set_item(1, Ptr(to_python(second)));
}
template <class First, class Second, class Third>
Tuple(const First& first, const Second& second, const Third& third)
: Object(Ptr(PyTuple_New(3)))
{
set_item(0, Ptr(to_python(first)));
set_item(1, Ptr(to_python(second)));
set_item(2, Ptr(to_python(third)));
}
static PyTypeObject* type_object();
static bool accepts(Ptr p);

View File

@@ -1,2 +0,0 @@
#define DEBUG_PYTHON
#include "objects.cpp"

139
overloading.html Normal file
View File

@@ -0,0 +1,139 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
"http://www.w3.org/TR/REC-html40/strict.dtd">
<title>
Function Overloading
</title>
<div>
<h1>
<img width="277" height="86" id="_x0000_i1025" align="center"
src="c++boost.gif" alt= "c++boost.gif (8819 bytes)">Function Overloading
</h1>
<h2>An Example</h2>
<p>
To expose overloaded functions in Python, simply <code>def()</code> each
one with the same Python name:
<blockquote>
<pre>
inline int f1() { return 3; }
inline int f2(int x) { return x + 1; }
class X {
public:
X() : m_value(0) {}
X(int n) : m_value(n) {}
int value() const { return m_value; }
void value(int v) { m_value = v; }
private:
int m_value;
};
...
void initoverload_demo()
{
    try
    {
py::Module overload_demo("overload_demo");
// Overloaded functions at module scope
overload_demo.def(f1, "f");
overload_demo.def(f2, "f");
py::ClassWrapper&lt;X&gt; x_class(overload_demo, "X");
// Overloaded constructors
x_class.def(py::Constructor&lt;&gt;());
x_class.def(py::Constructor&lt;int&gt;());
// Overloaded member functions
x_class.def((int (X::*)() const)&amp;X::value, "value");
x_class.def((void (X::*)(int))&amp;X::value, "value");
...
</pre>
</blockquote>
<p>
Now in Python:
<blockquote>
<pre>
>>> from overload_demo import *
>>> x0 = X()
>>> x1 = X(1)
>>> x0.value()
0
>>> x1.value()
1
>>> x0.value(3)
>>> x0.value()
3
>>> X('hello')
TypeError: No overloaded functions match (X, string). Candidates are:
void (*)()
void (*)(int)
>>> f()
3
>>> f(4)
5
</pre>
</blockquote>
<h2>Discussion</h2>
<p>
Notice that overloading in the Python module was produced three ways:<ol>
<li>by combining the non-overloaded C++ functions <code>int f1()</code>
and <code>int f2(int)</code> and exposing them as <code>f</code> in Python.
<li>by exposing the overloaded constructors of <code>class X</code>
<li>by exposing the overloaded member functions <code>X::value</code>.
</ol>
<p>
Techniques 1. and 3. above are really alternatives. In case 3, you need
to form a pointer to each of the overloaded functions. The casting
syntax shown above is one way to do that in C++. Case 1 does not require
complicated-looking casts, but may not be viable if you can't change
your C++ interface. N.B. There's really nothing unsafe about casting an
overloaded (member) function address this way: the compiler won't let
you write it at all unless you get it right.
<h2>An Alternative to Casting</h2>
<p>
This approach is not neccessarily better, but may be preferable for some
people who have trouble writing out the types of (member) function
pointers or simply prefer to avoid all casts as a matter of principle:
<blockquote>
<pre>
// Forwarding functions for X::value
inline void set_x_value(X&amp; self, int v) { self.value(v); }
inline int get_x_value(X&amp; self) { return self.value(); }
...
// Overloaded member functions
x_class.def(set_x_value, "value");
x_class.def(get_x_value, "value");
</pre>
</blockquote>
<p>Here we are taking advantage of the ability to expose C++ functions at
namespace scope as Python member functions.
<h2>Overload Resolution</h2>
<p>
The function overload resolution mechanism in py_cpp works as
follows:<ul>
<li>Attribute lookup for extension classes proceeds in the usual way. When a
class is found which has a matching attribute, only functions overloaded
in the context of that class are candidates for overload resolution.
<li>Within a name-space context (extension class or module), overloaded
functions are tried in the same order they were
<code>def()</code>ed. The first function whose signature can be made to
match each argument passed is the one which is ultimately called.
</ul>
<p>
Prev: <a href="overloading.html">Function Overloading</a>
Next: <a href="special.html">Special Method Names</a>
Up: <a href="py_cpp.html">Top</a>
<p>
&copy; 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.
<p>
Updated: Oct 15, 2000
</div>

175
overriding.html Normal file
View File

@@ -0,0 +1,175 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<title>
Overridable Virtual Functions
</title>
<h1>
<img src="c++boost.gif" alt="c++boost.gif (8819 bytes)" align="center"
width="277" height="86">
</h1>
<h1>
Overridable Virtual Functions
</h1>
<p>
In the <a href="example1.html">previous example</a> 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 <em>behave virtually</em>. Of course, the first
step if we want it to act like a virtual function when called from our
C++ code, is to <em>make</em> it virtual:
<blockquote>
<pre>
class world
{
...
<strong>virtual</strong> const char* get() const { return "hi, world"; }
...
};
</pre>
</blockquote>
<p>
Then we'll need a derived class<a href="#why_derived">*</a> to help us
dispatch the call to Python:
<blockquote>
<pre>
struct world_callback : hello::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&lt;const char*&gt;::call_method(m_self, "get");
}
// <a name=
"default_implementation">Something Python can call</a> in case there is no override of get()
const char* default_get() const
{ return this-&gt;hello::world::get(); }
private:
PyObject* m_self; // A way to hold onto the Python object
};
</pre>
</blockquote>
<p>
Finally, we add <code>world_callback</code> to the <code>
ClassWrapper&lt;&gt;</code> declaration in our module initialization
function:
<blockquote>
<pre>
// Create the <a name=
"world_class">Python type object</a> for our extension class
py::ClassWrapper&lt;hello::world<strong>,world_callback&gt;</strong> world_class(hello, "world");
...
</pre>
</blockquote>
<p>
...and when we define the function, we must tell py_cpp about the default
implementation:
<blockquote>
<pre>
// Add a virtual member function
world_class.def(&amp;hello::world::get, "get", &amp;world_callback::default_get);
</pre>
</blockquote>
<p>
Now our subclass of <code>hello.world</code> behaves as expected:
<p>
<blockquote>
<pre>
&gt;&gt;&gt; class my_subclass(hello.world):
... def get(self):
... return 'hello, world'
...
&gt;&gt;&gt; hello.length(my_subclass())
12
</pre>
</blockquote>
<p>
<a name="why_derived">*</a>You may ask, "Why do we need this derived
class? This could have been designed so that everything gets done right
inside of <code>hello::world</code>." One of the goals of py_cpp 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.
<h2>
Pure Virtual Functions
</h2>
<p>
A pure virtual function with no implementation is actually a lot easier
to deal with than a virtual function with a default implementation. First
of all, you obviously don't need to <a href="#default_implementation">
supply a default implementation</a>. Secondly, you don't need to call
<code>def()</code> on the <code>ExtensionClass&lt;&gt;</code> instance
for the virtual function. In fact, you wouldn't <em>want</em> to: if the
corresponding attribute on the Python class stays undefined, you'll get
an <code>AttributeError</code> in Python when you try to call the
function, indicating that it should have been implemented. For example:
<blockquote>
<pre>
struct baz
{
<strong>virtual</strong> void pure(int) = 0;
};
struct baz_callback
{
void pure(int x) { py::Callback&lt;void&gt;::call_method(m_self, "pure", x); }
};
extern "C"
#ifdef _WIN32
__declspec(dllexport)
#endif
initfoobar()
{
try
{
py::Module foobar("foobar");
py::ClassWrapper&lt;baz,baz_callback&gt; baz_class("baz");
baz_class.def(&amp;baz::pure, "pure");
}
catch(...)
{
py::handle_exception(); // Deal with the exception for Python
}
}
</pre>
</blockquote>
<p>
Now in Python:
<blockquote>
<pre>
&gt;&gt;&gt; from foobar import baz
&gt;&gt;&gt; x = baz()
&gt;&gt;&gt; x.pure()
Traceback (innermost last):
File "&lt;stdin&gt;", line 1, in ?
AttributeError: pure
&gt;&gt;&gt; class mumble(baz):
... def pure(self, z): pass
...
&gt;&gt;&gt; y = mumble()
&gt;&gt;&gt; y.pure()
&gt;&gt;&gt;
</pre>
</blockquote>
<p>
Prev: <a href="example1.html">A Simple Example Using py_cpp</a> Next: <a
href="under-the-hood.html">A Peek Under the Hood</a> Up: <a href=
"py_cpp.html">Top</a>
<p>
&copy; 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.
<p>
Updated: Sept 30, 2000

23
py.cpp
View File

@@ -7,7 +7,11 @@
// producing this work.
#include "py.h"
#include <boost/cast.hpp>
#include <boost/config.hpp>
#include <typeinfo>
#ifndef BOOST_NO_LIMITS
# include <boost/cast.hpp>
#endif
namespace py {
@@ -82,11 +86,19 @@ T integer_from_python(PyObject* p, py::Type<T>)
{
const long long_result = from_python(p, py::Type<long>());
#ifndef BOOST_NO_LIMITS
try
{
return boost::numeric_cast<T>(long_result);
}
catch(const boost::bad_numeric_cast&)
#else
if (static_cast<T>(long_result) == long_result)
{
return static_cast<T>(long_result);
}
else
#endif
{
char buffer[256];
const char message[] = "%ld out of range for %s";
@@ -103,12 +115,17 @@ template <class T>
PyObject* integer_to_python(T value)
{
long value_as_long;
#ifndef BOOST_NO_LIMITS
try
{
value_as_long = boost::numeric_cast<T>(value);
value_as_long = boost::numeric_cast<long>(value);
}
catch(const boost::bad_numeric_cast&)
#else
value_as_long = static_cast<long>(value);
if (value_as_long != value)
#endif
{
const char message[] = "value out of range for Python int";
PyErr_SetString(PyExc_ValueError, message);

5
py.h
View File

@@ -165,6 +165,11 @@ PyObject* to_python(boost::shared_ptr<T> p)
// inline implementations
//
inline PyObject* to_python(double d)
{
return PyFloat_FromDouble(d);
}
inline PyObject* to_python(long l)
{
return PyInt_FromLong(l);

View File

@@ -1,4 +0,0 @@
TargetName=py_cpp
TargetType=lib
SourceFiles=extclass.cpp init_function.cpp subclass.cpp functions.cpp objects.cpp
SourceFiles+=module.cpp newtypes.cpp py.cpp

122
py_cpp.html Normal file
View File

@@ -0,0 +1,122 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
"http://www.w3.org/TR/REC-html40/strict.dtd">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<title>
py_cpp Python/C++ binding documentation
</title>
<h1>
<img src="c++boost.gif" alt="c++boost.gif (8819 bytes)" width="277"
align="center" height="86"> py_cpp<a href="#naming_contest">*</a>
</h1>
<p>
Py_cpp is a system for quickly and easily interfacing C++ code with <a
href="http:www.python.org">Python</a> 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
<em>should</em> simply "reflect" your C++ classes and functions into
Python.
<p>
The source code for py_cpp, including a MSVC demo project is available
<a href="py_cpp_20001019.zip">here</a>. It has been tested against Python
1.5.2 with GCC 2.95.2, Metrowerks CodeWarrior Pro6 and with Microsoft
Visual C++ 6 sp4 using both the <a href="http://www.stlport.org">
STLport</a> standard library implementation <em>and</em> the library
implementation which ships with the compiler. It has also been tested
against Python 2.0c1 with MSVC++ 6sp4 by Alex Martelli.
Py_cpp requires the <a href="http://www.boost.org">Boost</a> libraries,
and is currently under formal review on <a href=
"http://www.egroups.com/list/boost">the boost mailing list</a> for
acceptance into boost.
<p>
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.
<h2>
Table of Contents
</h2>
<ol>
<li>
<a href="extending.html">A Brief Introduction to writing Python
extension modules</a>
<li>
<a href="comparisons.html">Comparisons between py_cpp and other systems
for extending Python</a>
<li>
<a href="example1.html">A Simple Example Using py_cpp</a>
<li>
<a href="overriding.html">Overridable Virtual Functions</a>
<li>
<a href="overloading.html">Function Overloading</a>
<li>
<a href="special.html">Special Method Name Support</a>
<li>
<a href="under-the-hood.html">A Peek Under the Hood</a>
</ol>
<p>
More sophisticated examples, including examples which demonstrate that
these ExtensionClasses support some of Python's "special" member
functions (e.g. <code>__getattr__(self, name)</code>), are given in
<code>extclass_demo.cpp</code>, <code> extclass_demo.h</code>, and <code>
test_extclass.py</code> in the <a href="py_cpp.tgz">source code
archive</a>. There's much more here, and much more documentation to
come...
<p>
Questions should be directed to <a href=
"http://www.egroups.com/list/boost">the boost mailing list</a> or to <a
href="mailto:abrahams@mediaone.net"> David Abrahams</a>, the primary
author and maintainer.
<h3>
Naming Contest
</h3>
<p>
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
naming contest. First prize? You get to pick the name&lt;0.2wink&gt; and
you will be credited in the documentation. Names that have been suggested
so far include:
<ul>
<li>
Py++
<li>
Python++
<li>
Coil
<li>
SnakeSkin
<li>
CCCP - <strong>C</strong>onvert <strong>C</strong>++ <strong>
C</strong>lasses to <strong>P</strong>ython
<li>
C<sup>3</sup>PO - <strong>C</strong>onvert <strong>C</strong>++
<strong>C</strong>lasses to <strong>P</strong>ython <strong>
O</strong>bjects
<li>
PALIN - <strong>P</strong>ython <strong>
A</strong>ugmented-<strong>L</strong>anguage <strong>
IN</strong>tegration
<li>
C-thru
<li>
SeamlessC
<li>
BorderCrossing
</ul>
Please <a href="http://www.egroups.com/list/boost">post</a> or send <a
href="http:mailto:abrahams@mediaone.net">me</a> your suggestions!<br>
<br>
<p>
&copy; 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.
<p>
Updated: Oct 18, 2000

Binary file not shown.

View File

@@ -1,4 +0,0 @@
TargetName=py_cpp_d
TargetType=lib
SourceFiles=extclass_d.cpp init_function_d.cpp subclass_d.cpp functions_d.cpp objects_d.cpp
SourceFiles+=module_d.cpp newtypes_d.cpp py_d.cpp

View File

@@ -1,21 +0,0 @@
ifndef PY_CPPHELP_MAK_INCLUDED
PY_CPPHELP_MAK_INCLUDED=1
ifndef DONT_BUILD_IMPORTS
PY_CPP_DIR=$(ROOT_DIR)/py_cpp
PY_CPP_LIB=$(PY_CPP_DIR)/$(OBJDIR)/py_cpp.lib
PY_CPP_D_LIB=$(PY_CPP_DIR)/$(OBJDIR)/py_cpp_d.lib
$(PY_CPP_LIB) : FORCE
$(RECURSIVE_MAKE) -C $(PY_CPP_DIR) py_cpp
$(PY_CPP_D_LIB) : FORCE
$(RECURSIVE_MAKE) -C $(PY_CPP_DIR) py_cpp_d
# end ifndef DONT_BUILD_IMPORTS
endif
# end ifndef PY_CPPHELP_MAK_INCLUDED
endif

View File

@@ -1,2 +0,0 @@
#define DEBUG_PYTHON
#include "py.cpp"

View File

@@ -19,7 +19,7 @@
# define PY_INLINE_FRIEND ::py
# endif
# if !defined(__GNUC__) && !defined(__MWERKS__) && !defined(__BORLANDC__) && defined(_MSC_VER)
# if defined(BOOST_MSVC)
# define PY_COMPILER_IS_MSVC 1
# if _MSC_VER <= 1200
# define PY_MSVC6_OR_EARLIER 1
@@ -29,6 +29,17 @@
# endif
// Work around the broken library implementation/strict ansi checking on some
// EDG-based compilers (e.g. alpha), which incorrectly warn that the result of
// offsetof() is not an integer constant expression.
# if defined(__DECCXX_VER) && __DECCXX_VER <= 60290024
# define PY_OFFSETOF(s_name, s_member) \
((size_t)__INTADDR__(&(((s_name *)0)->s_member)))
# else
# define PY_OFFSETOF(s_name, s_member) \
offsetof(s_name, s_member)
# endif
// The STLport puts all of the standard 'C' library names in std (as far as the
// user is concerned), but without it you need a fix if you're using MSVC.
# if defined(PY_MSVC6_OR_EARLIER) && !defined(__STLPORT)

401
special.html Normal file
View File

@@ -0,0 +1,401 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
"http://www.w3.org/TR/REC-html40/strict.dtd">
<title>
Special Method Name Support
</title>
<div>
<h1>
<img width="277" height="86" id="_x0000_i1025" align="center" src=
"c++boost.gif" alt="c++boost.gif (8819 bytes)">Special Method Name
Support
</h1>
<h2>
Overview
</h2>
<p>
Py_cpp supports all of the standard <a href=
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/specialnames.html">
special method names</a> supported by real Python class instances <em>
except:</em>
<ul>
<li><a href=
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/customization.html">
<code>__del__</code></a> (an oversight, to be corrected soon - but
don't worry, your underlying C++ objects are still properly destroyed!)
<li>the <code>__r<em>&lt;name&gt;</em>__</code> "reversed operand" <a href=
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/numeric-types.html">
numeric methods</a>, and
<li><code>__complex__</code>
</ul>
(more on the latter two <a href="#reverse">below</a>). So, for example, we can wrap a
<code>std::map&lt;std::size_t,std::string&gt;</code> as follows:
<h2>
Example
</h2>
<blockquote>
<pre>
typedef std::map&lt;std::size_t, std::string&gt; StringMap;
// A helper function for dealing with errors. Throw a Python exception
// if p == m.end().
void throw_key_error_if_end(
const StringMap&amp; m,
StringMap::const_iterator p,
std::size_t key)
{
if (p == m.end())
{
PyErr_SetObject(PyExc_KeyError, py::converters::to_python(key));
throw py::ErrorAlreadySet();
}
}
// Define some simple wrapper functions which match the Python protocol
// for __getitem__, __setitem__, and __delitem__. Just as in Python, a
// free function with a "self" first parameter makes a fine class method.
const std::string&amp; get_item(const StringMap&amp; self, std::size_t key)
{
const StringMap::const_iterator p = self.find(key);
throw_key_error_if_end(self, p, key);
return p-&gt;second;
}
// Sets the item corresponding to key in the map.
void StringMapPythonClass::set_item(StringMap&amp; self, std::size_t key, const std::string&amp; value)
{
self[key] = value;
}
// Deletes the item corresponding to key from the map.
void StringMapPythonClass::del_item(StringMap&amp; self, std::size_t key)
{
const StringMap::iterator p = self.find(key);
throw_key_error_if_end(self, p, key);
self.erase(p);
}
ClassWrapper&lt;StringMap&gt; string_map(my_module, "StringMap");
string_map.def(py::Constructor&lt;&gt;());
string_map.def(&amp;StringMap::size, "__len__");
string_map.def(get_item, "__getitem__");
string_map.def(set_item, "__setitem__");
string_map.def(del_item, "__delitem__");
</pre>
</blockquote>
<p>
Then in Python:
<blockquote>
<pre>
&gt;&gt;&gt; m = StringMap()
&gt;&gt;&gt; m[1]
Traceback (innermost last):
File "&lt;stdin&gt;", line 1, in ?
KeyError: 1
&gt;&gt;&gt; m[1] = 'hello'
&gt;&gt;&gt; m[1]
'hello'
&gt;&gt;&gt; del m[1]
&gt;&gt;&gt; m[1] # prove that it's gone
Traceback (innermost last):
File "&lt;stdin&gt;", line 1, in ?
KeyError: 1
&gt;&gt;&gt; del m[2]
Traceback (innermost last):
File "&lt;stdin&gt;", line 1, in ?
KeyError: 2
&gt;&gt;&gt; len(m)
0
&gt;&gt;&gt; m[3] = 'farther'
&gt;&gt;&gt; len(m)
1
</pre>
</blockquote>
<h2>
<a name="getter_setter">Getters and Setters</a>
</h2>
<p>
Py_cpp extension classes support some additional "special method"
protocols not supported by built-in Python classes. Because writing
<code>__getattr__</code>, <code> __setattr__</code>, and
<code>__delattr__</code> functions can be tedious in the common case
where the attributes being accessed are known statically, py_cpp checks
the special names
<ul>
<li>
<code>__getattr__<em>&lt;name&gt;</em>__</code>
<li>
<code>__setattr__<em>&lt;name&gt;</em>__</code>
<li>
<code>__delattr__<em>&lt;name&gt;</em>__</code>
</ul>
to provide functional access to the attribute <em>&lt;name&gt;</em>. This
facility can be used from C++ or entirely from Python. For example, the
following shows how we can implement a "computed attribute" in Python:
<blockquote>
<pre>
&gt;&gt;&gt; class Range(AnyPy_cppExtensionClass):
... def __init__(self, start, end):
... self.start = start
... self.end = end
... def __getattr__length__(self):
... return self.end - self.start
...
&gt;&gt;&gt; x = Range(3, 9)
&gt;&gt;&gt; x.length
6
</pre>
</blockquote>
<h2>
Direct Access to Data Members
</h2>
<p>
Py_cpp uses the special <code>
__xxxattr__<em>&lt;name&gt;</em>__</code> functionality described above
to allow direct access to data members through the following special
functions on <code>ClassWrapper&lt;&gt;</code> and <code>
ExtensionClass&lt;&gt;</code>:
<ul>
<li>
<code>def_getter(<em>pointer-to-member</em>, <em>name</em>)</code> //
read access to the member via attribute <em>name</em>
<li>
<code>def_setter(<em>pointer-to-member</em>, <em>name</em>)</code> //
write access to the member via attribute <em>name</em>
<li>
<code>def_readonly(<em>pointer-to-member</em>, <em>name</em>)</code>
// read-only access to the member via attribute <em>name</em>
<li>
<code>def_read_write(<em>pointer-to-member</em>, <em>
name</em>)</code> // read/write access to the member via attribute
<em>name</em>
</ul>
<p>
Note that the first two functions, used alone, may produce surprising
behavior. For example, when <code>def_getter()</code> is used, the
default functionality for <code>setattr()</code> and <code>
delattr()</code> remains in effect, operating on items in the extension
instance's name-space (i.e., its <code>__dict__</code>). For that
reason, you'll usually want to stick with <code>def_readonly</code> and
<code>def_read_write</code>.
<p>
For example, to expose a <code>std::pair&lt;int,long&gt;</code> we
might write:
<blockquote>
<pre>
typedef std::pair&lt;int,long&gt; Pil;
int first(const Pil&amp; x) { return x.first; }
long second(const Pil&amp; x) { return x.second; }
...
my_module.def(first, "first");
my_module.def(second, "second");
ClassWrapper&lt;Pil&gt; pair_int_long(my_module, "Pair");
pair_int_long.def(py::Constructor&lt;&gt;());
pair_int_long.def(py::Constructor&lt;int,long&gt;());
pair_int_long.def_read_write(&amp;Pil::first, "first");
pair_int_long.def_read_write(&amp;Pil::second, "second");
</pre>
</blockquote>
<p>
Now your Python class has attributes <code>first</code> and <code>
second</code> which, when accessed, actually modify or reflect the
values of corresponding data members of the underlying C++ object. Now
in Python:
<blockquote>
<pre>
&gt;&gt;&gt; x = Pair(3,5)
&gt;&gt;&gt; x.first
3
&gt;&gt;&gt; x.second
5
&gt;&gt;&gt; x.second = 8
&gt;&gt;&gt; x.second
8
&gt;&gt;&gt; second(x) # Prove that we're not just changing the instance __dict__
8
</pre>
</blockquote>
<h2>
<a name="numerics">Numeric Method Support</a>
</h2>
<p>
Py_cpp supports the following <a href=
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/numeric-types.html">
Python special numeric method names</a>:
<p>
<table summary="special numeric methods" cellpadding="5" border="1">
<thead>
<tr>
<td>
Name
<td>
Notes
<tr>
<td>
<code>__add__(self,&nbsp;other)</code>
<td>
<code>operator+(const T&amp;,&nbsp;const T&amp;)</code>
<tr>
<td>
<code>__sub__(self,&nbsp;other)</code>
<td>
<code>operator-(const T&amp;,&nbsp;const T&amp;)</code>
<tr>
<td>
<code>__mul__(self,&nbsp;other)</code>
<td>
<code>operator*(const T&amp;,&nbsp;const T&amp;)</code>
<tr>
<td>
<code>__div__(self,&nbsp;other)</code>
<td>
<code>operator/(const T&amp;,&nbsp;const T&amp;)</code>
<tr>
<td>
<code>__mod__(self,&nbsp;other)</code>
<td>
<code>operator%(const T&amp;,&nbsp;const T&amp;)</code>
<tr>
<td>
<code>__divmod__(self,&nbsp;other)</code>
<td>
return a <code> py::Tuple</code> initialized with <code>
(</code><em>quotient</em><code>,</code> <em>
remainder</em><code>)</code>.
<tr>
<td>
<code>__pow__(self,&nbsp;other&nbsp;[,&nbsp;modulo])</code>
<td>
use <a href="overloading.html">overloading</a> to support both
forms of __pow__
<tr>
<td>
<code>__lshift__(self,&nbsp;other)</code>
<td>
<code>operator&lt;&lt;(const T&amp;,&nbsp;const T&amp;)</code>
<tr>
<td>
<code>__rshift__(self,&nbsp;other)</code>
<td>
<code>operator&gt;&gt;(const T&amp;,&nbsp;const T&amp;)</code>
<tr>
<td>
<code>__and__(self,&nbsp;other)</code>
<td>
<code>operator&amp;(const T&amp;,&nbsp;const T&amp;)</code>
<tr>
<td>
<code>__xor__(self,&nbsp;other)</code>
<td>
<code>operator^(const T&amp;,&nbsp;const T&amp;)</code>
<tr>
<td>
<code>__or__(self,&nbsp;other)</code>
<td>
<code>operator|(const T&amp;,&nbsp;const T&amp;)</code>
<tr>
<td>
<code>__neg__(self)</code>
<td>
<code>operator-(const T&amp;)</code> (unary negation)
<tr>
<td>
<code>__pos__(self)</code>
<td>
<code>operator+(const T&amp;)</code> (identity)
<tr>
<td>
<code>__abs__(self)</code>
<td>
Called to implement the built-in function abs()
<tr>
<td>
<code>__invert__(self)</code>
<td>
<code>operator~(const T&amp;)</code>
<tr>
<td>
<code>__int__(self)</code>
<td>
<code>operator long() const</code>
<tr>
<td>
<code>__long__(self)</code>
<td>
Should return a Python <code>long</code> object. Can be
implemented with <code>PyLong_FromLong(<em>value</em>)</code>,
for example.
<tr>
<td>
<code>__float__(self)</code>
<td>
<code>operator double() const</code>
<tr>
<td>
<code>__oct__(self)</code>
<td>
Called to implement the built-in function oct(). Should return a
string value.
<tr>
<td>
<code>__hex__(self)</code>
<td>
Called to implement the built-in function hex(). Should return a
string value.
<tr>
<td>
<code>__coerce__(self,&nbsp;other)</code>
<td>
Should return a Python 2-<em>tuple</em> (C++ code may return a
<code>py::Tuple</code>) where the elements represent the values
of <code> self</code> and <code>other</code> converted to the
same type.
</table>
<h2><a name="reverse">Where are the <code>__r</code><i>&lt;name&gt;</i><code>__</code>
functions?</a></h2>
<p>
At first we thought that supporting <code>__radd__</code> and its ilk would be
impossible, since Python doesn't supply any direct support and in fact
implements a special case for its built-in class instances. <a
href="http://starship.python.net/crew/arcege/extwriting/pyextnum.html">This
article</a> gives a pretty good overview of the direct support for numerics
that Python supplies for extension types. We've since discovered that it can
be done, but there are some pretty convincing <a
href="http://starship.python.net/crew/lemburg/CoercionProposal.html">arguments</a>
out there that this arrangement is less-than-ideal. Instead of supplying a
sub-optimal solution for the sake of compatibility with built-in Python
classes, we're doing the neccessary research so we can "do it right". This
will also give us a little time to hear from users about what they want. The
direction we're headed in is based on the idea of <a
href="http://www.sff.net/people/neelk/open-source/Multimethod.py">multimethods</a>
rather than on trying to find a coercion function bound to one of the
arguments.
<h3>And what about <code>__complex__</code>?</h3>
<p>That, dear reader, is one problem we don't know how to solve. The Python
source contains the following fragment, indicating the special-case code really
is hardwired:
<blockquote>
<pre>
/* XXX Hack to support classes with __complex__ method */
if (PyInstance_Check(r)) { ...
</pre>
</blockquote>
<p>
Previous: <a href="overloading.html">Function Overloading</a> Next: <a
href="under-the-hood.html">A Peek Under the Hood</a> Up: <a href=
"py_cpp.html">Top</a>
<p>
&copy; 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.
<p>
Updated: Oct 19, 2000
</div>

View File

@@ -38,12 +38,8 @@ PyObject* Instance::getattr(const char* name, bool use_special_function)
}
else
{
// Suspend the error while we try special methods method (if any).
#if 0
SuspendError suspended_error(SuspendError::discard_new_error);
#else
// Clear the error while we try special methods method (if any).
PyErr_Clear();
#endif
// First we try the special method that comes from concatenating
// "__getattr__" and <name> and 2 trailing underscores. This is an
@@ -67,15 +63,11 @@ PyObject* Instance::getattr(const char* name, bool use_special_function)
}
// If there is no such method, throw now.
#if 0
suspended_error.throw_if_error();
#else
if (PyErr_Occurred())
{
PyErr_SetString(PyExc_AttributeError, name);
throw ErrorAlreadySet();
}
#endif
// Take ownership of the method
Ptr owner(getattr_method);
@@ -224,6 +216,131 @@ void Instance::set_slice(int start, int finish, PyObject* value)
Callback<void>::call_method(this, "__setslice__", start, finish, value);
}
PyObject* Instance::add(PyObject* other)
{
return Callback<PyObject*>::call_method(this, "__add__", other);
}
PyObject* Instance::subtract(PyObject* other)
{
return Callback<PyObject*>::call_method(this, "__sub__", other);
}
PyObject* Instance::multiply(PyObject* other)
{
return Callback<PyObject*>::call_method(this, "__mul__", other);
}
PyObject* Instance::divide(PyObject* other)
{
return Callback<PyObject*>::call_method(this, "__div__", other);
}
PyObject* Instance::remainder(PyObject* other)
{
return Callback<PyObject*>::call_method(this, "__mod__", other);
}
PyObject* Instance::divmod(PyObject* other)
{
return Callback<PyObject*>::call_method(this, "__divmod__", other);
}
PyObject* Instance::power(PyObject* exponent, PyObject* modulus)
{
if (as_object(modulus->ob_type) == Py_None)
return Callback<PyObject*>::call_method(this, "__pow__", exponent);
else
return Callback<PyObject*>::call_method(this, "__pow__", exponent, modulus);
}
PyObject* Instance::negative()
{
return Callback<PyObject*>::call_method(this, "__neg__");
}
PyObject* Instance::positive()
{
return Callback<PyObject*>::call_method(this, "__pos__");
}
PyObject* Instance::absolute()
{
return Callback<PyObject*>::call_method(this, "__abs__");
}
int Instance::nonzero()
{
return Callback<bool>::call_method(this, "__nonzero__");
}
PyObject* Instance::invert()
{
return Callback<PyObject*>::call_method(this, "__invert__");
}
PyObject* Instance::lshift(PyObject* other)
{
return Callback<PyObject*>::call_method(this, "__lshift__", other);
}
PyObject* Instance::rshift(PyObject* other)
{
return Callback<PyObject*>::call_method(this, "__rshift__", other);
}
PyObject* Instance::do_and(PyObject* other)
{
return Callback<PyObject*>::call_method(this, "__and__", other);
}
PyObject* Instance::do_xor(PyObject* other)
{
return Callback<PyObject*>::call_method(this, "__xor__", other);
}
PyObject* Instance::do_or(PyObject* other)
{
return Callback<PyObject*>::call_method(this, "__or__", other);
}
int Instance::coerce(PyObject** x, PyObject** y)
{
assert(this == *x);
// Coerce must return a tuple
Tuple result(Callback<Tuple>::call_method(this, "__coerce__", *y));
*x = result[0].release();
*y = result[1].release();
return 0;
}
PyObject* Instance::as_int()
{
return Callback<PyObject*>::call_method(this, "__int__");
}
PyObject* Instance::as_long()
{
return Callback<PyObject*>::call_method(this, "__long__");
}
PyObject* Instance::as_float()
{
return Callback<PyObject*>::call_method(this, "__float__");
}
PyObject* Instance::oct()
{
return Callback<PyObject*>::call_method(this, "__oct__");
}
PyObject* Instance::hex()
{
return Callback<PyObject*>::call_method(this, "__hex__");
}
namespace {
struct NamedCapability
{
@@ -250,7 +367,30 @@ namespace {
{ "__delitem__", TypeObjectBase::sequence_ass_item },
{ "__getslice__", TypeObjectBase::sequence_slice },
{ "__setslice__", TypeObjectBase::sequence_ass_slice },
{ "__delslice__", TypeObjectBase::sequence_ass_slice }
{ "__delslice__", TypeObjectBase::sequence_ass_slice },
{ "__add__", TypeObjectBase::number_add },
{ "__sub__", TypeObjectBase::number_subtract },
{ "__mul__", TypeObjectBase::number_multiply },
{ "__div__", TypeObjectBase::number_divide },
{ "__mod__", TypeObjectBase::number_remainder },
{ "__divmod__", TypeObjectBase::number_divmod },
{ "__pow__", TypeObjectBase::number_power },
{ "__neg__", TypeObjectBase::number_negative },
{ "__pos__", TypeObjectBase::number_positive },
{ "__abs__", TypeObjectBase::number_absolute },
{ "__nonzero__", TypeObjectBase::number_nonzero },
{ "__invert__", TypeObjectBase::number_invert },
{ "__lshift__", TypeObjectBase::number_lshift },
{ "__rshift__", TypeObjectBase::number_rshift },
{ "__and__", TypeObjectBase::number_and },
{ "__xor__", TypeObjectBase::number_xor },
{ "__or__", TypeObjectBase::number_or },
{ "__coerce__", TypeObjectBase::number_coerce },
{ "__int__", TypeObjectBase::number_int },
{ "__long__", TypeObjectBase::number_long },
{ "__float__", TypeObjectBase::number_float },
{ "__oct__", TypeObjectBase::number_oct },
{ "__hex__", TypeObjectBase::number_hex }
};
bool is_prefix(const char* s1, const char* s2)

View File

@@ -44,7 +44,32 @@ class Instance : public PythonObject
// Sequence methods
PyObject* get_slice(int start, int finish);
void set_slice(int start, int finish, PyObject* value);
// Number methods
PyObject* add(PyObject* other);
PyObject* subtract(PyObject* other);
PyObject* multiply(PyObject* other);
PyObject* divide(PyObject* other);
PyObject* remainder(PyObject* other);
PyObject* divmod(PyObject* other);
PyObject* power(PyObject*, PyObject*);
PyObject* negative();
PyObject* positive();
PyObject* absolute();
int nonzero();
PyObject* invert();
PyObject* lshift(PyObject* other);
PyObject* rshift(PyObject* other);
PyObject* do_and(PyObject* other);
PyObject* do_xor(PyObject* other);
PyObject* do_or(PyObject* other);
int coerce(PyObject**, PyObject**);
PyObject* as_int();
PyObject* as_long();
PyObject* as_float();
PyObject* oct();
PyObject* hex();
private: // noncopyable, without the size bloat
Instance(const Instance&);
void operator=(const Instance&);
@@ -92,6 +117,31 @@ class Class
PyObject* instance_sequence_slice(PyObject*, int start, int finish) const;
int instance_sequence_ass_slice(PyObject*, int start, int finish, PyObject* value) const;
private: // Implement number methods on instances
PyObject* instance_number_add(PyObject*, PyObject*) const;
PyObject* instance_number_subtract(PyObject*, PyObject*) const;
PyObject* instance_number_multiply(PyObject*, PyObject*) const;
PyObject* instance_number_divide(PyObject*, PyObject*) const;
PyObject* instance_number_remainder(PyObject*, PyObject*) const;
PyObject* instance_number_divmod(PyObject*, PyObject*) const;
PyObject* instance_number_power(PyObject*, PyObject*, PyObject*) const;
PyObject* instance_number_negative(PyObject*) const;
PyObject* instance_number_positive(PyObject*) const;
PyObject* instance_number_absolute(PyObject*) const;
int instance_number_nonzero(PyObject*) const;
PyObject* instance_number_invert(PyObject*) const;
PyObject* instance_number_lshift(PyObject*, PyObject*) const;
PyObject* instance_number_rshift(PyObject*, PyObject*) const;
PyObject* instance_number_and(PyObject*, PyObject*) const;
PyObject* instance_number_xor(PyObject*, PyObject*) const;
PyObject* instance_number_or(PyObject*, PyObject*) const;
int instance_number_coerce(PyObject*, PyObject**, PyObject**) const;
PyObject* instance_number_int(PyObject*) const;
PyObject* instance_number_long(PyObject*) const;
PyObject* instance_number_float(PyObject*) const;
PyObject* instance_number_oct(PyObject*) const;
PyObject* instance_number_hex(PyObject*) const;
private: // Miscellaneous "special" methods
PyObject* instance_call(PyObject* instance, PyObject* args, PyObject* keywords) const;
@@ -316,6 +366,144 @@ PyObject* Class<T>::instance_call(PyObject* instance, PyObject* args, PyObject*
return Downcast<T>(instance)->call(args, keywords);
}
template <class T>
PyObject* Class<T>::instance_number_add(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->add(other);
}
template <class T>
PyObject* Class<T>::instance_number_subtract(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->subtract(other);
}
template <class T>
PyObject* Class<T>::instance_number_multiply(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->multiply(other);
}
template <class T>
PyObject* Class<T>::instance_number_divide(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->divide(other);
}
template <class T>
PyObject* Class<T>::instance_number_remainder(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->remainder(other);
}
template <class T>
PyObject* Class<T>::instance_number_divmod(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->divmod(other);
}
template <class T>
PyObject* Class<T>::instance_number_power(PyObject* instance, PyObject* exponent, PyObject* modulus) const
{
return Downcast<T>(instance)->power(exponent, modulus);
}
template <class T>
PyObject* Class<T>::instance_number_negative(PyObject* instance) const
{
return Downcast<T>(instance)->negative();
}
template <class T>
PyObject* Class<T>::instance_number_positive(PyObject* instance) const
{
return Downcast<T>(instance)->positive();
}
template <class T>
PyObject* Class<T>::instance_number_absolute(PyObject* instance) const
{
return Downcast<T>(instance)->absolute();
}
template <class T>
int Class<T>::instance_number_nonzero(PyObject* instance) const
{
return Downcast<T>(instance)->nonzero();
}
template <class T>
PyObject* Class<T>::instance_number_invert(PyObject* instance) const
{
return Downcast<T>(instance)->invert();
}
template <class T>
PyObject* Class<T>::instance_number_lshift(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->lshift(other);
}
template <class T>
PyObject* Class<T>::instance_number_rshift(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->rshift(other);
}
template <class T>
PyObject* Class<T>::instance_number_and(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->do_and(other);
}
template <class T>
PyObject* Class<T>::instance_number_xor(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->do_xor(other);
}
template <class T>
PyObject* Class<T>::instance_number_or(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->do_or(other);
}
template <class T>
int Class<T>::instance_number_coerce(PyObject* instance, PyObject** x, PyObject** y) const
{
return Downcast<T>(instance)->coerce(x, y);
}
template <class T>
PyObject* Class<T>::instance_number_int(PyObject* instance) const
{
return Downcast<T>(instance)->as_int();
}
template <class T>
PyObject* Class<T>::instance_number_long(PyObject* instance) const
{
return Downcast<T>(instance)->as_long();
}
template <class T>
PyObject* Class<T>::instance_number_float(PyObject* instance) const
{
return Downcast<T>(instance)->as_float();
}
template <class T>
PyObject* Class<T>::instance_number_oct(PyObject* instance) const
{
return Downcast<T>(instance)->oct();
}
template <class T>
PyObject* Class<T>::instance_number_hex(PyObject* instance) const
{
return Downcast<T>(instance)->hex();
}
template <class T>
Dict& Class<T>::dict()
{

View File

@@ -1,2 +0,0 @@
#define DEBUG_PYTHON
#include "subclass.cpp"

View File

@@ -15,10 +15,11 @@ a single long parameter.
File "<stdin>", line 1, in ?
TypeError: function requires exactly 1 argument; 0 given
>>> ext = Foo('foo')
Traceback (innermost last):
File "<stdin>", line 1, in ?
TypeError: illegal argument type for built-in operation
>>> try: ext = Foo('foo')
... except TypeError, err:
... assert re.match(
... '(illegal argument type for built-in operation)|(an integer is required)', str(err))
... else: print 'no exception'
>>> ext = Foo(1)
@@ -129,7 +130,7 @@ But objects not derived from Bar cannot:
>>> baz.pass_bar(baz)
Traceback (innermost last):
...
TypeError: extension class 'Baz' is not derived from 'Bar'.
TypeError: extension class 'Baz' is not convertible into 'Bar'.
The clone function on Baz returns a smart pointer; we wrap it into an
ExtensionInstance and make it look just like any other Baz instance.
@@ -144,6 +145,8 @@ Functions expecting an std::auto_ptr<Baz> parameter will not accept a raw Baz
... except RuntimeError, err:
... assert re.match("Object of extension class 'Baz' does not wrap <.*>.",
... str(err))
... else:
... print 'no exception'
We can pass std::auto_ptr<Baz> where it is expected
@@ -159,6 +162,8 @@ And if the auto_ptr has given up ownership?
... try: baz_clone.clone()
... except RuntimeError, err:
... assert re.match('Converting from python, pointer or smart pointer to <.*> is NULL.', str(err))
... else:
... print 'no exeption'
Polymorphism also works:
@@ -232,6 +237,7 @@ Overloading tests:
... assert re.match(
... 'No overloaded functions match [(]Range, string[)]\. Candidates are:\n.*\n.*',
... str(e))
... else: print 'no exception'
Sequence tests:
>>> len(Range(3, 10))
@@ -258,6 +264,22 @@ Sequence tests:
>>> map(lambda x:x, Range(3, 10)[0:4])
[3, 4, 5, 6]
Numeric tests:
>>> x = Rational(2,3)
>>> y = Rational(1,4)
>>> print x + y
11/12
>>> print x - y
5/12
>>> print x * y
1/6
>>> print x / y
8/3
>>> print x + 1 # testing coercion
5/3
>>> print 1 + x # coercion the other way
5/3
delete non-existent attribute:
del m.foobar
Traceback (innermost last):
@@ -368,7 +390,7 @@ some __str__ and __repr__ tests:
>>> range = Range(5, 20)
>>> str(range)
'(5, 20)'
>>> assert re.match('<Range object at [0-9a-f]+>', repr(range))
>>> assert re.match('<Range object at [0-9a-fA-F]+>', repr(range))
__hash__ and __cmp__ tests:
@@ -406,6 +428,146 @@ Testing __call__:
0
>>> comparator(couple2, couple)
1
Testing overloaded free functions
>>> overloaded()
'Hello world!'
>>> overloaded(1)
1
>>> overloaded('foo')
'foo'
>>> overloaded(1,2)
3
>>> overloaded(1,2,3)
6
>>> overloaded(1,2,3,4)
10
>>> overloaded(1,2,3,4,5)
15
>>> try: overloaded(1, 'foo')
... except TypeError, err:
... assert re.match("No overloaded functions match \(int, string\)\. Candidates are:",
... str(err))
... else:
... print 'no exception'
Testing overloaded constructors
>>> over = OverloadTest()
>>> over.getX()
1000
>>> over = OverloadTest(1)
>>> over.getX()
1
>>> over = OverloadTest(1,1)
>>> over.getX()
2
>>> over = OverloadTest(1,1,1)
>>> over.getX()
3
>>> over = OverloadTest(1,1,1,1)
>>> over.getX()
4
>>> over = OverloadTest(1,1,1,1,1)
>>> over.getX()
5
>>> over = OverloadTest(over)
>>> over.getX()
5
>>> try: over = OverloadTest(1, 'foo')
... except TypeError, err:
... assert re.match("No overloaded functions match \(OverloadTest, int, string\)\. Candidates are:",
... str(err))
... else:
... print 'no exception'
Testing overloaded methods
>>> over.setX(3)
>>> over.overloaded()
3
>>> over.overloaded(1)
1
>>> over.overloaded(1,1)
2
>>> over.overloaded(1,1,1)
3
>>> over.overloaded(1,1,1,1)
4
>>> over.overloaded(1,1,1,1,1)
5
>>> try: over.overloaded(1,'foo')
... except TypeError, err:
... assert re.match("No overloaded functions match \(OverloadTest, int, string\)\. Candidates are:",
... str(err))
... else:
... print 'no exception'
Testing base class conversions
>>> testUpcast(over)
Traceback (innermost last):
TypeError: extension class 'OverloadTest' is not convertible into 'Base'.
>>> der1 = Derived1(333)
>>> der1.x()
333
>>> testUpcast(der1)
333
>>> der1 = derived1Factory(1000)
>>> testDowncast1(der1)
1000
>>> testDowncast2(der1)
Traceback (innermost last):
TypeError: extension class 'Base' is not convertible into 'Derived2'.
>>> der2 = Derived2(444)
>>> der2.x()
444
>>> testUpcast(der2)
444
>>> der2 = derived2Factory(1111)
>>> testDowncast2(der2)
Traceback (innermost last):
TypeError: extension class 'Base' is not convertible into 'Derived2'.
Testing interaction between callbacks, base declarations, and overloading
- testCallback() calls callback() (within C++)
- callback() is overloaded (in the wrapped class CallbackTest)
- callback() is redefined in RedefineCallback (overloading is simulated by type casing)
- testCallback() should use the redefined callback()
>>> c = CallbackTest()
>>> c.testCallback(1)
2
>>> c.testCallback('foo')
Traceback (innermost last):
File "<stdin>", line 1, in ?
TypeError: illegal argument type for built-in operation
>>> c.callback(1)
2
>>> c.callback('foo')
'foo 1'
>>> import types
>>> class RedefineCallback(CallbackTest):
... def callback(self, x):
... if type(x) is types.IntType:
... return x - 2
... else:
... return CallbackTest.callback(self,x)
...
>>> r = RedefineCallback()
>>> r.callback(1)
-1
>>> r.callback('foo')
'foo 1'
>>> r.testCallback('foo')
Traceback (innermost last):
File "<stdin>", line 1, in ?
TypeError: illegal argument type for built-in operation
>>> r.testCallback(1)
-1
>>> testCallback(r, 1)
-1
'''
from demo import *

View File

@@ -5,22 +5,95 @@ Document error-handling
Consider renaming PyPtr to Reference.
Report Cygwin linker memory issues
handle more arguments
MI from both ExtensionClasses and Python classes, or at least don't crash(!)
Remove one level of indirection on type objects (no vtbl?).
Much more testing, especially of things in objects.h
Make multiple inheritance from real Python classes work.
Make multiple inheritance from real Python classes work
Handle polymorphism (passing a Wrapped<Derived> as a Base*).
Specializations of Caller<> for commmon combinations of argument types (?)
special member functions for numeric types
pickling support
testing with Python 2.0
Make abstract classes non-instantiable (?)
Much more testing, especially of things in objects.h
Support for Python LONG types in Objects.h
Concept checking for to_python<T>() template function
Support for __del__()
Reference-counting for UniquePodSet?
Documentation:
building
overloading and the overload resolution mechanism
special member functions
adding conversions for fundamental types
generic conversions for template types (with partial spec).
extending multiple-argument support.
differences between Python classes and ExtensionClasses
additional capabilities of ExtensionClasses
slice adjustment
exception handling
building
dealing with non-const reference/pointer parameters
Advanced Topics:
Advanced Type Conversion
adding conversions for fundamental types
generic conversions for template types (with partial spec).
Interacting with built-in Python objects and types from C++
dealing with non-const reference/pointer parameters
Private virtual functions with default implementations
extending multiple-argument support using gen_all.py
limitations
templates
Yes. If you look at the examples in extclass_demo.cpp you'll see that I have
exposed several template instantiations (e.g. std::pair<int,int>) in Python.
Keep in mind, however, that you can only expose a template instantiation,
not a template. In other words, MyTemplate<Foo> can be exposed. MyTemplate
itself cannot.
Well, that's not strictly true. Wow, this is more complicated to explain
than I thought.
You can't make an ExtensionClass<MyTemplate>, since after all MyTemplate is
not a type. You can only expose a concrete type to Python.
What you *can* do (if your compiler supports partial ordering of function
templates - MSVC is broken and does not) is to write appropriate
from_python() and to_python() functions for converting a whole class of
template instantiations to/from Python. That won't let you create an
instance of MyTemplate<SomePythonType> from Python, but it will let you
pass/return arbitrary MyTemplate<SomeCplusplusType> instances to/from your
wrapped C++ functions.
template <class T>
MyTemplate<T> from_python(PyObject* x, py::Type<MyTemplate<T> >)
{
// code to convert x into a MyTemplate<T>... that part is up to you
}
template <class T>
PyObject* from_python(const MyTemplate<T>&)
{
// code to convert MyTemplate<T> into a PyObject*... that part is up to
you
}
For example, you could use this to convert Python lists to/from
std::vector<T> automatically.
enums
the importance of declaration order of ClassWrappers/ExtensionInstances
out parameters and non-const pointers
returning references to wrapped objects
About the vc6 project and the debug build
About doctest.py
Boost remarks:

58
under-the-hood.html Normal file
View File

@@ -0,0 +1,58 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<title>
A Peek Under the Hood
</title>
<h1>
<img src="c++boost.gif" alt="c++boost.gif (8819 bytes)" align="center"
width="277" height="86">
</h1>
<h1>
A Peek Under the Hood
</h1>
<p>
<code> ExtensionClass&lt;T&gt;</code> is a subclass of <code>
PyTypeObject</code>, the <code> struct</code> which Python's 'C' API uses
to describe a type. <a href="example1.html#world_class">An instance of
the <code>ExtensionClass&lt;&gt;</code></a> becomes the Python type
object corresponding to <code>hello::world</code>. When we <a href=
"example1.html#add_world_class">add it to the module</a> it goes into the
module's dictionary to be looked up under the name "world".
<p>
Py_cpp uses C++'s template argument deduction mechanism to determine the
types of arguments to functions (except constructors, for which we must
<a href="example1.html#Constructor_example">provide an argument list</a>
because they can't be named in C++). Then, it calls the appropriate
overloaded functions <code>PyObject*
to_python(</code><em>S</em><code>)</code> and <em>
S'</em><code>from_python(PyObject*,
Type&lt;</code><em>S</em><code>&gt;)</code> which convert between any C++
type <em>S</em> and a <code>PyObject*</code>, the type which represents a
reference to any Python object in its 'C' API. The <a href=
"example1.html#world_class"><code>ExtensionClass&lt;T&gt;</code></a>
template defines a whole raft of these conversions (for <code>T, T*,
T&amp;, std::auto_ptr&lt;T&gt;</code>, etc.), using the same inline
friend function technique employed by <a href=
"http://www.boost.org/libs/utility/operators.htm">the boost operators
library</a>.
<p>
Because the <code>to_python</code> and <code>from_python</code> functions
for a user-defined class are defined by <code>
ExtensionClass&lt;T&gt;</code>, it is important that an instantiation of
<code> ExtensionClass&lt;T&gt;</code> is visible to any code which wraps
a C++ function with a <code>T, T*, const T&amp;</code>, etc. parameter or
return value. In particular, you may want to create all of the classes at
the top of your module's init function, then <code>def</code> the member
functions later to avoid problems with inter-class dependencies.
<p>
Previous: <a href="overriding.html">Function Overloading</a> Up: <a
href="py_cpp.html">Top</a>
<p>
&copy; 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.
<p>
Updated: Sept 30, 2000

View File

@@ -1,41 +0,0 @@
========================================================================
DYNAMIC LINK LIBRARY : vc6_prj
========================================================================
AppWizard has created this vc6_prj DLL for you.
This file contains a summary of what you will find in each of the files that
make up your vc6_prj application.
vc6_prj.dsp
This file (the project file) contains information at the project level and
is used to build a single project or subproject. Other users can share the
project (.dsp) file, but they should export the makefiles locally.
vc6_prj.cpp
This is the main DLL source file.
When created, this DLL does not export any symbols. As a result, it
will not produce a .lib file when it is built. If you wish this project
to be a project dependency of some other project, you will either need to
add code to export some symbols from the DLL so that an export library
will be produced, or you can check the "doesn't produce lib" checkbox in
the Linker settings page for this project.
/////////////////////////////////////////////////////////////////////////////
Other standard files:
StdAfx.h, StdAfx.cpp
These files are used to build a precompiled header (PCH) file
named vc6_prj.pch and a precompiled types file named StdAfx.obj.
/////////////////////////////////////////////////////////////////////////////
Other notes:
AppWizard uses "TODO:" to indicate parts of the source code you
should add to or customize.
/////////////////////////////////////////////////////////////////////////////

View File

@@ -1,8 +0,0 @@
// stdafx.cpp : source file that includes just the standard includes
// vc6_prj.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

View File

@@ -1,24 +0,0 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#if !defined(AFX_STDAFX_H__A41B37EF_17F7_406A_95AC_FE67BDB05B1E__INCLUDED_)
#define AFX_STDAFX_H__A41B37EF_17F7_406A_95AC_FE67BDB05B1E__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// Insert your headers here
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
// TODO: reference additional headers your program requires here
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_STDAFX_H__A41B37EF_17F7_406A_95AC_FE67BDB05B1E__INCLUDED_)

View File

@@ -1,13 +0,0 @@
// vc6_prj.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}

Binary file not shown.