mirror of
https://github.com/boostorg/python.git
synced 2026-01-24 06:02:14 +00:00
This commit was manufactured by cvs2svn to create branch 'numerics'.
[SVN r7963]
This commit is contained in:
@@ -59,4 +59,4 @@ inline BaseObject<PythonType>::~BaseObject()
|
||||
|
||||
}
|
||||
|
||||
#endif BASE_OBJECT_DWA051600_H_
|
||||
#endif // BASE_OBJECT_DWA051600_H_
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -70,9 +67,6 @@ class ClassWrapper
|
||||
{ m_class->def_read_write(pm, name); }
|
||||
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
176
comparisons.html
Normal 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.
|
||||
The analogous feature of py_cp is designed to be used from C++ (I
|
||||
make no claims that the py_cpp mechanism is more useful than the Zope
|
||||
mechanism in this case)
|
||||
</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>
|
||||
© 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>
|
||||
|
||||
4
demo.bdf
4
demo.bdf
@@ -1,4 +0,0 @@
|
||||
TargetName=demo
|
||||
TargetType=dll
|
||||
SourceFiles=extclass_demo.cpp
|
||||
Libs=py_cpp python utils
|
||||
@@ -1,4 +0,0 @@
|
||||
TargetName=demo_d
|
||||
TargetType=dll
|
||||
SourceFiles=extclass_demo_d.cpp
|
||||
Libs=py_cpp_d python_d utils
|
||||
@@ -1,4 +0,0 @@
|
||||
TargetName=example1
|
||||
TargetType=dll
|
||||
SourceFiles=example1.cpp
|
||||
Libs=py_cpp python utils
|
||||
127
example1.html
Normal file
127
example1.html
Normal 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& 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 <py_cpp/class_wrapper.h>
|
||||
// Python requires an exported function called init<module-name> 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<hello::world> world_class(hello, "world");
|
||||
// Add the __init__ function
|
||||
world_class.def(py::Constructor<int>());
|
||||
// Add a regular member function
|
||||
world_class.def(&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 <windows.h>
|
||||
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>
|
||||
>>> import hello
|
||||
>>> hi_world = hello.world(3)
|
||||
>>> hi_world.get()
|
||||
'hi, world'
|
||||
>>> hello.length(hi_world)
|
||||
9
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
We can even make a subclass of <code>hello.world</code>:
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> class my_subclass(hello.world):
|
||||
... def get(self):
|
||||
... return 'hello, world'
|
||||
...
|
||||
>>> y = my_subclass(4)
|
||||
>>> y.get()
|
||||
'hello, world'
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Pretty cool! You can't do that with an ordinary Python extension type!
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> 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>
|
||||
© 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>
|
||||
|
||||
98
extclass.h
98
extclass.h
@@ -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_
|
||||
@@ -279,10 +282,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()))))));
|
||||
@@ -350,7 +355,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>
|
||||
@@ -403,20 +470,6 @@ 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)
|
||||
{
|
||||
@@ -434,6 +487,13 @@ inline void ClassRegistry<T>::unregister_class(Class<ExtensionInstance>* p)
|
||||
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_
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "extclass.cpp"
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "extclass_demo.cpp"
|
||||
@@ -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
73
extending.html
Normal 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>
|
||||
© 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>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "functions.cpp"
|
||||
@@ -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
|
||||
|
||||
427
gen_extclass.py
427
gen_extclass.py
@@ -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__':
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "init_function.cpp"
|
||||
48
makefile
48
makefile
@@ -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
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "module.cpp"
|
||||
@@ -445,7 +445,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)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "objects.cpp"
|
||||
139
overloading.html
Normal file
139
overloading.html
Normal 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<X> x_class(overload_demo, "X");
|
||||
// Overloaded constructors
|
||||
x_class.def(py::Constructor<>());
|
||||
x_class.def(py::Constructor<int>());
|
||||
|
||||
// Overloaded member functions
|
||||
x_class.def((int (X::*)() const)&X::value, "value");
|
||||
x_class.def((void (X::*)(int))&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& self, int v) { self.value(v); }
|
||||
inline int get_x_value(X& 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 functions 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="under-the-hoold.html">A Peek Under the Hood</a>
|
||||
Up: <a href="py_cpp.html">Top</a>
|
||||
<p>
|
||||
© 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
175
overriding.html
Normal 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<const char*>::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->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<></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<hello::world<strong>,world_callback></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(&hello::world::get, "get", &world_callback::default_get);
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Now our subclass of <code>hello.world</code> behaves as expected:
|
||||
<p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> class my_subclass(hello.world):
|
||||
... def get(self):
|
||||
... return 'hello, world'
|
||||
...
|
||||
>>> 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<></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<void>::call_method(m_self, "pure", x); }
|
||||
};
|
||||
|
||||
extern "C"
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
initfoobar()
|
||||
{
|
||||
try
|
||||
{
|
||||
py::Module foobar("foobar");
|
||||
py::ClassWrapper<baz,baz_callback> baz_class("baz");
|
||||
baz_class.def(&baz::pure, "pure");
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
py::handle_exception(); // Deal with the exception for Python
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Now in Python:
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> from foobar import baz
|
||||
>>> x = baz()
|
||||
>>> x.pure()
|
||||
Traceback (innermost last):
|
||||
File "<stdin>", line 1, in ?
|
||||
AttributeError: pure
|
||||
>>> class mumble(baz):
|
||||
... def pure(self, z): pass
|
||||
...
|
||||
>>> y = mumble()
|
||||
>>> y.pure()
|
||||
>>>
|
||||
</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>
|
||||
© 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
|
||||
|
||||
@@ -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
|
||||
120
py_cpp.html
Normal file
120
py_cpp.html
Normal file
@@ -0,0 +1,120 @@
|
||||
<!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_20001015.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 an unknown version of MSVC++ 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="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<0.2wink> 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>
|
||||
© 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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "subclass.cpp"
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
@@ -368,7 +374,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:
|
||||
|
||||
86
todo.txt
86
todo.txt
@@ -5,22 +5,96 @@ 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 - I don't think this is possible
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
exposing data members as attributes
|
||||
def_readonly()
|
||||
def_readwrite()
|
||||
def_getter()
|
||||
def_setter()
|
||||
|
||||
differences between Python classes and ExtensionClasses
|
||||
additional capabilities of ExtensionClasses
|
||||
|
||||
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
58
under-the-hood.html
Normal 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<T></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<></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<</code><em>S</em><code>>)</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<T></code></a>
|
||||
template defines a whole raft of these conversions (for <code>T, T*,
|
||||
T&, std::auto_ptr<T></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<T></code>, it is important that an instantiation of
|
||||
<code> ExtensionClass<T></code> is visible to any code which wraps
|
||||
a C++ function with a <code>T, T*, const T&</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>
|
||||
© 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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
@@ -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
|
||||
@@ -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_)
|
||||
@@ -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.
Reference in New Issue
Block a user