2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-21 17:12:22 +00:00

This commit was generated by cvs2svn to compensate for changes in r315,

which included commits to RCS files with non-trunk default branches.


[SVN r7932]
This commit is contained in:
Ullrich Köthe
2000-10-13 13:49:34 +00:00
commit 12a881ead5
74 changed files with 10015 additions and 0 deletions

96
.gitattributes vendored Normal file
View File

@@ -0,0 +1,96 @@
* text=auto !eol svneol=native#text/plain
*.gitattributes text svneol=native#text/plain
# Scriptish formats
*.bat text svneol=native#text/plain
*.bsh text svneol=native#text/x-beanshell
*.cgi text svneol=native#text/plain
*.cmd text svneol=native#text/plain
*.js text svneol=native#text/javascript
*.php text svneol=native#text/x-php
*.pl text svneol=native#text/x-perl
*.pm text svneol=native#text/x-perl
*.py text svneol=native#text/x-python
*.sh eol=lf svneol=LF#text/x-sh
configure eol=lf svneol=LF#text/x-sh
# Image formats
*.bmp binary svneol=unset#image/bmp
*.gif binary svneol=unset#image/gif
*.ico binary svneol=unset#image/ico
*.jpeg binary svneol=unset#image/jpeg
*.jpg binary svneol=unset#image/jpeg
*.png binary svneol=unset#image/png
*.tif binary svneol=unset#image/tiff
*.tiff binary svneol=unset#image/tiff
*.svg text svneol=native#image/svg%2Bxml
# Data formats
*.pdf binary svneol=unset#application/pdf
*.avi binary svneol=unset#video/avi
*.doc binary svneol=unset#application/msword
*.dsp text svneol=crlf#text/plain
*.dsw text svneol=crlf#text/plain
*.eps binary svneol=unset#application/postscript
*.gz binary svneol=unset#application/gzip
*.mov binary svneol=unset#video/quicktime
*.mp3 binary svneol=unset#audio/mpeg
*.ppt binary svneol=unset#application/vnd.ms-powerpoint
*.ps binary svneol=unset#application/postscript
*.psd binary svneol=unset#application/photoshop
*.rdf binary svneol=unset#text/rdf
*.rss text svneol=unset#text/xml
*.rtf binary svneol=unset#text/rtf
*.sln text svneol=native#text/plain
*.swf binary svneol=unset#application/x-shockwave-flash
*.tgz binary svneol=unset#application/gzip
*.vcproj text svneol=native#text/xml
*.vcxproj text svneol=native#text/xml
*.vsprops text svneol=native#text/xml
*.wav binary svneol=unset#audio/wav
*.xls binary svneol=unset#application/vnd.ms-excel
*.zip binary svneol=unset#application/zip
# Text formats
.htaccess text svneol=native#text/plain
*.bbk text svneol=native#text/xml
*.cmake text svneol=native#text/plain
*.css text svneol=native#text/css
*.dtd text svneol=native#text/xml
*.htm text svneol=native#text/html
*.html text svneol=native#text/html
*.ini text svneol=native#text/plain
*.log text svneol=native#text/plain
*.mak text svneol=native#text/plain
*.qbk text svneol=native#text/plain
*.rst text svneol=native#text/plain
*.sql text svneol=native#text/x-sql
*.txt text svneol=native#text/plain
*.xhtml text svneol=native#text/xhtml%2Bxml
*.xml text svneol=native#text/xml
*.xsd text svneol=native#text/xml
*.xsl text svneol=native#text/xml
*.xslt text svneol=native#text/xml
*.xul text svneol=native#text/xul
*.yml text svneol=native#text/plain
boost-no-inspect text svneol=native#text/plain
CHANGES text svneol=native#text/plain
COPYING text svneol=native#text/plain
INSTALL text svneol=native#text/plain
Jamfile text svneol=native#text/plain
Jamroot text svneol=native#text/plain
Jamfile.v2 text svneol=native#text/plain
Jamrules text svneol=native#text/plain
Makefile* text svneol=native#text/plain
README text svneol=native#text/plain
TODO text svneol=native#text/plain
# Code formats
*.c text svneol=native#text/plain
*.cpp text svneol=native#text/plain
*.h text svneol=native#text/plain
*.hpp text svneol=native#text/plain
*.ipp text svneol=native#text/plain
*.tpp text svneol=native#text/plain
*.jam text svneol=native#text/plain
*.java text svneol=native#text/plain

62
base_object.h Normal file
View File

@@ -0,0 +1,62 @@
// (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.
#ifndef BASE_OBJECT_DWA051600_H_
# define BASE_OBJECT_DWA051600_H_
# include "pyconfig.h"
# include "signatures.h" // really just for Type<>
# include "wrap_python.h"
# include <cstring>
namespace py {
// BaseObject - adds a constructor and non-virtual destructor to a
// base Python type (e.g. PyObject, PyTypeObject).
template <class PythonType>
struct BaseObject : PythonType
{
typedef PythonType BasePythonType;
// Initializes type and reference count. All other fields of BasePythonType are 0
BaseObject(PyTypeObject* type_object);
// Decrements reference count on the type
~BaseObject();
};
// Easy typedefs for common usage
typedef BaseObject<PyObject> PythonObject;
typedef BaseObject<PyTypeObject> PythonType;
//
// Class template member function implementations
//
template <class PythonType>
BaseObject<PythonType>::BaseObject(PyTypeObject* type_object)
{
BasePythonType* bp = this;
#if !defined(_MSC_VER) || defined(__STLPORT)
std::
#endif
memset(bp, 0, sizeof(BasePythonType));
ob_refcnt = 1;
ob_type = type_object;
Py_INCREF(type_object);
}
template <class PythonType>
inline BaseObject<PythonType>::~BaseObject()
{
Py_DECREF(ob_type);
}
}
#endif BASE_OBJECT_DWA051600_H_

83
callback.h Normal file
View File

@@ -0,0 +1,83 @@
// (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 was generated for 5-argument python callbacks by gen_callback.py
#ifndef CALLBACK_DWA_052100_H_
# define CALLBACK_DWA_052100_H_
# include "pyconfig.h"
# include "py.h"
namespace py {
// Just like the above, except we decrement p's reference count instead of returning it.
void expect_and_absorb_non_null(PyObject* p);
// Calling Python from C++
template <class R>
struct Callback
{
static R call_method(PyObject* self, const char* name)
{ return from_python(expect_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("()"))), Type<R>()); }
template <class A1>
static R call_method(PyObject* self, const char* name, const A1& a1)
{ return from_python(expect_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("(N)"), to_python(a1))), Type<R>()); }
template <class A1, class A2>
static R call_method(PyObject* self, const char* name, const A1& a1, const A2& a2)
{ return from_python(expect_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("(NN)"), to_python(a1), to_python(a2))), Type<R>()); }
template <class A1, class A2, class A3>
static R call_method(PyObject* self, const char* name, const A1& a1, const A2& a2, const A3& a3)
{ return from_python(expect_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("(NNN)"), to_python(a1), to_python(a2), to_python(a3))), Type<R>()); }
template <class A1, class A2, class A3, class A4>
static R call_method(PyObject* self, const char* name, const A1& a1, const A2& a2, const A3& a3, const A4& a4)
{ return from_python(expect_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("(NNNN)"), to_python(a1), to_python(a2), to_python(a3), to_python(a4))), Type<R>()); }
template <class A1, class A2, class A3, class A4, class A5>
static R call_method(PyObject* self, const char* name, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5)
{ return from_python(expect_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("(NNNNN)"), to_python(a1), to_python(a2), to_python(a3), to_python(a4), to_python(a5))), Type<R>()); }
};
// This specialization wouldn't be needed, but MSVC6 doesn't correctly allow the following:
// void g();
// void f() { return g(); }
template <>
struct Callback<void>
{ static void call_method(PyObject* self, const char* name)
{ expect_and_absorb_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("()"))); }
template <class A1>
static void call_method(PyObject* self, const char* name, const A1& a1)
{ expect_and_absorb_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("(N)"), to_python(a1))); }
template <class A1, class A2>
static void call_method(PyObject* self, const char* name, const A1& a1, const A2& a2)
{ expect_and_absorb_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("(NN)"), to_python(a1), to_python(a2))); }
template <class A1, class A2, class A3>
static void call_method(PyObject* self, const char* name, const A1& a1, const A2& a2, const A3& a3)
{ expect_and_absorb_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("(NNN)"), to_python(a1), to_python(a2), to_python(a3))); }
template <class A1, class A2, class A3, class A4>
static void call_method(PyObject* self, const char* name, const A1& a1, const A2& a2, const A3& a3, const A4& a4)
{ expect_and_absorb_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("(NNNN)"), to_python(a1), to_python(a2), to_python(a3), to_python(a4))); }
template <class A1, class A2, class A3, class A4, class A5>
static void call_method(PyObject* self, const char* name, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5)
{ expect_and_absorb_non_null(PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("(NNNNN)"), to_python(a1), to_python(a2), to_python(a3), to_python(a4), to_python(a5))); }
};
} // namespace py
#endif // CALLBACK_DWA_052100_H_

507
caller.h Normal file
View File

@@ -0,0 +1,507 @@
// (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 generated for 5-argument member functions and 5-argument free
// functions by gen_caller.py
#ifndef CALLER_DWA05090_H_
# define CALLER_DWA05090_H_
# include "pyconfig.h"
# include "wrap_python.h"
# include <boost/config.hpp>
# include "signatures.h"
# include "none.h"
namespace py {
// Calling C++ from Python
template <class R>
struct Caller
{
template <class T>
static PyObject* call(R (T::*pmf)(), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
if (!PyArg_ParseTuple(args, const_cast<char*>("O"), &self))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)());
}
template <class T, class A1>
static PyObject* call(R (T::*pmf)(A1), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &self, &a1))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(from_python(a1, Type<A1>())));
}
template <class T, class A1, class A2>
static PyObject* call(R (T::*pmf)(A1, A2), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOO"), &self, &a1, &a2))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>())));
}
template <class T, class A1, class A2, class A3>
static PyObject* call(R (T::*pmf)(A1, A2, A3), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOO"), &self, &a1, &a2, &a3))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>())));
}
template <class T, class A1, class A2, class A3, class A4>
static PyObject* call(R (T::*pmf)(A1, A2, A3, A4), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOOO"), &self, &a1, &a2, &a3, &a4))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>())));
}
template <class T, class A1, class A2, class A3, class A4, class A5>
static PyObject* call(R (T::*pmf)(A1, A2, A3, A4, A5), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
PyObject* a5;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOOOO"), &self, &a1, &a2, &a3, &a4, &a5))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>()),
from_python(a5, Type<A5>())));
}
template <class T>
static PyObject* call(R (T::*pmf)() const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
if (!PyArg_ParseTuple(args, const_cast<char*>("O"), &self))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)());
}
template <class T, class A1>
static PyObject* call(R (T::*pmf)(A1) const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &self, &a1))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(from_python(a1, Type<A1>())));
}
template <class T, class A1, class A2>
static PyObject* call(R (T::*pmf)(A1, A2) const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOO"), &self, &a1, &a2))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>())));
}
template <class T, class A1, class A2, class A3>
static PyObject* call(R (T::*pmf)(A1, A2, A3) const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOO"), &self, &a1, &a2, &a3))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>())));
}
template <class T, class A1, class A2, class A3, class A4>
static PyObject* call(R (T::*pmf)(A1, A2, A3, A4) const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOOO"), &self, &a1, &a2, &a3, &a4))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>())));
}
template <class T, class A1, class A2, class A3, class A4, class A5>
static PyObject* call(R (T::*pmf)(A1, A2, A3, A4, A5) const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
PyObject* a5;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOOOO"), &self, &a1, &a2, &a3, &a4, &a5))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>()),
from_python(a5, Type<A5>())));
}
// Free functions
static PyObject* call(R (*f)(), PyObject* args, PyObject* /* keywords */ ) {
if (!PyArg_ParseTuple(args, const_cast<char*>("")))
return 0;
return to_python(f());
}
template <class A1>
static PyObject* call(R (*f)(A1), PyObject* args, PyObject* /* keywords */ ) {
PyObject* a1;
if (!PyArg_ParseTuple(args, const_cast<char*>("O"), &a1))
return 0;
return to_python(f(from_python(a1, Type<A1>())));
}
template <class A1, class A2>
static PyObject* call(R (*f)(A1, A2), PyObject* args, PyObject* /* keywords */ ) {
PyObject* a1;
PyObject* a2;
if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &a1, &a2))
return 0;
return to_python(f(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>())));
}
template <class A1, class A2, class A3>
static PyObject* call(R (*f)(A1, A2, A3), PyObject* args, PyObject* /* keywords */ ) {
PyObject* a1;
PyObject* a2;
PyObject* a3;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOO"), &a1, &a2, &a3))
return 0;
return to_python(f(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>())));
}
template <class A1, class A2, class A3, class A4>
static PyObject* call(R (*f)(A1, A2, A3, A4), PyObject* args, PyObject* /* keywords */ ) {
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOO"), &a1, &a2, &a3, &a4))
return 0;
return to_python(f(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>())));
}
template <class A1, class A2, class A3, class A4, class A5>
static PyObject* call(R (*f)(A1, A2, A3, A4, A5), PyObject* args, PyObject* /* keywords */ ) {
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
PyObject* a5;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOOO"), &a1, &a2, &a3, &a4, &a5))
return 0;
return to_python(f(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>()),
from_python(a5, Type<A5>())));
}
};
template <>
struct Caller<void>
{
template <class T>
static PyObject* call(void (T::*pmf)(), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
if (!PyArg_ParseTuple(args, const_cast<char*>("O"), &self))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)();
return none();
}
template <class T, class A1>
static PyObject* call(void (T::*pmf)(A1), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &self, &a1))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)(from_python(a1, Type<A1>()));
return none();
}
template <class T, class A1, class A2>
static PyObject* call(void (T::*pmf)(A1, A2), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOO"), &self, &a1, &a2))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()));
return none();
}
template <class T, class A1, class A2, class A3>
static PyObject* call(void (T::*pmf)(A1, A2, A3), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOO"), &self, &a1, &a2, &a3))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()));
return none();
}
template <class T, class A1, class A2, class A3, class A4>
static PyObject* call(void (T::*pmf)(A1, A2, A3, A4), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOOO"), &self, &a1, &a2, &a3, &a4))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>()));
return none();
}
template <class T, class A1, class A2, class A3, class A4, class A5>
static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
PyObject* a5;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOOOO"), &self, &a1, &a2, &a3, &a4, &a5))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>()),
from_python(a5, Type<A5>()));
return none();
}
template <class T>
static PyObject* call(void (T::*pmf)() const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
if (!PyArg_ParseTuple(args, const_cast<char*>("O"), &self))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)();
return none();
}
template <class T, class A1>
static PyObject* call(void (T::*pmf)(A1) const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &self, &a1))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)(from_python(a1, Type<A1>()));
return none();
}
template <class T, class A1, class A2>
static PyObject* call(void (T::*pmf)(A1, A2) const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOO"), &self, &a1, &a2))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()));
return none();
}
template <class T, class A1, class A2, class A3>
static PyObject* call(void (T::*pmf)(A1, A2, A3) const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOO"), &self, &a1, &a2, &a3))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()));
return none();
}
template <class T, class A1, class A2, class A3, class A4>
static PyObject* call(void (T::*pmf)(A1, A2, A3, A4) const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOOO"), &self, &a1, &a2, &a3, &a4))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>()));
return none();
}
template <class T, class A1, class A2, class A3, class A4, class A5>
static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5) const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
PyObject* a5;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOOOO"), &self, &a1, &a2, &a3, &a4, &a5))
return 0;
T& target = from_python(self, Type<T&>());
(target.*pmf)(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>()),
from_python(a5, Type<A5>()));
return none();
}
// Free functions
static PyObject* call(void (*f)(), PyObject* args, PyObject* /* keywords */ ) {
if (!PyArg_ParseTuple(args, const_cast<char*>("")))
return 0;
f();
return none();
}
template <class A1>
static PyObject* call(void (*f)(A1), PyObject* args, PyObject* /* keywords */ ) {
PyObject* a1;
if (!PyArg_ParseTuple(args, const_cast<char*>("O"), &a1))
return 0;
f(from_python(a1, Type<A1>()));
return none();
}
template <class A1, class A2>
static PyObject* call(void (*f)(A1, A2), PyObject* args, PyObject* /* keywords */ ) {
PyObject* a1;
PyObject* a2;
if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &a1, &a2))
return 0;
f(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()));
return none();
}
template <class A1, class A2, class A3>
static PyObject* call(void (*f)(A1, A2, A3), PyObject* args, PyObject* /* keywords */ ) {
PyObject* a1;
PyObject* a2;
PyObject* a3;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOO"), &a1, &a2, &a3))
return 0;
f(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()));
return none();
}
template <class A1, class A2, class A3, class A4>
static PyObject* call(void (*f)(A1, A2, A3, A4), PyObject* args, PyObject* /* keywords */ ) {
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOO"), &a1, &a2, &a3, &a4))
return 0;
f(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>()));
return none();
}
template <class A1, class A2, class A3, class A4, class A5>
static PyObject* call(void (*f)(A1, A2, A3, A4, A5), PyObject* args, PyObject* /* keywords */ ) {
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
PyObject* a5;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOOO"), &a1, &a2, &a3, &a4, &a5))
return 0;
f(from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>()),
from_python(a5, Type<A5>()));
return none();
}
};
}
#endif

79
cast.h Normal file
View File

@@ -0,0 +1,79 @@
// (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.
#ifndef CAST_DWA052500_H_
# define CAST_DWA052500_H_
# include "wrap_python.h"
# include <boost/operators.hpp>
namespace py {
// The default way of converting a PyObject* or PyTypeObject* to a T*
template <class T>
struct DowncastTraits
{
template <class U>
static T* cast(U* p) { return static_cast<T*>(p); }
};
inline PyTypeObject* as_base_object(const PyTypeObject*, PyObject* p)
{
return reinterpret_cast<PyTypeObject*>(p);
}
inline PyObject* as_base_object(const PyObject*, PyObject* p)
{
return p;
}
inline const PyTypeObject* as_base_object(const PyTypeObject*, const PyObject* p)
{
return reinterpret_cast<const PyTypeObject*>(p);
}
inline const PyObject* as_base_object(const PyObject*, const PyObject* p)
{
return p;
}
// Convert a pointer to any type derived from PyObject or PyTypeObject to a PyObject*
inline PyObject* as_object(PyObject* p) { return p; }
inline PyObject* as_object(PyTypeObject* p) { return reinterpret_cast<PyObject*>(p); }
// If I didn't have to support stupid MSVC6 we could just use a simple template function:
// template <class T> T* downcast(PyObject*).
template <class T>
struct Downcast : boost::dereferenceable<Downcast<T>, T*>
{
Downcast(PyObject* p)
: m_p(DowncastTraits<T>::cast(as_base_object((T*)0, p)))
{}
Downcast(const PyObject* p)
: m_p(DowncastTraits<T>::cast(as_base_object((const T*)0, p)))
{}
Downcast(PyTypeObject* p)
: m_p(DowncastTraits<T>::cast(p))
{}
Downcast(const PyTypeObject* p)
: m_p(DowncastTraits<T>::cast(p))
{}
operator T*() const { return m_p; }
T* get() const { return m_p; }
T& operator*() const { return *m_p; }
private:
T* m_p;
};
}
#endif // CAST_DWA052500_H_

87
class_wrapper.h Normal file
View File

@@ -0,0 +1,87 @@
#ifndef CLASS_WRAPPER_DWA101000_H_
# define CLASS_WRAPPER_DWA101000_H_
#include "extclass.h"
#include "module.h"
#include "py.h"
#include "cast.h"
namespace py {
namespace detail {
struct EmptyBase {};
}
// Syntactic sugar to make wrapping classes more convenient
template <class T, class U = HeldInstance<T> >
class ClassWrapper
: PyExtensionClassConverters<T, U> // Works around MSVC6.x/GCC2.95.2 bug described below
{
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);
}
// define constructors
template <class A1, class A2, class A3, class A4, class A5>
void def(Constructor<A1, A2, A3, A4, A5> signature)
{ m_class->def(signature); }
// 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)
{ m_class->def(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)
{ m_class->def(fn, name, default_fn); }
// 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)
{ m_class->def_getter(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)
{ m_class->def_getter(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)
{ m_class->def_readonly(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)
{ 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
// global functions declared within the body of a class template will only be
// generated when the class template is constructed, and when (for some reason)
// the construction does not occur via a new-expression. Otherwise, we could
// rely on the initialization of the m_class data member to cause all of the
// to_/from_python functions to come into being.
}
#endif // CLASS_WRAPPER_DWA101000_H_

4
demo.bdf Normal file
View File

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

4
demo_d.bdf Normal file
View File

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

1112
doctest.py Normal file

File diff suppressed because it is too large Load Diff

708
doxyfile Normal file
View File

@@ -0,0 +1,708 @@
# Doxyfile 1.2.2
# This file describes the settings to be used by doxygen for a project
#
# All text after a hash (#) is considered a comment and will be ignored
# The format is:
# TAG = value [value, ...]
# For lists items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (" ")
#---------------------------------------------------------------------------
# General configuration options
#---------------------------------------------------------------------------
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME = py_cpp
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = 0.9
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY = ./docs
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# The default language is English, other supported languages are:
# Dutch, French, Italian, Czech, Swedish, German, Finnish, Japanese,
# Korean, Hungarian, Spanish, Romanian, Russian, Croatian, Polish, and
# Portuguese.
OUTPUT_LANGUAGE = English
# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
# documentation are documented, even if no documentation was available.
# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
# will be included in the documentation.
EXTRACT_PRIVATE = YES
# If the EXTRACT_STATIC tag is set to YES all static members of a file
# will be included in the documentation.
EXTRACT_STATIC = NO
# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
# undocumented members of documented classes, files or namespaces.
# If set to NO (the default) these members will be included in the
# various overviews, but no documentation section is generated.
# This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy.
# If set to NO (the default) these class will be included in the various
# overviews. This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_CLASSES = NO
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
# include brief member descriptions after the members that are listed in
# the file and class documentation (similar to JavaDoc).
# Set to NO to disable this.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
# the brief description of a member or function before the detailed description.
# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
REPEAT_BRIEF = YES
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# Doxygen will generate a detailed section even if there is only a brief
# description.
ALWAYS_DETAILED_SEC = NO
# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
# path before files name in the file list and in the header files. If set
# to NO the shortest path that makes the file name unique will be used.
FULL_PATH_NAMES = NO
# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
# can be used to strip a user defined part of the path. Stripping is
# only done if one of the specified strings matches the left-hand part of
# the path. It is allowed to use relative paths in the argument list.
STRIP_FROM_PATH =
# The INTERNAL_DOCS tag determines if documentation
# that is typed after a \internal command is included. If the tag is set
# to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation.
INTERNAL_DOCS = NO
# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
# generate a class diagram (in Html and LaTeX) for classes with base or
# super classes. Setting the tag to NO turns the diagrams off.
CLASS_DIAGRAMS = YES
# If the SOURCE_BROWSER tag is set to YES then a list of source files will
# be generated. Documented entities will be cross-referenced with these sources.
SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
# doxygen to hide any special comment blocks from generated source code
# fragments. Normal C and C++ comments will always remain visible.
STRIP_CODE_COMMENTS = YES
# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
# file names in lower case letters. If set to YES upper case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# users are adviced to set this option to NO.
CASE_SENSE_NAMES = NO
# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
# will show members with their full class and namespace scopes in the
# documentation. If set to YES the scope will be hidden.
HIDE_SCOPE_NAMES = NO
# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
VERBATIM_HEADERS = YES
# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
# will put list of the files that are included by a file in the documentation
# of that file.
SHOW_INCLUDE_FILES = YES
# If the JAVADOC_AUTOBRIEF tag is set to YES (the default) then Doxygen
# will interpret the first line (until the first dot) of a JavaDoc-style
# comment as the brief description. If set to NO, the Javadoc-style will
# behave just like the Qt-style comments.
JAVADOC_AUTOBRIEF = YES
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
# member inherits the documentation from any documented member that it
# reimplements.
INHERIT_DOCS = YES
# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
# will sort the (detailed) documentation of file and class members
# alphabetically by member name. If set to NO the members will appear in
# declaration order.
SORT_MEMBER_DOCS = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES, then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
TAB_SIZE = 4
# The ENABLE_SECTIONS tag can be used to enable conditional
# documentation sections, marked by \if sectionname ... \endif.
ENABLED_SECTIONS =
# The GENERATE_TODOLIST tag can be used to enable (YES) or
# disable (NO) the todo list. This list is created by putting \todo
# commands in the documentation.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or
# disable (NO) the test list. This list is created by putting \test
# commands in the documentation.
GENERATE_TESTLIST = YES
# This tag can be used to specify a number of aliases that acts
# as commands in the documentation. An alias has the form "name=value".
# For example adding "sideeffect=\par Side Effects:\n" will allow you to
# put the command \sideeffect (or @sideeffect) in the documentation, which
# will result in a user defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
ALIASES =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated by doxygen. Possible values are YES and NO. If left blank
# NO is used.
WARNINGS = YES
# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
# automatically be disabled.
WARN_IF_UNDOCUMENTED = YES
# The WARN_FORMAT tag determines the format of the warning messages that
# doxygen can produce. The string should contain the $file, $line, and $text
# tags, which will be replaced by the file and line number from which the
# warning originated and the warning text.
WARN_FORMAT = "$file:$line: $text"
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag can be used to specify the files and/or directories that contain
# documented source files. You may enter file names like "myfile.cpp" or
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = c:/prj/manhattan/py_cpp
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
FILE_PATTERNS = *.cpp *.h *.py *.hpp
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE =
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
EXCLUDE_PATTERNS =
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
# the \include command).
EXAMPLE_PATH = example1.cpp test_extclass.py extclass_demo.cpp extclass_demo.h
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
EXAMPLE_PATTERNS =
# The IMAGE_PATH tag can be used to specify one or more files or
# directories that contain image that are included in the documentation (see
# the \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command <filter> <input-file>, where <filter>
# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
# input file. Doxygen will then use the output that the filter program writes
# to standard output.
INPUT_FILTER =
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.
ALPHABETICAL_INDEX = NO
# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all
# classes will be put under the same header in the alphabetical index.
# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
# should be ignored while generating the index headers.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
# generate HTML output.
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
HTML_OUTPUT = html
# The HTML_HEADER tag can be used to specify a personal HTML header for
# each generated HTML page. If it is left blank doxygen will generate a
# standard header.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a personal HTML footer for
# each generated HTML page. If it is left blank doxygen will generate a
# standard footer.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user defined cascading
# style sheet that is used by each HTML page. It can be used to
# fine-tune the look of the HTML output. If the tag is left blank doxygen
# will generate a default style sheet
HTML_STYLESHEET =
# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
# files or namespaces will be aligned in HTML using tables. If set to
# NO a bullet list will be used.
HTML_ALIGN_MEMBERS = YES
# If the GENERATE_HTMLHELP tag is set to YES, additional index files
# will be generated that can be used as input for tools like the
# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
# top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it.
DISABLE_INDEX = NO
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
# generate Latex output.
GENERATE_LATEX = NO
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `latex' will be used as the default path.
LATEX_OUTPUT = latex
# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
# LaTeX documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
# the generated latex document. The header should contain everything until
# the first chapter. If it is left blank doxygen will generate a
# standard header. Notice: only use this tag if you know what you are doing!
LATEX_HEADER =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
# is prepared for conversion to pdf (using ps2pdf). The pdf file will
# contain links (just like the HTML output) instead of page references
# This makes the output suitable for online browsing using a pdf viewer.
PDF_HYPERLINKS = NO
# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
# plain latex in the generated Makefile. Set this option to YES to get a
# higher quality PDF documentation.
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
# command to the generated LaTeX files. This will instruct LaTeX to keep
# running if errors occur, instead of asking the user for help.
# This option is also used when generating formulas in HTML.
LATEX_BATCHMODE = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
# The RTF output is optimised for Word 97 and may not look very pretty with
# other RTF readers or editors.
GENERATE_RTF = YES
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `rtf' will be used as the default path.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
# RTF documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
# will contain hyperlink fields. The RTF file will
# contain links (just like the HTML output) instead of page references.
# This makes the output suitable for online browsing using a WORD or other.
# programs which support those fields.
# Note: wordpad (write) and others do not support links.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# config file, i.e. a series of assigments. You only have to provide
# replacements, missing definitions are set to their default value.
RTF_STYLESHEET_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `man' will be used as the default path.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to
# the generated man pages (default is the subroutine's section .3)
MAN_EXTENSION = .3
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES Doxygen will
# generate an XML file that captures the structure of
# the code including all documentation. Warning: This feature
# is still experimental and very incomplete.
GENERATE_XML = NO
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
# evaluate all C-preprocessor directives found in the sources and include
# files.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
# names in the source code. If set to NO (the default) only conditional
# compilation will be performed. Macro expansion can be done in a controlled
# way by setting EXPAND_ONLY_PREDEF to YES.
MACRO_EXPANSION = NO
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_PREDEFINED tags.
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# in the INCLUDE_PATH (see below) will be search if a #include is found.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by
# the preprocessor.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will
# be used.
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that
# are defined before the preprocessor is started (similar to the -D option of
# gcc). The argument of the tag is a list of macros of the form: name
# or name=definition (no spaces). If the definition and the = are
# omitted =1 is assumed.
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
# The macro definition that is found in the sources will be used.
# Use the PREDEFINED tag if you want to use a different macro definition.
EXPAND_AS_DEFINED =
#---------------------------------------------------------------------------
# Configuration::addtions related to external references
#---------------------------------------------------------------------------
# The TAGFILES tag can be used to specify one or more tagfiles.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES all external classes will be listed
# in the class index. If set to NO only the inherited external classes
# will be listed.
ALLEXTERNALS = NO
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH = c:\dragon\ntbin\perl.exe
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz, a graph visualization
# toolkit from AT&T and Lucent Bell Labs. The other options in this section
# have no effect if this option is set to NO (the default)
HAVE_DOT = NO
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect inheritance relations. Setting this tag to YES will force the
# the CLASS_DIAGRAMS tag to NO.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect implementation dependencies (inheritance, containment, and
# class references variables) of the class with other documented classes.
COLLABORATION_GRAPH = YES
# If the ENABLE_PREPROCESSING, INCLUDE_GRAPH, and HAVE_DOT tags are set to
# YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other
# documented files.
INCLUDE_GRAPH = YES
# If the ENABLE_PREPROCESSING, INCLUDED_BY_GRAPH, and HAVE_DOT tags are set to
# YES then doxygen will generate a graph for each documented header file showing
# the documented files that directly or indirectly include this file
INCLUDED_BY_GRAPH = YES
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = YES
# The tag DOT_PATH can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found on the path.
DOT_PATH =
# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_WIDTH = 1024
# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_HEIGHT = 1024
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
# The SEARCHENGINE tag specifies whether or not a search engine should be
# used. If set to NO the values of all tags below this one will be ignored.
SEARCHENGINE = NO
# The CGI_NAME tag should be the name of the CGI script that
# starts the search engine (doxysearch) with the correct parameters.
# A script with this name will be generated by doxygen.
CGI_NAME = search.cgi
# The CGI_URL tag should be the absolute URL to the directory where the
# cgi binaries are located. See the documentation of your http daemon for
# details.
CGI_URL =
# The DOC_URL tag should be the absolute URL to the directory where the
# documentation is located. If left blank the absolute path to the
# documentation, with file:// prepended to it, will be used.
DOC_URL =
# The DOC_ABSPATH tag should be the absolute path to the directory where the
# documentation is located. If left blank the directory on the local machine
# will be used.
DOC_ABSPATH =
# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
# is installed.
BIN_ABSPATH = C:\tools\doxygen-1.2.2\bin
# The EXT_DOC_PATHS tag can be used to specify one or more paths to
# documentation generated for other projects. This allows doxysearch to search
# the documentation for these projects as well.
EXT_DOC_PATHS =

30
errors.h Normal file
View File

@@ -0,0 +1,30 @@
// (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.
#ifndef ERRORS_DWA052500_H_
# define ERRORS_DWA052500_H_
namespace py {
struct ErrorAlreadySet {};
struct ArgumentError : ErrorAlreadySet {};
// Handles exceptions caught just before returning to Python code.
void handle_exception();
template <class T>
T* expect_non_null(T* x)
{
if (x == 0)
throw ErrorAlreadySet();
return x;
}
} // namespace py
#endif // ERRORS_DWA052500_H_

4
example1.bdf Normal file
View File

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

54
example1.cpp Normal file
View File

@@ -0,0 +1,54 @@
#include <string.h>
namespace hello {
class world
{
public:
world(int) {}
~world() {}
const char* get() const { return "hi, world"; }
};
size_t length(const world& x) { return strlen(x.get()); }
}
#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

343
extclass.cpp Normal file
View File

@@ -0,0 +1,343 @@
// (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.
#include "extclass.h"
#include <cstring>
namespace py {
ExtensionInstance* get_extension_instance(PyObject* p)
{
// The object's type will just be some Class<ExtensionInstance> object,
// but if its meta-type is right, then it is an ExtensionInstance.
if (p->ob_type->ob_type != extension_meta_class())
{
PyErr_SetString(PyExc_TypeError, p->ob_type->tp_name);
throw py::ArgumentError();
}
return static_cast<ExtensionInstance*>(p);
}
void
ExtensionInstance::add_implementation(std::auto_ptr<InstanceHolderBase> holder)
{
for (WrappedObjects::const_iterator p = m_wrapped_objects.begin();
p != m_wrapped_objects.end(); ++p)
{
if (typeid(*holder) == typeid(**p))
{
PyErr_SetString(PyExc_RuntimeError, "Base class already initialized");
throw ErrorAlreadySet();
}
}
m_wrapped_objects.push_back(holder.release());
}
ExtensionInstance::ExtensionInstance(PyTypeObject* class_)
: Instance(class_)
{
}
ExtensionInstance::~ExtensionInstance()
{
for (WrappedObjects::const_iterator p = m_wrapped_objects.begin(),
finish = m_wrapped_objects.end();
p != finish; ++p)
{
delete *p;
}
}
MetaClass<ExtensionInstance>* extension_meta_class()
{
static MetaClass<ExtensionInstance> result;
return &result;
}
typedef Class<ExtensionInstance> ExtClass;
bool is_subclass(const ExtClass* derived,
const PyObject* possible_base)
{
Tuple bases = derived->bases();
for (std::size_t i = 0, size = bases.size(); i < size; ++i)
{
const PyObject* base = bases[i].get();
if (base == possible_base)
return true;
if (base->ob_type == extension_meta_class())
{
const ExtClass* base_class = Downcast<const ExtClass>(base);
if (is_subclass(base_class, possible_base))
return true;
}
}
return false;
}
// Return true iff instance is an instance of target_class
bool is_instance(ExtensionInstance* instance,
Class<ExtensionInstance>* target_class)
{
if (instance->ob_type == target_class)
return true;
else
{
return is_subclass(
Downcast<Class<ExtensionInstance> >(instance->ob_type).get(),
as_object(target_class));
}
}
void two_string_error(PyObject* exception_object, const char* format, const char* s1, const char* s2)
{
char buffer[256];
std::size_t format_length = PY_CSTD_::strlen(format);
std::size_t length1 = PY_CSTD_::strlen(s1);
std::size_t length2 = PY_CSTD_::strlen(s2);
std::size_t additional_length = length1 + length2;
if (additional_length + format_length > format_length - 1)
{
std::size_t difference = sizeof(buffer) - 1 - additional_length;
length1 -= difference / 2;
additional_length -= difference / 2;
}
sprintf(buffer, format, length1, s1, length2, s2);
PyErr_SetString(exception_object, buffer);
if (exception_object == PyExc_TypeError)
throw ArgumentError();
else
throw ErrorAlreadySet();
}
// This is called when an attempt has been made to convert the given instance to
// a C++ type for which it doesn't have any instance data. In that case, either
// the instance was not derived from the target_class, or the appropriate
// __init__ function wasn't called to initialize the instance data of the target class.
void report_missing_instance_data(
ExtensionInstance* instance, // The object being converted
Class<ExtensionInstance>* target_class, // the extension class of the C++ type
const std::type_info& target_typeid, // The typeid of the C++ type
bool target_is_ptr)
{
char buffer[256];
if (is_instance(instance, target_class))
{
if (target_is_ptr)
{
two_string_error(PyExc_RuntimeError,
"Object of extension class '%.*s' does not wrap <%.*s>.",
instance->ob_type->tp_name, target_typeid.name());
}
else
{
const char message[] = "__init__ function for extension class '%.*s' was never called.";
sprintf(buffer, message, sizeof(buffer) - sizeof(message) - 1,
target_class->tp_name);
}
PyErr_SetString(PyExc_RuntimeError, buffer);
}
else if (target_class == 0)
{
const char message[] = "Cannot convert to <%.*s>; its Python class was never created or has been deleted.";
sprintf(buffer, message, sizeof(buffer) - sizeof(message) - 1, target_typeid.name());
PyErr_SetString(PyExc_RuntimeError, buffer);
}
else
{
two_string_error(PyExc_TypeError, "extension class '%.*s' is not derived from '%.*s'.",
instance->ob_type->tp_name, target_class->tp_name);
}
}
void report_missing_instance_data(
ExtensionInstance* instance, // The object being converted
Class<ExtensionInstance>* target_class, // the extension class of the C++ type
const std::type_info& target_typeid) // The typeid of the C++ type
{
report_missing_instance_data(instance, target_class, target_typeid, false);
}
void report_missing_ptr_data(
ExtensionInstance* instance, // The object being converted
Class<ExtensionInstance>* target_class, // the extension class of the C++ type
const std::type_info& target_typeid) // The typeid of the C++ type
{
report_missing_instance_data(instance, target_class, target_typeid, true);
}
void report_missing_class_object(const std::type_info& info)
{
char buffer[256];
const char message[] = "Cannot convert <%.*s> to python; its Python class was never created or has been deleted.";
sprintf(buffer, message, sizeof(buffer) - sizeof(message) - 1, info.name());
PyErr_SetString(PyExc_RuntimeError, buffer);
throw ErrorAlreadySet();
}
void report_released_smart_pointer(const std::type_info& info)
{
char buffer[256];
const char message[] = "Converting from python, pointer or smart pointer to <%.*s> is NULL.";
sprintf(buffer, message, sizeof(buffer) - sizeof(message) - 1, info.name());
PyErr_SetString(PyExc_RuntimeError, buffer);
throw ArgumentError();
}
ReadOnlySetattrFunction::ReadOnlySetattrFunction(const char* name)
: m_name(name)
{
}
PyObject* ReadOnlySetattrFunction::do_call(PyObject* /*args*/, PyObject* /*keywords*/) const
{
PyErr_SetObject(PyExc_AttributeError, ("'" + m_name + "' attribute is read-only").get());
return 0;
}
const char* ReadOnlySetattrFunction::description() const
{
return "uncallable";
}
ExtensionClassBase::ExtensionClassBase(const char* name)
: Class<ExtensionInstance>(
extension_meta_class(), String(name), Tuple(), Dict())
{
}
void ExtensionClassBase::add_method(Function* method, const char* name)
{
add_method(PyPtr<Function>(method), name);
}
void ExtensionClassBase::add_method(PyPtr<Function> method, const char* name)
{
// If we have created a special Python base class which wraps C++ classes
// derived from T, the method should really be added there. Target will be
// that Python class object.
Class<ExtensionInstance>* target = (bases().size() == 0)
? this
: Downcast<Class<ExtensionInstance> >(bases()[0].get()).get();
// Add the attribute to the computed target
Function::add_to_namespace(method, name, target->dict().get());
// If it is a special member function it should be enabled both here and there.
enable_named_method(this, name);
}
void ExtensionClassBase::add_default_method(Function* method, const char* name)
{
add_default_method(PyPtr<Function>(method), name);
}
// A rather complicated thing is going on here in order to make a very specific
// class of cases work. When wrapping the following C++:
//
// struct Base {
// Base(); // will be constructed from Python
// virtual int f() const // might be called from C++
// { return 1; } // default implementation
// };
//
// struct Derived : Base {
// int f() const { return 0; } // overridden in C++
// };
//
// boost::shared_ptr<Base> factory(bool selector) {
// return boost::shared_ptr<Base>(selector ? new Base : new Derived);
// }
//
// Normally we would use the same Python ExtensionClass object to represent both
// Base and boost::shared_ptr<Base>, since they have essentially the same
// operations (see the comment on InstanceHolder in extclass_pygen.h for
// details). If there was no need to override Base::f() in Python, that would
// work fine. In this case, since f() is virtual, the programmer must provide a
// subclass of Base which calls back into Python:
//
// struct BaseCallback : Base {
// BaseCallback(PyObject* self) : m_self(self) {}
// int f() const { return py::Callback<int>::call_method(m_self, "f"); }
// static int default_f(const Base* self) const { self->Base::f(); }
// };
//
// default_f() is what gets registered under the name "f" in the "Base"
// ExtensionClass' attribute dict. When C++ calls f() on a wrapped instance of
// Base, we call back into Python to find the "f" attribute, which calls
// default_f() (unless it has been overridden in Python) and in turn the default
// implementation (Base::f()) is called.
//
// Now consider what happens when the Python programmer writes
// >>> factory(0).f()
//
// The shared_ptr<Derived> which is created on the C++ side is converted by
// to_python() into a Python instance of the "Base" ExtensionClass. Then Python
// looks up the "f" attribute, and finds the wrapper for default_f(), which it
// calls. That calls Base::f(), returning 1. What we really wanted was a call to
// Derived::f(), returning 0.
//
// In this case we actually need a different Python ExtensionClass to represent
// C++ subclasses of Base. When the first default method implementation is added
// to an ExtensionClass, we "push" all of the non-default methods up into a
// newly-created base class of the "Base" ExtensionClass, called
// "Base_base". "Base's" attribute dict contains only default method
// implementations.
//
// A Python call to factory() then results in an object of class "Base_base",
// whose "f" method is bound to Base::f() - since this is a virtual function
// pointer, the member function actually called is determined by the
// most-derived class that implements f().
//
// A Python call to Base() results in an object of class "Base" wrapping a
// BaseCallback object, whose "f" method is bound to BaseCallback::default_f()
// ...which calls Base::f() explicitly.
void ExtensionClassBase::add_default_method(PyPtr<Function> method, const char* name)
{
if (bases().size() == 0)
{
Class<ExtensionInstance>* new_base
= new Class<ExtensionInstance>(
extension_meta_class(), this->name() + String("_base"), Tuple(),
dict());
add_base(Ptr(as_object(new_base)));
// We have transferred everything in our dict into the base class, so
// clear our dict now. It will henceforth contain only default method
// implementations.
dict() = Dict();
}
Function::add_to_namespace(method, name, dict().get());
}
void ExtensionClassBase::add_constructor_object(Function* init_function)
{
add_method(init_function, "__init__");
}
void ExtensionClassBase::add_setter_method(Function* setter_, const char* name)
{
PyPtr<Function> setter(setter_);
add_method(setter, (detail::setattr_string() + name + "__").c_str());
}
void ExtensionClassBase::add_getter_method(Function* getter_, const char* name)
{
PyPtr<Function> getter(getter_);
add_method(getter, (detail::getattr_string() + name + "__").c_str());
}
} // namespace py

439
extclass.h Normal file
View File

@@ -0,0 +1,439 @@
// (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.
#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
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
prepend(Type<A4>::Id(),
prepend(Type<A5>::Id(),
Signature0()))))));
}
// 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));
}
};
#include "extclass_pygen.h"
template <class PtrType, class HeldType>
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;
};
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);
}
#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)
{
// 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;
}
} // namespace py
#endif // EXTENSION_CLASS_DWA052000_H_

3
extclass_d.cpp Normal file
View File

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

382
extclass_demo.cpp Normal file
View File

@@ -0,0 +1,382 @@
// (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.
#include "extclass_demo.h"
#include "class_wrapper.h"
#include <stdio.h> // used for portability on broken compilers
namespace extclass_demo {
FooCallback::FooCallback(PyObject* self, int x)
: Foo(x), m_self(self)
{
}
int FooCallback::add_len(const char* x) const
{
// Try to call the "add_len" method on the corresponding Python object.
return py::Callback<int>::call_method(m_self, "add_len", x);
}
// A function which Python can call in case bar is not overridden from
// Python. In true Python style, we use a free function taking an initial self
// parameter. This function anywhere needn't be a static member of the callback
// class. The only reason to do it this way is that Foo::add_len is private, and
// FooCallback is already a friend of Foo.
int FooCallback::default_add_len(const Foo* self, const char* x)
{
// Don't forget the Foo:: qualification, or you'll get an infinite
// recursion!
return self->Foo::add_len(x);
}
// Since Foo::pure() is pure virtual, we don't need a corresponding
// default_pure(). A failure to override it in Python will result in an
// exception at runtime when pure() is called.
const char* FooCallback::pure() const
{
return py::Callback<const char*>::call_method(m_self, "pure");
}
// The initializer for ExtensionClass<Foo,FooCallback> is entirely optional. It
// only affects the way that instances of this class _print_ in Python. If you
// need an absolutely predictable name for the type, use the
// initializer. Otherwise, C++ will generate an implementation-dependent
// representation of the type name, usually something like "class
// extclass_demo::Foo". I've supplied it here in part so that I can write
// doctests that exactly anticipate the generated error messages.
Foo::PythonClass::PythonClass()
: py::ExtensionClass<Foo,FooCallback>("Foo") // optional
{
def(py::Constructor<int>());
def(&Foo::mumble, "mumble");
def(&Foo::set, "set");
def(&Foo::call_pure, "call_pure");
def(&Foo::call_add_len, "call_add_len");
// This is the way we add a virtual function that has a default implementation.
def(&Foo::add_len, "add_len", &FooCallback::default_add_len);
// Since pure() is pure virtual, we are leaving it undefined.
}
BarPythonClass::BarPythonClass()
: py::ExtensionClass<Bar>("Bar") // optional
{
def(py::Constructor<int, int>());
def(&Bar::first, "first");
def(&Bar::second, "second");
def(&Bar::pass_baz, "pass_baz");
}
BazPythonClass::BazPythonClass()
: py::ExtensionClass<Baz>("Baz") // optional
{
def(py::Constructor<py::Void>());
def(&Baz::pass_bar, "pass_bar");
def(&Baz::clone, "clone");
def(&Baz::create_foo, "create_foo");
def(&Baz::get_foo_value, "get_foo_value");
def(&Baz::eat_baz, "eat_baz");
}
StringMapPythonClass::StringMapPythonClass()
: py::ExtensionClass<StringMap >("StringMap")
{
def(py::Constructor<py::Void>());
def(&StringMap::size, "__len__");
def(&get_item, "__getitem__");
def(&set_item, "__setitem__");
def(&del_item, "__delitem__");
}
int get_first(const IntPair& p)
{
return p.first;
}
void set_first(IntPair& p, int value)
{
p.first = -value;
}
void del_first(const IntPair&)
{
PyErr_SetString(PyExc_AttributeError, "first can't be deleted!");
throw py::ErrorAlreadySet();
}
IntPairPythonClass::IntPairPythonClass()
: py::ExtensionClass<IntPair>("IntPair")
{
def(py::Constructor<int, int>());
def(&getattr, "__getattr__");
def(&setattr, "__setattr__");
def(&delattr, "__delattr__");
def(&get_first, "__getattr__first__");
def(&set_first, "__setattr__first__");
def(&del_first, "__delattr__first__");
}
void IntPairPythonClass::setattr(IntPair& x, const std::string& name, int value)
{
if (name == "second")
{
x.second = value;
}
else
{
PyErr_SetString(PyExc_AttributeError, name.c_str());
throw py::ErrorAlreadySet();
}
}
void IntPairPythonClass::delattr(IntPair&, const char*)
{
PyErr_SetString(PyExc_AttributeError, "Attributes can't be deleted!");
throw py::ErrorAlreadySet();
}
int IntPairPythonClass::getattr(const IntPair& p, const std::string& s)
{
if (s == "second")
{
return p.second;
}
else
{
PyErr_SetString(PyExc_AttributeError, s.c_str());
throw py::ErrorAlreadySet();
}
#if defined(__MWERKS__) && __MWERKS__ <= 0x6000
return 0;
#endif
}
namespace { namespace file_local {
void throw_key_error_if_end(const StringMap& 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();
}
}
}} // namespace <anonymous>::file_local
const std::string& StringMapPythonClass::get_item(const StringMap& m, std::size_t key)
{
const StringMap::const_iterator p = m.find(key);
file_local::throw_key_error_if_end(m, p, key);
return p->second;
}
void StringMapPythonClass::set_item(StringMap& m, std::size_t key, const std::string& value)
{
m[key] = value;
}
void StringMapPythonClass::del_item(StringMap& m, std::size_t key)
{
const StringMap::iterator p = m.find(key);
file_local::throw_key_error_if_end(m, p, key);
m.erase(p);
}
//
// Show that polymorphism can work. a DerivedFromFoo object will be passed to
// Python in a smart pointer object.
//
class DerivedFromFoo : public Foo
{
public:
DerivedFromFoo(int x) : Foo(x) {}
private:
const char* pure() const
{ return "this was never pure!"; }
int add_len(const char*) const
{ return 1000; }
};
//
// function implementations
//
IntPair make_pair(int x, int y)
{
return std::make_pair(x, y);
}
const char* Foo::mumble()
{
return "mumble";
}
void Foo::set(long x)
{
m_x = x;
}
const char* Foo::call_pure()
{
return this->pure();
}
int Foo::call_add_len(const char* s) const
{
return this->add_len(s);
}
int Foo::add_len(const char* s) const // sum the held value and the length of s
{
return PY_CSTD_::strlen(s) + static_cast<int>(m_x);
}
boost::shared_ptr<Foo> Baz::create_foo()
{
return boost::shared_ptr<Foo>(new DerivedFromFoo(0));
}
// We can accept smart pointer parameters
int Baz::get_foo_value(boost::shared_ptr<Foo> foo)
{
return foo->call_add_len("");
}
// Show what happens in python when we take ownership from an auto_ptr
void Baz::eat_baz(std::auto_ptr<Baz> baz)
{
baz->clone(); // just do something to show that it is valid.
}
Baz Bar::pass_baz(Baz b)
{
return b;
}
std::string stringpair_repr(const StringPair& sp)
{
return "('" + sp.first + "', '" + sp.second + "')";
}
int stringpair_compare(const StringPair& sp1, const StringPair& sp2)
{
return sp1 < sp2 ? -1 : sp2 < sp1 ? 1 : 0;
}
py::String range_str(const Range& r)
{
char buf[200];
sprintf(buf, "(%d, %d)", r.m_start, r.m_finish);
return py::String(buf);
}
int range_compare(const Range& r1, const Range& r2)
{
int d = r1.m_start - r2.m_start;
if (d == 0)
d = r1.m_finish - r2.m_finish;
return d;
}
long range_hash(const Range& r)
{
return r.m_start * 123 + r.m_finish;
}
void init_module(py::Module& m)
{
m.add(new Foo::PythonClass);
m.add(new BarPythonClass);
m.add(new BazPythonClass);
m.add(new StringMapPythonClass);
m.add(new IntPairPythonClass);
m.def(make_pair, "make_pair");
m.add(new CompareIntPairPythonClass);
py::ClassWrapper<StringPair> string_pair(m, "StringPair");
string_pair.def(py::Constructor<std::string, std::string>());
string_pair.def_readonly(&StringPair::first, "first");
string_pair.def_read_write(&StringPair::second, "second");
string_pair.def(&stringpair_repr, "__repr__");
string_pair.def(&stringpair_compare, "__cmp__");
m.def(first_string, "first_string");
m.def(second_string, "second_string");
py::ClassWrapper<Range> range(m, "Range");
range.def(py::Constructor<int>());
range.def(py::Constructor<int, int>());
range.def((void (Range::*)(std::size_t))&Range::length, "__len__");
range.def((std::size_t (Range::*)() const)&Range::length, "__len__");
range.def(&Range::operator[], "__getitem__");
range.def(&Range::slice, "__getslice__");
range.def(&range_str, "__str__");
range.def(&range_compare, "__cmp__");
range.def(&range_hash, "__hash__");
range.def_readonly(&Range::m_start, "start");
range.def_readonly(&Range::m_finish, "finish");
}
void init_module()
{
py::Module demo("demo");
init_module(demo);
// Just for giggles, add a raw metaclass.
demo.add(new py::MetaClass<py::Instance>);
}
extern "C"
#ifdef _WIN32
__declspec(dllexport)
#endif
void initdemo()
{
try {
extclass_demo::init_module();
}
catch(...) {
py::handle_exception();
} // Need a way to report other errors here
}
CompareIntPairPythonClass::CompareIntPairPythonClass()
: py::ExtensionClass<CompareIntPair>("CompareIntPair")
{
def(py::Constructor<py::Void>());
def(&CompareIntPair::operator(), "__call__");
}
} // namespace extclass_demo
#if defined(_WIN32)
# include <windows.h>
extern "C" BOOL WINAPI DllMain ( HINSTANCE hInst, DWORD wDataSeg, LPVOID lpvReserved );
# ifdef PY_COMPILER_IS_MSVC
extern "C" void structured_exception_translator(unsigned int, EXCEPTION_POINTERS*)
{
throw;
}
# endif
BOOL WINAPI DllMain(
HINSTANCE, //hDllInst
DWORD fdwReason,
LPVOID // lpvReserved
)
{
# ifdef PY_COMPILER_IS_MSVC
_set_se_translator(structured_exception_translator);
#endif
return 1;
(void)fdwReason; // warning suppression.
}
#endif // _WIN32

231
extclass_demo.h Normal file
View File

@@ -0,0 +1,231 @@
// (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.
#ifndef EXTCLASS_DEMO_DWA052200_H_
# define EXTCLASS_DEMO_DWA052200_H_
//
// Example code demonstrating extension class usage
//
# include "extclass.h"
# include "callback.h"
# include <boost/utility.hpp>
# include <cstring>
# include <iostream>
# include <cstddef>
# include <string>
# include <map>
namespace extclass_demo {
//
// example: Foo, Bar, and Baz are C++ classes we want to wrap.
//
class Foo // prohibit copying, proving that it doesn't choke
: boost::noncopyable // our generation of to_python().
{
public: // constructor/destructor
Foo(int x) : m_x(x) {}
virtual ~Foo() {}
public: // non-virtual functions
const char* mumble(); // mumble something
void set(long x); // change the held value
// These two call virtual functions
const char* call_pure(); // call a pure virtual fuction
int call_add_len(const char* s) const; // virtual function with a default implementation
private:
// by default, sum the held value and the length of s
virtual int add_len(const char* s) const;
// Derived classes can do whatever they want here, but they must do something!
virtual const char* pure() const = 0;
public: // friend declarations
// If you have private virtual functions such as add_len which you want to
// override in Python and have default implementations, they must be
// accessible by the thing making the def() call on the ExtensionClass (in
// this case, the nested PythonClass itself), and by the C++ derived class
// which is used to cause the Python callbacks (in this case,
// FooCallback). See the definition of FooCallback::add_len()
struct PythonClass;
friend struct PythonClass;
friend class FooCallback;
private:
int m_x; // the held value
};
//
// Bar and Baz have mutually-recursive type conversion dependencies (see
// pass_xxx functions). I've done this to prove that it doesn't cause a
// problem for Python class definitions, which happen later.
//
// Bar and Baz functions are only virtual to increase the likelihood of a crash
// if I inadvertently use a pointer to garbage memory (a likely thing to test
// for considering the amount of type casting needed to translate to and from
// Python).
struct Baz;
struct Bar
{
Bar(int x, int y) : m_first(x), m_second(y) {}
virtual int first() const { return m_first; }
virtual int second() const { return m_second; }
virtual Baz pass_baz(Baz x);
int m_first, m_second;
};
struct Baz
{
virtual Bar pass_bar(const Bar& x) { return x; }
// We can return smart pointers
virtual std::auto_ptr<Baz> clone() { return std::auto_ptr<Baz>(new Baz(*this)); }
// This illustrates creating a polymorphic derived class of Foo
virtual boost::shared_ptr<Foo> create_foo();
// We can accept smart pointer parameters
virtual int get_foo_value(boost::shared_ptr<Foo>);
// Show what happens in python when we take ownership from an auto_ptr
virtual void eat_baz(std::auto_ptr<Baz>);
};
typedef std::map<std::size_t, std::string> StringMap;
typedef std::pair<int, int> IntPair;
IntPair make_pair(int, int);
typedef std::less<IntPair> CompareIntPair;
typedef std::pair<std::string, std::string> StringPair;
inline std::string first_string(const StringPair& x)
{
return x.first;
}
inline std::string second_string(const StringPair& x)
{
return x.second;
}
struct Range
{
Range(int x)
: m_start(x), m_finish(x) {}
Range(int start, int finish)
: m_start(start), m_finish(finish) {}
std::size_t length() const
{ return m_finish < m_start ? 0 : m_finish - m_start; }
void length(std::size_t new_length)
{ m_finish = m_start + new_length; }
int operator[](std::size_t n)
{ return m_start + n; }
Range slice(std::size_t start, std::size_t end)
{
if (start > length())
start = length();
if (end > length())
end = length();
return Range(m_start + start, m_start + end);
}
int m_start, m_finish;
};
////////////////////////////////////////////////////////////////////////
// //
// Begin wrapping code. Usually this would live in a separate header. //
// //
////////////////////////////////////////////////////////////////////////
// Since Foo has virtual functions which we want overriden in Python, we must
// derive FooCallback.
class FooCallback : public Foo
{
public:
// Note the additional constructor parameter "self", which is needed to
// allow function overriding from Python.
FooCallback(PyObject* self, int x);
friend struct PythonClass; // give it access to the functions below
private: // implementations of Foo virtual functions that are overridable in python.
int add_len(const char* x) const;
// A function which Python can call in case bar is not overridden from
// Python. In true Python style, we use a free function taking an initial
// self parameter. You can put this function anywhere; it needn't be a
// static member of the wrapping class.
static int default_add_len(const Foo* self, const char* x);
// Since Foo::pure() is pure virtual, we don't need a corresponding
// default_pure(). A failure to override it in Python will result in an
// exception at runtime when pure() is called.
const char* pure() const;
private: // Required boilerplate if functions will be overridden
PyObject* m_self; // No, we don't want a py::Ptr here, or we'd get an ownership cycle.
};
// Define the Python base class
struct Foo::PythonClass : py::ExtensionClass<Foo, FooCallback> { PythonClass(); };
// No virtual functions on Bar or Baz which are actually supposed to behave
// virtually from C++, so we'll rely on the library to define a wrapper for
// us. Even so, Python Class types for each type we're wrapping should be
// _defined_ here in a header where they can be seen by other extension class
// definitions, since it is the definition of the py::ExtensionClass<> that
// causes to_python/from_python conversion functions to be generated.
struct BarPythonClass : py::ExtensionClass<Bar> { BarPythonClass(); };
struct BazPythonClass : py::ExtensionClass<Baz> { BazPythonClass(); };
struct StringMapPythonClass
: py::ExtensionClass<StringMap>
{
StringMapPythonClass();
// These static functions implement the right argument protocols for
// implementing the Python "special member functions" for mapping on
// StringMap. Could just as easily be global functions.
static const std::string& get_item(const StringMap& m, std::size_t key);
static void set_item(StringMap& m, std::size_t key, const std::string& value);
static void del_item(StringMap& m, std::size_t key);
};
struct IntPairPythonClass
: py::ExtensionClass<IntPair>
{
IntPairPythonClass();
// The following could just as well be a free function; it implements the
// getattr functionality for IntPair.
static int getattr(const IntPair&, const std::string& s);
static void setattr(IntPair&, const std::string& name, int value);
static void delattr(IntPair&, const char* name);
};
struct CompareIntPairPythonClass
: py::ExtensionClass<CompareIntPair>
{
CompareIntPairPythonClass();
};
} // namespace extclass_demo
#endif // EXTCLASS_DEMO_DWA052200_H_

23
extclass_demo.py Normal file
View File

@@ -0,0 +1,23 @@
# A stand-in for the real extension module. We're just using this one to
# simulate it while we have all the parts linked together into cpp.dll
# using "import*-safe" imports
import sys
_sys = sys
del sys
import os
_os = os
del os
_savecwd = _os.getcwd()
if len(_sys.argv) > 1:
_os.chdir(_sys.argv[1])
try:
from cpp import *
finally:
if len(_sys.argv) > 1:
_os.chdir(_savecwd)

2
extclass_demo_d.cpp Normal file
View File

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

80
extclass_pygen.h Normal file
View File

@@ -0,0 +1,80 @@
// (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

161
functions.cpp Normal file
View File

@@ -0,0 +1,161 @@
// (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.
#include "functions.h"
#include "newtypes.h"
#include "singleton.h"
#include "objects.h"
#include "errors.h"
namespace py {
struct Function::TypeObject :
Singleton<Function::TypeObject, Callable<py::TypeObject<Function> > >
{
TypeObject() : SingletonBase(&PyType_Type) {}
};
void Function::add_to_namespace(PyPtr<Function> new_function, const char* name, PyObject* dict)
{
Dict d(Ptr(dict, Ptr::borrowed));
String key(name);
Ptr existing_object = d.get_item(key.reference());
if (existing_object.get() == 0)
{
d[key] = Ptr(new_function.get(), Ptr::borrowed);
}
else
{
if (existing_object->ob_type == TypeObject::singleton())
{
Function* f = static_cast<Function*>(existing_object.get());
while (f->m_overloads.get() != 0)
f = f->m_overloads.get();
f->m_overloads = new_function;
}
else
{
PyErr_SetObject(PyExc_RuntimeError,
(String("Attempt to overload ") + name
+ " failed. The existing attribute has type "
+ existing_object->ob_type->tp_name).get());
throw ErrorAlreadySet();
}
}
}
Function::Function()
: PythonObject(TypeObject::singleton())
{
}
PyObject* Function::call(PyObject* args, PyObject* keywords) const
{
for (const Function* f = this; f != 0; f = f->m_overloads.get())
{
PyErr_Clear();
try
{
PyObject* const result = f->do_call(args, keywords);
if (result != 0)
return result;
}
catch(const ArgumentError&)
{
}
}
if (m_overloads.get() == 0)
return 0;
PyErr_Clear();
String message("No overloaded functions match (");
Tuple arguments(Ptr(args, Ptr::borrowed));
for (std::size_t i = 0; i < arguments.size(); ++i)
{
if (i != 0)
message += ", ";
message += arguments[i]->ob_type->tp_name;
}
message += "). Candidates are:\n";
for (const Function* f1 = this; f1 != 0; f1 = f1->m_overloads.get())
{
if (f1 != this)
message += "\n";
message += f1->description();
}
PyErr_SetObject(PyExc_TypeError, message.get());
return 0;
}
Ptr BoundFunction::create(Ptr target, Ptr fn)
{
BoundFunction* result = free_list;
if (result != 0)
{
free_list = result->m_free_list_link;
result->m_target = target;
result->m_unbound_function = fn;
}
else
{
result = new BoundFunction(target, fn);
}
return Ptr(result, Ptr::new_ref);
}
struct BoundFunction::TypeObject :
Singleton<BoundFunction::TypeObject, Callable<py::TypeObject<BoundFunction> > >
{
TypeObject() : SingletonBase(&PyType_Type) {}
private: // TypeObject<BoundFunction> hook override
void dealloc(BoundFunction*) const;
};
BoundFunction::BoundFunction(Ptr target, Ptr fn)
: PythonObject(TypeObject::singleton()),
m_target(target),
m_unbound_function(fn),
m_free_list_link(0)
{
}
PyObject*
BoundFunction::call(PyObject* args, PyObject* keywords) const
{
// Build a new tuple which prepends the target to the arguments
Tuple tail_arguments(Ptr(args, Ptr::borrowed));
Ptr all_arguments(PyTuple_New(tail_arguments.size() + 1));
PyTuple_SET_ITEM(all_arguments.get(), 0, m_target.get());
Py_INCREF(m_target.get());
for (std::size_t i = 0; i < tail_arguments.size(); ++i)
{
PyTuple_SET_ITEM(all_arguments.get(), i + 1, tail_arguments[i].get());
Py_INCREF(tail_arguments[i].get());
}
return PyEval_CallObjectWithKeywords(m_unbound_function.get(), all_arguments.get(), keywords);
}
void BoundFunction::TypeObject::dealloc(BoundFunction* instance) const
{
instance->m_free_list_link = free_list;
free_list = instance;
instance->m_target.reset();
instance->m_unbound_function.reset();
}
BoundFunction* BoundFunction::free_list;
}

175
functions.h Normal file
View File

@@ -0,0 +1,175 @@
// (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.
#ifndef FUNCTIONS_DWA051400_H_
# define FUNCTIONS_DWA051400_H_
# include "pyconfig.h"
# include "wrap_python.h"
# include "pyptr.h"
# include "signatures.h"
# include "caller.h"
# include <boost/call_traits.hpp>
# include "objects.h"
# include "base_object.h"
# include <typeinfo>
namespace py {
class Function : public PythonObject
{
public:
Function();
// Function objects are reasonably rare, so we guess we can afford a virtual table.
// This cuts down on the number of distinct type objects which need to be defined.
virtual ~Function() {}
PyObject* call(PyObject* args, PyObject* keywords) const;
static void add_to_namespace(PyPtr<Function> f, const char* name, PyObject* dict);
private:
virtual PyObject* do_call(PyObject* args, PyObject* keywords) const = 0;
virtual const char* description() const = 0;
private:
struct TypeObject;
private:
PyPtr<Function> m_overloads;
};
template <class R, class F>
struct WrappedFunctionPointer : Function
{
typedef F PtrFun; // pointer-to--function or pointer-to-member-function
WrappedFunctionPointer(PtrFun pf)
: m_pf(pf) {}
private:
PyObject* do_call(PyObject* args, PyObject* keywords) const
{ return Caller<R>::call(m_pf, args, keywords); }
const char* description() const
{ return typeid(F).name(); }
private:
const PtrFun m_pf;
};
// A helper function for new_member_function(), below. Implements the core
// functionality once the return type has already been deduced. R is expected to
// be Type<X>, where X is the actual return type of pmf.
template <class F, class R>
Function* new_wrapped_function_aux(R, F pmf)
{
// We can't just use "typename R::Type" below because MSVC (incorrectly) pukes.
typedef typename R::Type ReturnType;
return new WrappedFunctionPointer<ReturnType, F>(pmf);
}
// Create and return a new member function object wrapping the given
// pointer-to-member function
template <class F>
inline Function* new_wrapped_function(F pmf)
{
// Deduce the return type and pass it off to the helper function above
return new_wrapped_function_aux(return_value(pmf), pmf);
}
// A function with a bundled "bound target" object. This is what is produced by
// the expression a.b where a is an Instance or ExtensionInstance object and b
// is a callable object not found in the instance namespace but on its class or
// a base class.
class BoundFunction : public PythonObject
{
public:
static Ptr create(Ptr target, Ptr fn);
BoundFunction(Ptr target, Ptr fn);
PyObject* call(PyObject*args, PyObject* keywords) const;
private:
struct TypeObject;
friend struct TypeObject;
Ptr m_target;
Ptr m_unbound_function;
private: // data members for allocation/deallocation optimization
BoundFunction* m_free_list_link;
static BoundFunction* free_list;
};
// Special functions designed to access data members of a wrapped C++ object.
template <class ClassType, class MemberType>
class GetterFunction : public Function
{
public:
typedef MemberType ClassType::* PointerToMember;
GetterFunction(PointerToMember pm)
: m_pm(pm) {}
private:
PyObject* do_call(PyObject* args, PyObject* keywords) const;
const char* description() const
{ return typeid(MemberType (*)(const ClassType&)).name(); }
private:
PointerToMember m_pm;
};
template <class ClassType, class MemberType>
class SetterFunction : public Function
{
public:
typedef MemberType ClassType::* PointerToMember;
SetterFunction(PointerToMember pm)
: m_pm(pm) {}
private:
PyObject* do_call(PyObject* args, PyObject* keywords) const;
const char* description() const
{ return typeid(void (*)(const ClassType&, const MemberType&)).name(); }
private:
PointerToMember m_pm;
};
template <class ClassType, class MemberType>
PyObject* GetterFunction<ClassType, MemberType>::do_call(
PyObject* args, PyObject* /* keywords */) const
{
PyObject* self;
if (!PyArg_ParseTuple(args, const_cast<char*>("O"), &self))
return 0;
return to_python(
from_python(self, Type<const ClassType*>())->*m_pm);
}
template <class ClassType, class MemberType>
PyObject* SetterFunction<ClassType, MemberType>::do_call(
PyObject* args, PyObject* /* keywords */) const
{
PyObject* self;
PyObject* value;
if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &self, &value))
return 0;
typedef typename boost::call_traits<MemberType>::const_reference ExtractType;
from_python(self, Type<ClassType*>())->*m_pm
= from_python(value, Type<ExtractType>());
return none();
}
}
#endif // FUNCTIONS_DWA051400_H_

2
functions_d.cpp Normal file
View File

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

40
gcc.mak Normal file
View File

@@ -0,0 +1,40 @@
LIBSRC = \
extclass.cpp \
init_function.cpp \
py.cpp \
module.cpp \
subclass.cpp \
functions.cpp \
newtypes.cpp \
objects.cpp
LIBOBJ = $(LIBSRC:.cpp=.o)
OBJ = $(LIBOBJ) extclass_demo.o
INC = -I/home/koethe/include -I/home/koethe/C++/boost -I/home/koethe/python/include/python1.5
%.o: %.cpp
g++ -fPIC $(INC) -c $*.cpp
%.d: %.cpp
@echo creating $@
@set -e; g++ -M $(INC) -c $*.cpp \
| sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \
[ -s $@ ] || rm -f $@
demo: extclass_demo.o libpycpp.a
g++ -shared -o demomodule.so extclass_demo.o -L. -lpycpp
python test_extclass.py
clean:
rm -rf *.o *.so *.a *.d *.pyc *.bak a.out
libpycpp.a: $(LIBOBJ)
rm -f libpycpp.a
ar cq libpycpp.a $(LIBOBJ)
DEP = $(OBJ:.o=.d)
ifneq "$(MAKECMDGOALS)" "clean"
include $(DEP)
endif

26
gen_all.py Normal file
View File

@@ -0,0 +1,26 @@
from gen_callback import *
from gen_caller import *
from gen_init_function import *
from gen_signatures import *
from gen_singleton import *
from gen_extclass import *
def gen_all(args):
open('callback.h', 'w').write(gen_callback(args))
open('caller.h', 'w').write(gen_caller(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))
if __name__ == '__main__':
import sys
if len(sys.argv) == 1:
args = 5
else:
args = int(sys.argv[1])
print gen_all(args)

71
gen_callback.py Normal file
View File

@@ -0,0 +1,71 @@
from gen_function import *
import string
def gen_callback(args):
# A template for the call_method function which we're going to generate
call_method = '''%{ template <%(class A%n%:, %)>
%} static %1 call_method(PyObject* self, const char* name%(, const A%n& a%n%))
{ %2PyEval_CallMethod(self, const_cast<char*>(name), const_cast<char*>("(%(N%))")%(, to_python(a%n)%))%3; }
'''
non_void = ('R', 'return from_python(expect_non_null(', '), Type<R>())')
void = ('void', 'expect_and_absorb_non_null(', ')')
return (
"""// (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 was generated for %d-argument python callbacks by gen_callback.py
#ifndef CALLBACK_DWA_052100_H_
# define CALLBACK_DWA_052100_H_
# include "pyconfig.h"
# include "py.h"
namespace py {
// Just like the above, except we decrement p's reference count instead of returning it.
void expect_and_absorb_non_null(PyObject* p);
// Calling Python from C++
template <class R>
struct Callback
{
""" % args
+ gen_functions(call_method, args, 'R', 'return from_python(expect_non_null(', '), Type<R>())')
+
"""};
// This specialization wouldn't be needed, but MSVC6 doesn't correctly allow the following:
// void g();
// void f() { return g(); }
template <>
struct Callback<void>
{
"""
+ gen_functions(call_method, args, 'void', 'expect_and_absorb_non_null(', ')')
+
"""};
} // namespace py
#endif // CALLBACK_DWA_052100_H_
""")
if __name__ == '__main__':
import sys
if len(sys.argv) == 1:
args = 5
else:
args = int(sys.argv[1])
print gen_callback(args)

138
gen_caller.py Normal file
View File

@@ -0,0 +1,138 @@
# (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.
from gen_function import *
import string
header = '''// (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 generated for %d-argument member functions and %d-argument free
// functions by gen_caller.py
'''
body_sections = (
'''
#ifndef CALLER_DWA05090_H_
# define CALLER_DWA05090_H_
# include "pyconfig.h"
# include "wrap_python.h"
# include <boost/config.hpp>
# include "signatures.h"
# include "none.h"
namespace py {
// Calling C++ from Python
template <class R>
struct Caller
{
''',
'''
''',
''' // Free functions
''',
'''};
template <>
struct Caller<void>
{
''',
'''
''',
'''
// Free functions
''',
'''};
}
#endif
''')
#'
member_function = ''' template <class T%(, class A%n%)>
static PyObject* call(%1 (T::*pmf)(%(A%n%:, %))%2, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
%( PyObject* a%n;
%) if (!PyArg_ParseTuple(args, const_cast<char*>("O%(O%)"), &self%(, &a%n%)))
return 0;
T& target = from_python(self, Type<T&>());
%3(target.*pmf)(%(from_python(a%n, Type<A%n>())%:,
%))%4
}
'''
free_function = '''%{ template <%(class A%n%:, %)>
%} static PyObject* call(%1 (*f)(%(A%n%:, %)), PyObject* args, PyObject* /* keywords */ ) {
%( PyObject* a%n;
%) if (!PyArg_ParseTuple(args, const_cast<char*>("%(O%)")%(, &a%n%)))
return 0;
%2f(%(from_python(a%n, Type<A%n>())%:,
%))%3
}
'''
def gen_caller(member_function_args, free_function_args = None):
if free_function_args is None:
free_function_args = member_function_args
return_none = ''';
return none();'''
return (header % (member_function_args, free_function_args)
+ body_sections[0]
+ gen_functions(member_function, member_function_args,
'R', '', 'return to_python(', ');')
+ body_sections[1]
+ gen_functions(member_function, member_function_args,
'R', ' const', 'return to_python(', ');')
+ body_sections[2]
+ gen_functions(free_function, free_function_args,
'R', 'return to_python(', ');')
+ body_sections[3]
# specialized part for void return values begins here
+ gen_functions(member_function, member_function_args,
'void', '', '', return_none)
+ body_sections[4]
+ gen_functions(member_function, member_function_args,
'void', ' const', '', return_none)
+ body_sections[5]
+ gen_functions(free_function, free_function_args,
'void', '', return_none)
+ body_sections[6]
)
if __name__ == '__main__':
import sys
if len(sys.argv) == 1:
member_function_args = 5
free_function_args = 6
else:
member_function_args = int(sys.argv[1])
if len(sys.argv) > 2:
free_function_args = int(sys.argv[2])
else:
free_function_args = member_function_args
print gen_caller(member_function_args, free_function_args)

81
gen_extclass.py Normal file
View File

@@ -0,0 +1,81 @@
from gen_function import *
import string
def gen_extclass(args):
held_instance = """%{
template <%(class A%n%:, %)>%}
HeldInstance(PyObject* p%(, const A%n%& a%n%)) : T(%(a%n%:, %)), m_self(p) {}"""
instance_value_holder = """%{
template <%(class A%n%:, %)>%}
InstanceValueHolder(ExtensionInstance* p%(, const A%n& a%n%)) :
m_held(p%(, a%n%)) {}"""
return (
"""// (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 %d-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:""" % args
+ gen_functions(held_instance, args)
+ """
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; }
"""
+ gen_functions(instance_value_holder, args)
+ """
private:
Wrapper m_held;
};
#endif
""")
if __name__ == '__main__':
import sys
if len(sys.argv) == 1:
args = 5
else:
args = int(sys.argv[1])
print gen_extclass(args)

184
gen_function.py Normal file
View File

@@ -0,0 +1,184 @@
import string
def _find(s, sub, start=0, end=None):
"""Just like string.find, except it returns end or len(s) when not found.
"""
if end == None:
end = len(s)
pos = string.find(s, sub, start, end)
if pos < 0:
return end
else:
return pos
def _gen_common_key(key, n, args):
if len(key) > 0 and key in '123456789':
return str(args[int(key) - 1])
elif key == 'x':
return str(n)
else:
return key
def _gen_arg(template, n, args, delimiter = '%'):
result = ''
i = 0
while i < len(template): # until the template is consumed
# consume everything up to the first delimiter
delimiter_pos = _find(template, delimiter, i)
result = result + template[i:delimiter_pos]
# The start position of whatever comes after the delimiter+key
start = delimiter_pos + 2
key = template[start - 1 : start] # the key character. If there were no
# delimiters left, key will be empty
if key == 'n':
result = result + `n`
else:
result = result + _gen_common_key(key, n, args)
i = start
return result
def gen_function(template, n, *args, **keywords):
r"""gen_function(template, n, [args...] ) -> string
Generate a function declaration based on the given template.
Sections of the template between '%(', '%)' pairs are repeated n times. If '%:'
appears in the middle, it denotes the beginning of a delimiter.
Sections of the template between '%{', '%}' pairs are ommitted if n == 0.
%n is transformed into the string representation of 1..n for each repetition
of n.
%x, where x is a digit, is transformed into the corresponding additional
argument.
for example,
>>> gen_function('%1 abc(%(int a%n%:, %));%{ // all args are ints%}', 2, 'void')
'void abc(int a1, int a2); // all args are ints'
>>> gen_function('%1 abc(%(int a%n%:, %));%{ // all args are ints%}', 0, 'x')
'x abc();'
>>> template = ''' template <class T%(, class A%n%)>
... static PyObject* call( %1(T::*pmf)(%(A%n%:, %))%2, PyObject* args, PyObject* /* keywords */ ) {
... PyObject* self;
... %( PyObject* a%n;
... %) if (!PyArg_ParseTuple(args, const_cast<char*>("O%(O%)"), &self%(, &a%n%)))
... return 0;
... T& target = from_python(self, Type<T&>());
... %3to_python((target.*pmf)(%(
... from_python(a%n, Type<A%n>())%:,%)
... ));%4
... }'''
>>> print gen_function(template, 0, 'R ', '', 'return ', '')
template <class T>
static PyObject* call( R (T::*pmf)(), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
if (!PyArg_ParseTuple(args, const_cast<char*>("O"), &self))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(
));
}
>>> print gen_function(template, 2, 'R ', '', 'return ', '')
template <class T, class A1, class A2>
static PyObject* call( R (T::*pmf)(A1, A2), PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOO"), &self, &a1, &a2))
return 0;
T& target = from_python(self, Type<T&>());
return to_python((target.*pmf)(
from_python(a1, Type<A1>()),
from_python(a2, Type<A2>())
));
}
>>> print gen_function(template, 3, 'void ', ' const', '', '\n'+8*' ' + 'return none();')
template <class T, class A1, class A2, class A3>
static PyObject* call( void (T::*pmf)(A1, A2, A3) const, PyObject* args, PyObject* /* keywords */ ) {
PyObject* self;
PyObject* a1;
PyObject* a2;
PyObject* a3;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOO"), &self, &a1, &a2, &a3))
return 0;
T& target = from_python(self, Type<T&>());
to_python((target.*pmf)(
from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>())
));
return none();
}
"""
delimiter = keywords.get('delimiter', '%')
result = ''
i = 0
while i < len(template): # until the template is consumed
# consume everything up to the first delimiter
delimiter_pos = _find(template, delimiter, i)
result = result + template[i:delimiter_pos]
# The start position of whatever comes after the delimiter+key
start = delimiter_pos + 2
key = template[start - 1 : start] # the key character. If there were no
# delimiters left, key will be empty
pairs = { '(':')', '{':'}' }
if key in pairs.keys():
end = string.find(template, delimiter + pairs[key], start)
assert end >= 0, "Matching '" + delimiter + pairs[key] +"' not found!"
delimiter_pos = end
if key == '{':
if n > 0:
result = result + gen_function(template[start:end], n, args, delimiter)
else:
separator_pos = _find(template, delimiter + ':', start, end)
separator = template[separator_pos+2 : end]
for x in range(1, n + 1):
result = result + _gen_arg(template[start:separator_pos], x, args,
delimiter)
if x != n:
result = result + separator
else:
result = result + _gen_common_key(key, n, args)
i = delimiter_pos + 2
return result
def gen_functions(template, n, *args):
r"""gen_functions(template, n, [args...]) -> string
Call gen_function repeatedly with from 0..n and the given optional
arguments.
>>> print gen_functions('%1 abc(%(int a%n%:, %));%{ // all args are ints%}\n', 2, 'void'),
void abc();
void abc(int a1); // all args are ints
void abc(int a1, int a2); // all args are ints
"""
result = ''
for x in range(n + 1):
result = result + apply(gen_function, (template, x) + args)
return result
if __name__ == '__main__':
import doctest
doctest.testmod()

82
gen_init_function.py Normal file
View File

@@ -0,0 +1,82 @@
from gen_function import *
import string
def gen_init_function(args):
return (
"""// (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 was generated for %d-argument constructors by gen_init_function.py
#ifndef INIT_FUNCTION_DWA052000_H_
# define INIT_FUNCTION_DWA052000_H_
# include "pyconfig.h"
# include "functions.h"
# include "signatures.h"
# include <typeinfo>
namespace py {
class ExtensionInstance;
class InstanceHolderBase;
class Init;
"""
+ gen_functions('template <class T%(, class A%n%)> struct Init%x;\n', args)
+ """
template <class T>
struct InitFunction
{
""" + gen_functions("""%{
template <%(class A%n%:, %)>
%} static Init* create(Signature%x%{<%(A%n%:, %)>%})
{ return new Init%x<T%(, A%n%)>; }
""", args)+"""};
class Init : public Function
{
private: // override Function hook
PyObject* do_call(PyObject* args, PyObject* keywords) const;
private:
virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* tail_args, PyObject* keywords) const = 0;
};
""" + gen_functions("""
template <class T%(, class A%n%)>
struct Init%x : Init
{
virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* args, PyObject* /*keywords*/) const
{
%(PyObject* a%n;
%)if (!PyArg_ParseTuple(args, const_cast<char*>("%(O%)")%(, &a%n%)))
throw ArgumentError();
return new T(self%(,
from_python(a%n, Type<A%n>())%)
);
}
const char* description() const
{ return typeid(void (*)(%(A%n%:, %))).name(); }
};""", args) + """
}
#endif // INIT_FUNCTION_DWA052000_H_
""")
if __name__ == '__main__':
import sys
if len(sys.argv) == 1:
args = 5
else:
args = int(sys.argv[1])
print gen_init_function(args)

152
gen_signatures.py Normal file
View File

@@ -0,0 +1,152 @@
from gen_function import *
import string
def gen_struct_signatures(args):
result = ''
for n in range(args, -1, -1):
result = (
result + gen_function("""%{template <%(class T%n%:, %)>
%}struct Signature%x {};
""", n)
# + ((n == args) and [""] or
# [gen_function("""
# template <class X>
# static inline Signature%1<X%(, T%n%)> prepend(Type<X>)
# { return Signature%1<X%(, T%n%)>(); }""",
# n, (str(n+1),))
# ]
# )[0]
#
# + ((n != 0) and [""] or
# ["""
# // This one terminates the chain. Prepending Void to the head of a Void
# // signature results in a Void signature again.
# static inline Signature0 prepend(Void) { return Signature0(); }"""]
# )[0]
# + """
#};
#
#"""
+ ((n == args) and [""] or
[gen_function(
"""template <%(class T%n%, %)class X>
inline Signature%1<X%(, T%n%)> prepend(Type<X>, Signature%x%{<%(T%n%:, %)>%})
{ return Signature%1<X%(, T%n%)>(); }
""", n, str(n+1))
]
)[0]
)
return result
def gen_signatures(args):
return (
"""// (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 by gen_signatures.py for %d arguments.
#ifndef SIGNATURES_DWA050900_H_
# define SIGNATURES_DWA050900_H_
# include "pyconfig.h"
namespace py {
// A stand-in for the built-in void. This one can be passed to functions and
// (under MSVC, which has a bug, be used as a default template type parameter).
struct Void {};
// An envelope in which type information can be delivered for the purposes
// of selecting an overloaded from_python() function. This is needed to work
// around MSVC's lack of partial specialiation/ordering. Where normally we'd
// want to form a function call like void f<const T&>(), We instead pass
// Type<const T&> as one of the function parameters to select a particular
// overload.
//
// The Id typedef helps us deal with the lack of partial ordering by generating
// unique types for constructor signatures. In general, Type<T>::Id is Type<T>,
// but Type<Void>::Id is just Void.
template <class T>
struct Type
{
typedef Type Id;
};
template <>
struct Type<Void>
{
typedef Void Id;
};
// These basically encapsulate a chain of types, , used to make the syntax of
// add(Constructor<T1, ...>()) work. We need to produce a unique type for each number
// of non-default parameters to Constructor<>. Q: why not use a recursive
// formulation for infinite extensibility? A: MSVC6 seems to choke on constructs
// that involve recursive template nesting.
//
// Signature chaining
""" % args
+ gen_struct_signatures(args)
+ """
// This one terminates the chain. Prepending Void to the head of a Void
// signature results in a Void signature again.
inline Signature0 prepend(Void, Signature0) { return Signature0(); }
"""
+ gen_function("""
template <%(class A%n% = Void%:, %)>
struct Constructor
{
};
""", args)
+ """
// Return value extraction:
// This is just another little envelope for carrying a typedef (see Type,
// above). I could have re-used Type, but that has a very specific purpose. I
// thought this would be clearer.
template <class T>
struct ReturnValue { typedef T Type; };
// free functions"""
+ gen_functions("""
template <class R%(, class A%n%)>
ReturnValue<R> return_value(R (*)(%(A%n%:, %))) { return ReturnValue<R>(); }
""", args)
+
"""
// TODO(?): handle 'const void'
// member functions"""
+ gen_functions("""
template <class R, class T%(, class A%n%)>
ReturnValue<R> return_value(R (T::*)(%(A%n%:, %))) { return ReturnValue<R>(); }
""", args)
+ gen_functions("""
template <class R, class T%(, class A%n%)>
ReturnValue<R> return_value(R (T::*)(%(A%n%:, %)) const) { return ReturnValue<R>(); }
""", args)
+ """
}
#endif
""")
if __name__ == '__main__':
import sys
if len(sys.argv) == 1:
args = 5
else:
args = int(sys.argv[1])
print gen_signatures(args)

58
gen_singleton.py Normal file
View File

@@ -0,0 +1,58 @@
from gen_function import *
import string
def gen_singleton(args):
return (
"""// (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.
#ifndef SINGLETON_DWA051900_H_
# define SINGLETON_DWA051900_H_
# include "pyconfig.h"
namespace py {
struct Empty {};
template <class Derived, class Base = Empty>
struct Singleton : Base
{
typedef Singleton SingletonBase; // Convenience type for derived class constructors
static Derived* singleton();
// Pass-through constructors
"""
+ gen_functions("""%{
template <%(class A%n%:, %)>
%} Singleton(%(const A%n& a%n%:, %)) : Base(%(a%n%:, %)) {}
""", args)
+ """
};
template <class Derived, class Base>
Derived* Singleton<Derived,Base>::singleton()
{
static Derived x;
return &x;
}
}
#endif
""")
if __name__ == '__main__':
import sys
if len(sys.argv) == 1:
args = 5
else:
args = int(sys.argv[1])
print gen_singleton(args)

36
init_function.cpp Normal file
View File

@@ -0,0 +1,36 @@
// (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.
#include "init_function.h"
#include "objects.h"
#include "extclass.h"
#include <utility>
namespace py {
PyObject* Init::do_call(PyObject* args_, PyObject* keywords) const
{
Tuple args(Ptr(args_, Ptr::borrowed));
if (args[0]->ob_type->ob_type != extension_meta_class())
{
PyErr_SetString(PyExc_TypeError, "argument 1 to __init__ must be an ExtensionInstance");
return 0;
}
ExtensionInstance *self = static_cast<ExtensionInstance*>(args[0].get());
Tuple ctor_args = args.slice(1, args.size());
std::auto_ptr<InstanceHolderBase> result(
create_holder(self, ctor_args.get(), keywords));
self->add_implementation(result);
return none();
}
}

184
init_function.h Normal file
View File

@@ -0,0 +1,184 @@
// (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 was generated for %d-argument constructors by gen_init_function.py
#ifndef INIT_FUNCTION_DWA052000_H_
# define INIT_FUNCTION_DWA052000_H_
# include "pyconfig.h"
# include "functions.h"
# include "signatures.h"
# include <typeinfo>
namespace py {
class ExtensionInstance;
class InstanceHolderBase;
class Init;
template <class T> struct Init0;
template <class T, class A1> struct Init1;
template <class T, class A1, class A2> struct Init2;
template <class T, class A1, class A2, class A3> struct Init3;
template <class T, class A1, class A2, class A3, class A4> struct Init4;
template <class T, class A1, class A2, class A3, class A4, class A5> struct Init5;
template <class T>
struct InitFunction
{
static Init* create(Signature0)
{ return new Init0<T>; }
template <class A1>
static Init* create(Signature1<A1>)
{ return new Init1<T, A1>; }
template <class A1, class A2>
static Init* create(Signature2<A1, A2>)
{ return new Init2<T, A1, A2>; }
template <class A1, class A2, class A3>
static Init* create(Signature3<A1, A2, A3>)
{ return new Init3<T, A1, A2, A3>; }
template <class A1, class A2, class A3, class A4>
static Init* create(Signature4<A1, A2, A3, A4>)
{ return new Init4<T, A1, A2, A3, A4>; }
template <class A1, class A2, class A3, class A4, class A5>
static Init* create(Signature5<A1, A2, A3, A4, A5>)
{ return new Init5<T, A1, A2, A3, A4, A5>; }
};
class Init : public Function
{
private: // override Function hook
PyObject* do_call(PyObject* args, PyObject* keywords) const;
private:
virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* tail_args, PyObject* keywords) const = 0;
};
template <class T>
struct Init0 : Init
{
virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* args, PyObject* /*keywords*/) const
{
if (!PyArg_ParseTuple(args, const_cast<char*>("")))
throw ArgumentError();
return new T(self
);
}
const char* description() const
{ return typeid(void (*)()).name(); }
};
template <class T, class A1>
struct Init1 : Init
{
virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* args, PyObject* /*keywords*/) const
{
PyObject* a1;
if (!PyArg_ParseTuple(args, const_cast<char*>("O"), &a1))
throw ArgumentError();
return new T(self,
from_python(a1, Type<A1>())
);
}
const char* description() const
{ return typeid(void (*)(A1)).name(); }
};
template <class T, class A1, class A2>
struct Init2 : Init
{
virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* args, PyObject* /*keywords*/) const
{
PyObject* a1;
PyObject* a2;
if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &a1, &a2))
throw ArgumentError();
return new T(self,
from_python(a1, Type<A1>()),
from_python(a2, Type<A2>())
);
}
const char* description() const
{ return typeid(void (*)(A1, A2)).name(); }
};
template <class T, class A1, class A2, class A3>
struct Init3 : Init
{
virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* args, PyObject* /*keywords*/) const
{
PyObject* a1;
PyObject* a2;
PyObject* a3;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOO"), &a1, &a2, &a3))
throw ArgumentError();
return new T(self,
from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>())
);
}
const char* description() const
{ return typeid(void (*)(A1, A2, A3)).name(); }
};
template <class T, class A1, class A2, class A3, class A4>
struct Init4 : Init
{
virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* args, PyObject* /*keywords*/) const
{
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOO"), &a1, &a2, &a3, &a4))
throw ArgumentError();
return new T(self,
from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>())
);
}
const char* description() const
{ return typeid(void (*)(A1, A2, A3, A4)).name(); }
};
template <class T, class A1, class A2, class A3, class A4, class A5>
struct Init5 : Init
{
virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* args, PyObject* /*keywords*/) const
{
PyObject* a1;
PyObject* a2;
PyObject* a3;
PyObject* a4;
PyObject* a5;
if (!PyArg_ParseTuple(args, const_cast<char*>("OOOOO"), &a1, &a2, &a3, &a4, &a5))
throw ArgumentError();
return new T(self,
from_python(a1, Type<A1>()),
from_python(a2, Type<A2>()),
from_python(a3, Type<A3>()),
from_python(a4, Type<A4>()),
from_python(a5, Type<A5>())
);
}
const char* description() const
{ return typeid(void (*)(A1, A2, A3, A4, A5)).name(); }
};
}
#endif // INIT_FUNCTION_DWA052000_H_

2
init_function_d.cpp Normal file
View File

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

48
makefile Normal file
View File

@@ -0,0 +1,48 @@
#########################################################
#
# 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

39
module.cpp Normal file
View File

@@ -0,0 +1,39 @@
// (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.
#include "module.h"
namespace py {
Module::Module(const char* name)
: m_module(Py_InitModule(const_cast<char*>(name), initial_methods))
{
}
void
Module::add(Function* x, const char* name)
{
PyPtr<Function> f(x); // First take possession of the object.
Function::add_to_namespace(f, name, PyModule_GetDict(m_module));
}
void Module::add(Ptr x, const char* name)
{
PyObject* dictionary = PyModule_GetDict( m_module );
PyDict_SetItemString(dictionary, const_cast<char*>(name), x.get());
};
void Module::add(PyTypeObject* x, const char* name /*= 0*/)
{
this->add(Ptr(as_object(x), Ptr::new_ref),
name ? name : x->tp_name);
}
PyMethodDef Module::initial_methods[] = { { 0, 0, 0, 0 } };
}

43
module.h Normal file
View File

@@ -0,0 +1,43 @@
// (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.
#ifndef MODULE_DWA051000_H_
# define MODULE_DWA051000_H_
# include "pyconfig.h"
# include "pyptr.h"
# include "functions.h"
namespace py {
class ExtensionType;
class Module
{
public:
Module(const char* name);
void add(Function* x, const char* name);
void add(PyTypeObject* x, const char* name = 0);
void add(Ptr x, const char*name);
template <class Fn>
void def(Fn fn, const char* name)
{
add(new_wrapped_function(fn), name);
}
private:
PyObject* m_module;
static PyMethodDef initial_methods[1];
};
}
#endif

2
module_d.cpp Normal file
View File

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

628
newtypes.cpp Normal file
View File

@@ -0,0 +1,628 @@
// (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.
#include "newtypes.h"
#include "pyptr.h" // for handle_exception()
#include "module.h"
#include "none.h"
#include <cstring>
#include <vector>
#include <cstddef>
#include <boost/smart_ptr.hpp>
#include "objects.h"
namespace py {
namespace detail {
UniquePodSet& UniquePodSet::instance()
{
static UniquePodSet me;
return me;
}
struct UniquePodSet::Compare
{
bool operator()(const std::pair<const char*, const char*>& x1,
const std::pair<const char*, const char*>& x2) const
{
const std::ptrdiff_t n1 = x1.second - x1.first;
const std::ptrdiff_t n2 = x2.second - x2.first;
return n1 < n2 || n1 == n2 && PY_CSTD_::memcmp(x1.first, x2.first, n1) < 0;
}
};
const void* UniquePodSet::get_element(const void* buffer, std::size_t size)
{
const Holder element(static_cast<const char*>(buffer),
static_cast<const char*>(buffer) + size);
const Storage::iterator found
= std::lower_bound(m_storage.begin(), m_storage.end(), element, Compare());
if (found != m_storage.end() && !Compare()(element, *found))
return found->first;
std::size_t length = element.second - element.first;
char* base_address = new char[length];
try {
PY_CSTD_::memcpy(base_address, element.first, length);
Holder new_element(base_address, base_address + length);
m_storage.insert(found, new_element);
}
catch(...) {
delete[] base_address;
throw;
}
return base_address;
}
UniquePodSet::~UniquePodSet()
{
for (Storage::const_iterator p = m_storage.begin(), finish = m_storage.end();
p != finish; ++p)
{
delete[] const_cast<char*>(p->first);
}
}
} // namespace detail
template <class MethodStruct, class MemberPtr, class Fn>
static MethodStruct* enable_method(const MethodStruct* base, MemberPtr p, Fn f)
{
MethodStruct new_value;
if (base != 0)
new_value = *base;
else
PY_CSTD_::memset(&new_value, 0, sizeof(PyMappingMethods));
new_value.*p = 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.
extern "C" {
static PyObject* do_instance_repr(PyObject* instance)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_repr(instance);
}
catch(...)
{
handle_exception();
return 0;
}
}
static int do_instance_compare(PyObject* instance, PyObject* other)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_compare(instance, other);
}
catch(...)
{
handle_exception();
return -1;
}
}
static PyObject* do_instance_str(PyObject* instance)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_str(instance);
}
catch(...)
{
handle_exception();
return 0;
}
}
static long do_instance_hash(PyObject* instance)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_hash(instance);
}
catch(...)
{
handle_exception();
return -1;
}
}
static PyObject* do_instance_call(PyObject* instance, PyObject* args, PyObject* keywords)
{
try
{
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_call(instance, args, keywords);
}
catch(...)
{
handle_exception();
return 0;
}
}
static void do_instance_dealloc(PyObject* instance)
{
try
{
static_cast<TypeObjectBase*>(instance->ob_type)
->instance_dealloc(instance);
}
catch(...)
{
assert(!"exception during destruction!");
handle_exception();
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
static PyObject* do_instance_sq_item(PyObject* instance, int index)
{
try
{
const PyTypeObject* const type = instance->ob_type;
// This is an extension to standard class behavior. If sequence_length
// is implemented and n >= sequence_length(), raise an IndexError. That
// keeps users from having to worry about raising it themselves
if (type->tp_as_sequence != 0 && type->tp_as_sequence->sq_length != 0
&& index >= type->tp_as_sequence->sq_length(instance))
{
PyErr_SetString(PyExc_IndexError, type->tp_name);
return 0;
}
return static_cast<TypeObjectBase*>(instance->ob_type)
->instance_sequence_item(instance, index);
}
catch(...)
{
handle_exception();
return 0;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
}
namespace detail {
template <std::size_t> struct category_type;
#define DECLARE_CAPABILITY_TYPE(field, sub_structure) \
template <> \
struct category_type<(offsetof(PyTypeObject, tp_as_##field))> \
{ \
typedef sub_structure type; \
}
#define CAPABILITY(field) \
{ 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), \
Dispatch(do_instance_##field), \
sizeof(category_type<offsetof(PyTypeObject, tp_as_##category)>::type), \
offsetof(AllMethods, category) \
}
DECLARE_CAPABILITY_TYPE(mapping, PyMappingMethods);
DECLARE_CAPABILITY_TYPE(sequence, PySequenceMethods);
const CapabilityEntry capabilities[] = {
CAPABILITY(hash),
CAPABILITY(call),
CAPABILITY(str),
CAPABILITY(getattr),
CAPABILITY(setattr),
CAPABILITY(compare),
CAPABILITY(repr),
CAPABILITY2(mapping, mp_length),
CAPABILITY2(mapping, mp_subscript),
CAPABILITY2(mapping, mp_ass_subscript),
CAPABILITY2(sequence, sq_length),
CAPABILITY2(sequence, sq_item),
CAPABILITY2(sequence, sq_ass_item),
CAPABILITY2(sequence, sq_concat),
CAPABILITY2(sequence, sq_repeat),
CAPABILITY2(sequence, sq_slice),
CAPABILITY2(sequence, sq_ass_slice)
};
const std::size_t num_capabilities = PY_ARRAY_LENGTH(capabilities);
void add_capability(
std::size_t n,
PyTypeObject* dest_,
AllMethods& all_methods,
const PyTypeObject* src_)
{
assert(n < PY_ARRAY_LENGTH(capabilities));
const CapabilityEntry& c = capabilities[n];
const char* const* src = src_ ? reinterpret_cast<const char* const*>(
reinterpret_cast<const char*>(src_) + c.offset1) : 0;
char** const dest = reinterpret_cast<char**>(
reinterpret_cast<char*>(dest_) + c.offset1);
if (c.substructure_size == 0)
{
if (src == 0 ||
#if defined(__MWERKS__) && __MWERKS__ <= 0x4000
((const Dispatch*)src)
#else
reinterpret_cast<const Dispatch*>(src)
#endif
!= 0) {
*reinterpret_cast<Dispatch*>(dest) = c.dispatch;
}
}
else
{
if (src == 0 ||
*src != 0 && *reinterpret_cast<const Dispatch*>(*src + c.offset2) != 0)
{
*dest = reinterpret_cast<char*>(&all_methods) + c.allmethods_offset;
*reinterpret_cast<Dispatch*>(*dest + c.offset2) = c.dispatch;
}
}
}
} // namespace detail
namespace {
union SubStructures {
PyMappingMethods mapping;
PySequenceMethods sequence;
PyNumberMethods number;
PyBufferProcs buffer;
};
}
void TypeObjectBase::enable(TypeObjectBase::Capability capability)
{
using detail::capabilities;
using detail::CapabilityEntry;
using detail::Dispatch;
assert((std::size_t)capability < PY_ARRAY_LENGTH(capabilities));
const CapabilityEntry& c = capabilities[capability];
PyTypeObject* const me = this;
char* const base_address = reinterpret_cast<char*>(me);
if (c.substructure_size == 0)
{
// Stuff the dispatch function directly into the PyTypeObject
*reinterpret_cast<Dispatch*>(base_address + c.offset1) = c.dispatch;
return;
}
const char*& sub_structure = *reinterpret_cast<const char**>(base_address + c.offset1);
// Initialize this POD union with the current state-of-the-world
SubStructures sub;
if (sub_structure == 0)
PY_CSTD_::memset(&sub, 0, c.substructure_size);
else
PY_CSTD_::memcpy(&sub, sub_structure, c.substructure_size);
// Stuff the dispatch function into the sub-structure
*reinterpret_cast<Dispatch*>(reinterpret_cast<char*>(&sub) + c.offset2) = c.dispatch;
// Retrieve the unique dynamically-allocated substructure and stuff it into
// the PyTypeObject.
sub_structure = static_cast<const char*>(
detail::UniquePodSet::instance().get_element(&sub, c.substructure_size));
}
TypeObjectBase::TypeObjectBase(PyTypeObject* t)
: PythonType(t)
{
this->tp_dealloc = do_instance_dealloc;
}
namespace {
struct ErrorType {
operator PyObject*() const { return 0; }
operator int() const { return -1; }
};
ErrorType unimplemented(const char* name)
{
assert(!"Control should never reach here");
String s("Unimplemented ");
s += String(name);
PyErr_SetObject(PyExc_RuntimeError, s.get());
return ErrorType();
}
}
PyObject* TypeObjectBase::instance_repr(PyObject*) const
{
return unimplemented("instance_repr");
}
int TypeObjectBase::instance_compare(PyObject*, PyObject*) const
{
return unimplemented("instance_compare");
}
PyObject* TypeObjectBase::instance_str(PyObject*) const
{
return unimplemented("instance_str");
}
long TypeObjectBase::instance_hash(PyObject* /* instance */) const
{
return unimplemented("instance_hash");
}
PyObject* TypeObjectBase::instance_call(PyObject* /*instance*/, PyObject* /*args*/, PyObject* /*kw*/) const
{
return unimplemented("instance_call");
}
PyObject* TypeObjectBase::instance_getattr(PyObject* /*instance*/, const char* /*name*/) const
{
return unimplemented("instance_getattr");
}
int TypeObjectBase::instance_setattr(PyObject* /*instance*/, const char* /*name*/, PyObject* /*value*/) const
{
return unimplemented("instance_setattr");
}
int TypeObjectBase::instance_mapping_length(PyObject*) const
{
return unimplemented("instance_mapping_length");
}
int TypeObjectBase::instance_sequence_length(PyObject*) const
{
return unimplemented("instance_sequence_length");
}
PyObject* TypeObjectBase::instance_mapping_subscript(PyObject*, PyObject*) const
{
return unimplemented("instance_mapping_subscript");
}
PyObject* TypeObjectBase::instance_sequence_item(PyObject*, int) const
{
return unimplemented("instance_sequence_item");
}
int TypeObjectBase::instance_mapping_ass_subscript(PyObject*, PyObject*, PyObject*) const
{
return unimplemented("instance_mapping_ass_subscript");
}
int TypeObjectBase::instance_sequence_ass_item(PyObject*, int, PyObject*) const
{
return unimplemented("instance_sequence_ass_item");
}
PyObject* TypeObjectBase::instance_sequence_concat(PyObject*, PyObject*) const
{
return unimplemented("instance_sequence_concat");
}
PyObject* TypeObjectBase::instance_sequence_repeat(PyObject*, int) const
{
return unimplemented("instance_sequence_repeat");
}
PyObject* TypeObjectBase::instance_sequence_slice(PyObject*, int, int) const
{
return unimplemented("instance_sequence_slice");
}
int TypeObjectBase::instance_sequence_ass_slice(PyObject*, int, int, PyObject*) const
{
return unimplemented("instance_sequence_ass_slice");
}
TypeObjectBase::~TypeObjectBase()
{
}
}

264
newtypes.h Normal file
View File

@@ -0,0 +1,264 @@
// (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.
#ifndef TYPES_DWA051800_H_
# define TYPES_DWA051800_H_
// Usage:
// class X : public
// py::Callable<
// py::Getattrable <
// py::Setattrable<PythonObject, X> > >
// {
// public:
// Ptr call(args, kw);
// Ptr getattr(args, kw);
// Ptr setattr(args, kw);
// };
# include "pyconfig.h"
# include "signatures.h" // really just for Type<>
# include "cast.h"
# include "base_object.h"
# include <typeinfo>
# include <vector>
namespace py {
class TypeObjectBase : public PythonType
{
public:
explicit TypeObjectBase(PyTypeObject* type_type);
virtual ~TypeObjectBase();
public:
enum Capability
{
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
};
void enable(Capability);
//
// Type behaviors
//
public: // Callbacks for basic type functionality.
virtual PyObject* instance_repr(PyObject*) const;
virtual int instance_compare(PyObject*, PyObject* other) const;
virtual PyObject* instance_str(PyObject*) const;
virtual long instance_hash(PyObject*) const;
virtual PyObject* instance_call(PyObject* instance, PyObject* args, PyObject* kw) const;
virtual PyObject* instance_getattr(PyObject* instance, const char* name) const;
virtual int instance_setattr(PyObject* instance, const char* name, PyObject* value) const;
// Dealloc is a special case, since every type needs a nonzero tp_dealloc slot.
virtual void instance_dealloc(PyObject*) const = 0;
public: // Callbacks for mapping methods
virtual int instance_mapping_length(PyObject*) const;
virtual PyObject* instance_mapping_subscript(PyObject*, PyObject*) const ;
virtual int instance_mapping_ass_subscript(PyObject*, PyObject*, PyObject*) const;
public: // Callbacks for sequence methods
virtual int instance_sequence_length(PyObject* instance) const;
virtual PyObject* instance_sequence_concat(PyObject* instance, PyObject* other) const;
virtual PyObject* instance_sequence_repeat(PyObject* instance, int n) const;
virtual PyObject* instance_sequence_item(PyObject* instance, int n) const;
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;
};
template <class T>
class TypeObject : public TypeObjectBase
{
public:
typedef T Instance;
TypeObject(PyTypeObject* type_type, const char* name = 0)
: TypeObjectBase(type_type)
{
this->tp_name = const_cast<char*>(name ? name : typeid(Instance).name());
}
private: // Overridable behaviors.
// Called when the reference count goes to zero. The default implementation
// is "delete p". If you have not allocated your object with operator new or
// you have other constraints, you'll need to override this
virtual void dealloc(T* p) const;
private: // Implementation of TypeObjectBase hooks. Do not reimplement in derived classes.
void instance_dealloc(PyObject*) const;
};
//
// Type objects
//
template <class Base>
class Callable : public Base
{
public:
typedef Callable Properties; // Convenience for derived class construction
typedef typename Base::Instance Instance;
Callable(PyTypeObject* type_type, const char* name = 0);
private:
PyObject* instance_call(PyObject* instance, PyObject* args, PyObject* kw) const;
};
template <class Base>
class Getattrable : public Base
{
public:
typedef Getattrable Properties; // Convenience for derived class construction
typedef typename Base::Instance Instance;
Getattrable(PyTypeObject* type_type, const char* name = 0);
private:
PyObject* instance_getattr(PyObject* instance, const char* name) const;
};
template <class Base>
class Setattrable : public Base
{
public:
typedef Setattrable Properties; // Convenience for derived class construction
typedef typename Base::Instance Instance;
Setattrable(PyTypeObject* type_type, const char* name = 0);
private:
int instance_setattr(PyObject* instance, const char* name, PyObject* value) const;
};
//
// Member function definitions
//
// TypeObject<>
template <class T>
void TypeObject<T>::instance_dealloc(PyObject* instance) const
{
this->dealloc(Downcast<Instance>(instance).get());
}
template <class T>
void TypeObject<T>::dealloc(T* instance) const
{
delete instance;
}
// Callable
template <class Base>
Callable<Base>::Callable(PyTypeObject* type_type, const char* name)
: Base(type_type, name)
{
this->enable(call);
}
template <class Base>
PyObject* Callable<Base>::instance_call(PyObject* instance, PyObject* args, PyObject* kw) const
{
return Downcast<Instance>(instance)->call(args, kw);
}
// Getattrable
template <class Base>
Getattrable<Base>::Getattrable(PyTypeObject* type_type, const char* name)
: Base(type_type, name)
{
this->enable(getattr);
}
template <class Base>
PyObject* Getattrable<Base>::instance_getattr(PyObject* instance, const char* name) const
{
return Downcast<Instance>(instance)->getattr(name);
}
// Setattrable
template <class Base>
Setattrable<Base>::Setattrable(PyTypeObject* type_type, const char* name)
: Base(type_type, name)
{
this->enable(setattr);
}
template <class Base>
int Setattrable<Base>::instance_setattr(PyObject* instance, const char* name, PyObject* value) const
{
return Downcast<Instance>(instance)->setattr(name, value);
}
namespace detail {
struct AllMethods {
PyMappingMethods mapping;
PySequenceMethods sequence;
PyNumberMethods number;
PyBufferProcs buffer;
};
typedef void (*Dispatch)();
struct CapabilityEntry
{
std::size_t offset1;
std::size_t offset2;
Dispatch dispatch;
std::size_t substructure_size;
int allmethods_offset;
};
extern const CapabilityEntry capabilities[];
extern const std::size_t num_capabilities;
void add_capability(std::size_t index, PyTypeObject* dest, AllMethods&, const PyTypeObject* src = 0);
class UniquePodSet
{
typedef std::pair<const char*, const char*> Holder;
typedef std::vector<Holder> Storage;
public:
static UniquePodSet& instance();
~UniquePodSet();
template <class T>
T* get(const T& x)
{
char* base = const_cast<char*>(
reinterpret_cast<const char*>(&x));
return const_cast<T*>(
static_cast<const T*>(
get_element(base, sizeof(T))));
}
const void* get_element(const void* buffer, std::size_t size);
private:
struct Compare;
private:
UniquePodSet() {} // singleton
private:
Storage m_storage;
};
# define PY_ARRAY_LENGTH(a) \
(sizeof(::py::detail::countof_validate(a, &(a))) ? sizeof(a) / sizeof((a)[0]) : 0)
template<typename T>
inline void countof_validate(T* const, T* const*);
template<typename T>
inline int countof_validate(const void*, T);
}
}
#endif // TYPES_DWA051800_H_

2
newtypes_d.cpp Normal file
View File

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

21
none.h Normal file
View File

@@ -0,0 +1,21 @@
// (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.
#ifndef NONE_DWA_052000_H_
# define NONE_DWA_052000_H_
# include "pyconfig.h"
# include "wrap_python.h"
namespace py {
inline PyObject* none() { Py_INCREF(Py_None); return Py_None; }
}
#endif // NONE_DWA_052000_H_

454
objects.cpp Normal file
View File

@@ -0,0 +1,454 @@
// (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.
// TODO: Move inline implementations from objects.cpp here
#include "objects.h"
#include "none.h"
namespace py {
template <class T>
T object_from_python(PyObject* p, Type<T>)
{
Ptr x(p);
if (!T::accepts(x))
{
PyErr_SetString(PyExc_TypeError, p->ob_type->tp_name);
throw ErrorAlreadySet();
}
return T(x);
}
inline PyObject* object_to_python(const Object& x)
{
return x.reference().release();
}
Object::Object(Ptr p)
: m_p(p) {}
// Return a reference to the held object
Ptr Object::reference() const
{
return m_p;
}
// Return a raw pointer to the held object
PyObject* Object::get() const
{
return m_p.get();
}
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
} // Back to the global namespace for this GCC bug
#endif
PyObject* to_python(const py::Tuple& x)
{
return object_to_python(x);
}
py::Tuple from_python(PyObject* p, py::Type<py::Tuple> type)
{
return py::object_from_python(p, type);
}
PyObject* to_python(const py::List& x)
{
return object_to_python(x);
}
py::List from_python(PyObject* p, py::Type<py::List> type)
{
return py::object_from_python(p, type);
}
PyObject* to_python(const py::Dict& x)
{
return object_to_python(x);
}
py::Dict from_python(PyObject* p, py::Type<py::Dict> type)
{
return py::object_from_python(p, type);
}
PyObject* to_python(const py::String& x)
{
return object_to_python(x);
}
py::String from_python(PyObject* p, py::Type<py::String> type)
{
return py::object_from_python(p, type);
}
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
namespace py {
#endif
Tuple::Tuple(std::size_t n)
: Object(Ptr(PyTuple_New(n)))
{
for (std::size_t i = 0; i < n; ++i)
PyTuple_SET_ITEM(get(), i, none());
}
Tuple::Tuple(Ptr p)
: Object(p)
{
assert(accepts(p));
}
PyTypeObject* Tuple::type_object()
{
return &PyTuple_Type;
}
bool Tuple::accepts(Ptr p)
{
return PyTuple_Check(p.get());
}
std::size_t Tuple::size() const
{
return PyTuple_Size(get());
}
Ptr Tuple::operator[](std::size_t pos) const
{
return Ptr(PyTuple_GetItem(get(), static_cast<int>(pos)),
Ptr::new_ref);
}
void Tuple::set_item(std::size_t pos, const Ptr& rhs)
{
int failed = PyTuple_SetItem(
get(), static_cast<int>(pos), Ptr(rhs).release()); // A reference is stolen here.
(void)failed;
assert(failed == 0);
}
Tuple Tuple::slice(int low, int high) const
{
return Tuple(Ptr(PyTuple_GetSlice(get(), low, high)));
}
Tuple& Tuple::operator+=(const Tuple& rhs)
{
return *this = *this + rhs;
}
// Construct from an owned PyObject*.
// Precondition: p must point to a python string.
String::String(Ptr p)
: Object(p) { assert(accepts(p)); }
String::String(const char* s)
: Object(Ptr(PyString_FromString(s))) {}
String::String(const char* s, std::size_t length)
: Object(Ptr(PyString_FromStringAndSize(s, length))) {}
String::String(const char* s, Interned)
: Object(Ptr(PyString_InternFromString(s))) {}
#if 0
String::String(const char* s, std::size_t length, Interned)
: Object(Ptr(PyString_InternFromStringAndSize(s, length))) {}
#endif
String::String(const String& rhs)
: Object(rhs.reference()) {}
// Get the type object for Strings
PyTypeObject* String::type_object()
{ return &PyString_Type; }
// Return true if the given object is a python String
bool String::accepts(Ptr o)
{ return PyString_Check(o.get()); }
// Return the length of the string.
std::size_t String::size() const
{
int size = PyString_GET_SIZE(get());
assert(size >= 0);
return static_cast<std::size_t>(size);
}
// Returns a null-terminated representation of the contents of string.
// The pointer refers to the internal buffer of string, not a copy.
// The data must not be modified in any way. It must not be de-allocated.
const char* String::c_str() const
{ return PyString_AS_STRING(get()); }
void String::intern()
{ // UNTESTED!!
*this = String(Ptr(PyString_InternFromString(c_str()), Ptr::borrowed));
}
Dict::Dict(Ptr p)
: Object(p) { assert(accepts(p)); }
Dict::Dict()
: Object(Ptr(PyDict_New())) {}
PyTypeObject* Dict::type_object()
{ return &PyDict_Type; }
bool Dict::accepts(Ptr p)
{ return PyDict_Check(p.get()); }
void Dict::clear()
{ PyDict_Clear(get()); }
const Ptr& Dict::Proxy::operator=(const Ptr& rhs) {
if (PyDict_SetItem(m_dict.get(), m_key.get(), rhs.get()) == -1)
throw ErrorAlreadySet();
return rhs;
}
Dict::Proxy::operator Ptr() const
{
return Ptr(m_dict->ob_type->tp_as_mapping->mp_subscript(m_dict.get(), m_key.get()),
Ptr::borrowed);
}
Dict::Proxy::Proxy(const Ptr& dict, const Ptr& key)
: m_dict(dict), m_key(key) {}
Dict::Proxy Dict::operator[](Ptr key)
{ return Proxy(reference(), key); }
Ptr Dict::operator[](Ptr key) const {
// An odd MSVC bug causes the ".operator Ptr()" to be needed
return Proxy(reference(), key).operator Ptr();
}
Ptr Dict::get_item(const Ptr& key, const Ptr& _default /* = Ptr() */)
{
PyObject* value_or_null = PyDict_GetItem(get(), key.get());
if (value_or_null == 0 && !PyErr_Occurred())
return _default;
else
return Ptr(value_or_null, Ptr::borrowed); // Will throw if there was another error
}
void Dict::erase(Ptr key) {
if (PyDict_DelItem(get(), key.get()) == -1)
throw ErrorAlreadySet();
}
Ptr Dict::items() const { return Ptr(PyDict_Items(get())); }
Ptr Dict::keys() const { return Ptr(PyDict_Keys(get())); }
Ptr Dict::values() const { return Ptr(PyDict_Values(get())); }
std::size_t Dict::size() const { return static_cast<std::size_t>(PyDict_Size(get())); }
Dict::Proxy Dict::operator[](const Object& key)
{
return this->operator[](key.reference());
}
Ptr Dict::operator[](const Object& key) const
{
return this->operator[](key.reference());
}
Ptr Dict::get_item(const Object& key, Ptr default_)
{
return this->get_item(key.reference(), default_);
}
void Dict::erase(const Object& key)
{
this->erase(key.reference());
}
// TODO: iterator support
String operator+(String x, String y)
{
PyObject* io_string = x.reference().release();
PyString_Concat(&io_string, y.get());
return String(Ptr(io_string));
}
String& String::operator+=(const String& rhs)
{
return *this = *this + rhs;
}
String& String::operator+=(const char* y)
{
return *this += String(y);
}
String operator%(const String& format, const Tuple& args)
{
return String(Ptr(PyString_Format(format.get(), args.reference().get())));
}
String operator+(String x, const char* y)
{
return x + String(y);
}
String operator+(const char* x, String y)
{
return String(x) + y;
}
Tuple operator+(const Tuple& x, const Tuple& y)
{
Tuple result(x.size() + y.size());
for (std::size_t xi = 0; xi < x.size(); ++xi)
result.set_item(xi, x[xi]);
for (std::size_t yi = 0; yi < y.size(); ++yi)
result.set_item(yi + x.size(), y[yi]);
return result;
}
List::List(Ptr p)
: Object(p)
{
assert(accepts(p));
}
List::List(std::size_t sz)
: Object(Ptr(PyList_New(sz)))
{
}
PyTypeObject* List::type_object()
{
return &PyList_Type;
}
bool List::accepts(Ptr p)
{
return PyList_Check(p.get());
}
std::size_t List::size()
{
return PyList_Size(get());
}
Ptr List::operator[](std::size_t pos) const
{
return Ptr(PyList_GetItem(get(), pos), Ptr::borrowed);
}
List::Proxy List::operator[](std::size_t pos)
{
return Proxy(reference(), pos);
}
void List::insert(std::size_t index, Ptr item)
{
if (PyList_Insert(get(), index, item.get()) == -1)
throw ErrorAlreadySet();
}
void List::push_back(Ptr item)
{
if (PyList_Append(get(), item.get()) == -1)
throw ErrorAlreadySet();
}
void List::append(Ptr item)
{
this->push_back(item);
}
List List::slice(int low, int high) const
{
return List(Ptr(PyList_GetSlice(get(), low, high)));
}
List::SliceProxy List::slice(int low, int high)
{
return SliceProxy(reference(), low, high);
}
void List::sort()
{
if (PyList_Sort(get()) == -1)
throw ErrorAlreadySet();
}
void List::reverse()
{
if (PyList_Reverse(get()) == -1)
throw ErrorAlreadySet();
}
Tuple List::as_tuple() const
{
return Tuple(Ptr(PyList_AsTuple(get())));
}
const Ptr& List::Proxy::operator=(const Ptr& rhs)
{
int result = PyList_SetItem(m_list.get(), m_index, rhs.get());
if (result == -1)
throw ErrorAlreadySet();
Py_INCREF(rhs.get());
return rhs;
}
List::Proxy::operator Ptr() const
{
return Ptr(PyList_GetItem(m_list.get(), m_index), Ptr::borrowed);
}
List::Proxy::Proxy(const Ptr& list, std::size_t index)
: m_list(list), m_index(index)
{
}
const List& List::SliceProxy::operator=(const List& rhs)
{
if (PyList_SetSlice(m_list.get(), m_low, m_high, rhs.get()) == -1)
throw ErrorAlreadySet();
return rhs;
}
List::SliceProxy::operator Ptr() const
{
return Ptr(PyList_GetSlice(m_list.get(), m_low, m_high));
}
List::SliceProxy::operator List() const
{
return List(this->operator Ptr());
}
std::size_t List::SliceProxy::size()
{
return this->operator List().size();
}
Ptr List::SliceProxy::operator[](std::size_t pos) const
{
return this->operator List()[pos].operator Ptr();
}
List::SliceProxy::SliceProxy(const Ptr& list, int low, int high)
: m_list(list), m_low(low), m_high(high)
{
}
}

233
objects.h Normal file
View File

@@ -0,0 +1,233 @@
// (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.
#ifndef OBJECTS_DWA051100_H_
# define OBJECTS_DWA051100_H_
# include "wrap_python.h"
# include "pyconfig.h"
# include "pyptr.h"
# include "boost/operators.hpp"
namespace py {
class Object
{
public:
explicit Object(Ptr p);
// Return a reference to the held object
Ptr reference() const;
// Return a raw pointer to the held object
PyObject* get() const;
private:
Ptr m_p;
};
class Tuple : public Object
{
public:
Tuple(std::size_t n = 0);
explicit Tuple(Ptr p);
Tuple(const Ptr* start, const Ptr* finish); // not yet implemented.
static PyTypeObject* type_object();
static bool accepts(Ptr p);
std::size_t size() const;
Ptr operator[](std::size_t pos) const;
void set_item(std::size_t pos, const Ptr& rhs);
Tuple slice(int low, int high) const;
friend Tuple operator+(const Tuple&, const Tuple&);
Tuple& operator+=(const Tuple& rhs);
};
class List : public Object
{
struct Proxy;
struct SliceProxy;
public:
explicit List(Ptr p);
explicit List(std::size_t sz = 0);
static PyTypeObject* type_object();
static bool accepts(Ptr p);
std::size_t size();
Ptr operator[](std::size_t pos) const;
Proxy operator[](std::size_t pos);
void insert(std::size_t index, Ptr item);
void push_back(Ptr item);
void append(Ptr item);
List slice(int low, int high) const;
SliceProxy slice(int low, int high);
void sort();
void reverse();
Tuple as_tuple() const;
};
class String : public Object
{
public:
// Construct from an owned PyObject*.
// Precondition: p must point to a python string.
explicit String(Ptr p);
explicit String(const char* s);
String(const char* s, std::size_t length);
String(const String& rhs);
enum Interned { interned };
String(const char* s, Interned);
#if 0
String(const char* s, std::size_t length, Interned);
#endif
// Get the type object for Strings
static PyTypeObject* type_object();
// Return true if the given object is a python String
static bool accepts(Ptr o);
// Return the length of the string.
std::size_t size() const;
// Returns a null-terminated representation of the contents of string.
// The pointer refers to the internal buffer of string, not a copy.
// The data must not be modified in any way. It must not be de-allocated.
const char* c_str() const;
String& operator+=(const String& rhs);
friend String operator+(String x, String y);
String& operator+=(const char* rhs);
friend String operator+(String x, const char* y);
friend String operator+(const char* x, String y);
void intern(); // UNTESTED!!
friend String operator%(const String& format, const Tuple& args);
};
class Dict : public Object
{
private:
struct Proxy;
public:
explicit Dict(Ptr p);
Dict();
void clear();
static PyTypeObject* type_object();
static bool accepts(Ptr p);
public:
Proxy operator[](Ptr key);
Ptr operator[](Ptr key) const;
Ptr get_item(const Ptr& key, const Ptr& _default = Ptr());
void erase(Ptr key);
Proxy operator[](const Object& key);
Ptr operator[](const Object& key) const;
Ptr get_item(const Object& key, Ptr default_ = Ptr());
void erase(const Object& key);
Ptr items() const;
Ptr keys() const;
Ptr values() const;
std::size_t size() const;
// TODO: iterator support
};
struct Dict::Proxy
{
const Ptr& operator=(const Ptr& rhs);
operator Ptr() const;
private:
friend class Dict;
Proxy(const Ptr& dict, const Ptr& key);
private:
Ptr m_dict;
Ptr m_key;
};
struct List::Proxy
{
const Ptr& operator=(const Ptr& rhs);
operator Ptr() const;
private:
friend class List;
Proxy(const Ptr& list, std::size_t index);
private:
Ptr m_list;
std::size_t m_index;
};
struct List::SliceProxy
{
const List& operator=(const List& rhs);
operator Ptr() const;
operator List() const;
std::size_t size();
Ptr operator[](std::size_t pos) const;
private:
friend class List;
SliceProxy(const Ptr& list, int low, int high);
private:
Ptr m_list;
int m_low, m_high;
};
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
} // Back to the global namespace for this GCC bug
#endif
PyObject* to_python(const py::Tuple&);
py::Tuple from_python(PyObject* p, py::Type<py::Tuple>);
inline py::Tuple from_python(PyObject* p, py::Type<const py::Tuple&>)
{
return from_python(p, py::Type<py::Tuple>());
}
PyObject* to_python(const py::List&);
py::List from_python(PyObject* p, py::Type<py::List>);
inline py::List from_python(PyObject* p, py::Type<const py::List&>)
{
return from_python(p, py::Type<py::List>());
}
PyObject* to_python(const py::String&);
py::String from_python(PyObject* p, py::Type<py::String>);
inline py::String from_python(PyObject* p, py::Type<const py::String&>)
{
return from_python(p, py::Type<py::String>());
}
PyObject* to_python(const py::Dict&);
py::Dict from_python(PyObject* p, py::Type<py::Dict>);
inline py::Dict from_python(PyObject* p, py::Type<const py::Dict&>)
{
return from_python(p, py::Type<py::Dict>());
}
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
namespace py {
#endif
}
#endif

2
objects_d.cpp Normal file
View File

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

204
py.cpp Normal file
View File

@@ -0,0 +1,204 @@
// (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.
#include "py.h"
#include <boost/cast.hpp>
namespace py {
void expect_and_absorb_non_null(PyObject* p)
{
Py_XDECREF(expect_non_null(p));
}
// IMPORTANT: this function may only be called from within a catch block!
void handle_exception()
{
try {
// re-toss the current exception so we can find out what type it is.
// NOTE: a heinous bug in MSVC6 causes exception objects re-thrown in
// this way to be double-destroyed. Thus, you must only use objects that
// can tolerate double-destruction with that compiler. Metrowerks
// Codewarrior doesn't suffer from this problem.
throw;
}
catch(const py::ErrorAlreadySet&)
{
// The python error reporting has already been handled.
}
catch(const std::bad_alloc&)
{
PyErr_NoMemory();
}
catch(const std::exception& x)
{
PyErr_SetString(PyExc_RuntimeError, x.what());
}
catch(...)
{
PyErr_SetString(PyExc_RuntimeError, "unidentifiable C++ exception");
}
}
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
}
#endif
long from_python(PyObject* p, py::Type<long>)
{
// Why am I clearing the error here before trying to convert? I know there's a reason...
long result;
{
// py::SuspendError suspended_error(py::SuspendError::discard_old_error);
result = PyInt_AsLong(p);
if (PyErr_Occurred())
throw py::ArgumentError();
// suspended_error.throw_if_error();
}
return result;
}
double from_python(PyObject* p, py::Type<double>)
{
// Why am I clearing the error here before trying to convert? I know there's a reason...
double result;
{
// py::SuspendError suspended_error(py::SuspendError::discard_old_error);
result = PyFloat_AsDouble(p);
if (PyErr_Occurred())
throw py::ArgumentError();
// suspended_error.throw_if_error();
}
return result;
}
template <class T>
T integer_from_python(PyObject* p, py::Type<T>)
{
const long long_result = from_python(p, py::Type<long>());
try
{
return boost::numeric_cast<T>(long_result);
}
catch(const boost::bad_numeric_cast&)
{
char buffer[256];
const char message[] = "%ld out of range for %s";
sprintf(buffer, message, long_result, typeid(T).name());
PyErr_SetString(PyExc_ValueError, buffer);
throw py::ArgumentError();
}
#if defined(__MWERKS__) && __MWERKS__ < 0x6000
return 0; // Not smart enough to know that the catch clause always rethrows
#endif
}
template <class T>
PyObject* integer_to_python(T value)
{
long value_as_long;
try
{
value_as_long = boost::numeric_cast<T>(value);
}
catch(const boost::bad_numeric_cast&)
{
const char message[] = "value out of range for Python int";
PyErr_SetString(PyExc_ValueError, message);
throw py::ErrorAlreadySet();
}
return to_python(value_as_long);
}
int from_python(PyObject* p, py::Type<int> type)
{
return integer_from_python(p, type);
}
PyObject* to_python(unsigned int i)
{
return integer_to_python(i);
}
unsigned int from_python(PyObject* p, py::Type<unsigned int> type)
{
return integer_from_python(p, type);
}
short from_python(PyObject* p, py::Type<short> type)
{
return integer_from_python(p, type);
}
float from_python(PyObject* p, py::Type<float>)
{
return static_cast<float>(from_python(p, py::Type<double>()));
}
PyObject* to_python(unsigned short i)
{
return integer_to_python(i);
}
unsigned short from_python(PyObject* p, py::Type<unsigned short> type)
{
return integer_from_python(p, type);
}
PyObject* to_python(unsigned long x)
{
return integer_to_python(x);
}
unsigned long from_python(PyObject* p, py::Type<unsigned long> type)
{
return integer_from_python(p, type);
}
void from_python(PyObject* p, py::Type<void>)
{
if (p != Py_None) {
PyErr_SetString(PyExc_TypeError, "expected argument of type None");
throw py::ArgumentError();
}
}
const char* from_python(PyObject* p, py::Type<const char*>)
{
const char* s = PyString_AsString(p);
if (!s) throw py::ArgumentError();
return s;
}
PyObject* to_python(const std::string& s)
{
return PyString_FromString(s.c_str());
}
std::string from_python(PyObject* p, py::Type<std::string>)
{
return std::string(from_python(p, py::Type<const char*>()));
}
bool from_python(PyObject* p, py::Type<bool>)
{
int value = from_python(p, py::Type<int>());
if (value == 0)
return false;
return true;
}
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
namespace py {
#endif
}

266
py.h Normal file
View File

@@ -0,0 +1,266 @@
// (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.
#ifndef METHOD_DWA122899_H_
# define METHOD_DWA122899_H_
# include "wrap_python.h"
# include "none.h"
# include "signatures.h"
# include <boost/smart_ptr.hpp>
# include "errors.h"
# include <string>
namespace py {
template <class P, class T> class WrappedPointer;
//#pragma warn_possunwant off
inline void decref_impl(PyObject* p) { Py_DECREF(p); }
inline void xdecref_impl(PyObject* p) { Py_XDECREF(p); }
//#pragma warn_possunwant reset
template <class T>
inline void decref(T* p)
{
char* const raw_p = reinterpret_cast<char*>(p);
char* const p_base = raw_p - offsetof(PyObject, ob_refcnt);
decref_impl(reinterpret_cast<PyObject*>(p_base));
}
template <class T>
inline void xdecref(T* p)
{
char* const raw_p = reinterpret_cast<char*>(p);
char* const p_base = raw_p - offsetof(PyObject, ob_refcnt);
xdecref_impl(reinterpret_cast<PyObject*>(p_base));
}
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
}
#endif
//
// Converters
//
PyObject* to_python(long);
long from_python(PyObject* p, py::Type<long>);
long from_python(PyObject* p, py::Type<const long&>);
PyObject* to_python(unsigned long);
unsigned long from_python(PyObject* p, py::Type<unsigned long>);
unsigned long from_python(PyObject* p, py::Type<const unsigned long&>);
PyObject* to_python(int);
int from_python(PyObject*, py::Type<int>);
int from_python(PyObject*, py::Type<const int&>);
PyObject* to_python(unsigned int);
unsigned int from_python(PyObject*, py::Type<unsigned int>);
unsigned int from_python(PyObject*, py::Type<const unsigned int&>);
PyObject* to_python(short);
short from_python(PyObject*, py::Type<short>);
short from_python(PyObject*, py::Type<const short&>);
PyObject* to_python(unsigned short);
unsigned short from_python(PyObject*, py::Type<unsigned short>);
unsigned short from_python(PyObject*, py::Type<const unsigned short&>);
PyObject* to_python(float);
float from_python(PyObject*, py::Type<float>);
float from_python(PyObject*, py::Type<const float&>);
PyObject* to_python(double);
double from_python(PyObject*, py::Type<double>);
double from_python(PyObject*, py::Type<const double&>);
PyObject* to_python(bool);
bool from_python(PyObject*, py::Type<bool>);
bool from_python(PyObject*, py::Type<const bool&>);
PyObject* to_python(void);
void from_python(PyObject*, py::Type<void>);
PyObject* to_python(const char* s);
const char* from_python(PyObject*, py::Type<const char*>);
PyObject* to_python(const std::string& s);
std::string from_python(PyObject*, py::Type<std::string>);
std::string from_python(PyObject*, py::Type<const std::string&>);
// For when your C++ function really wants to pass/return a PyObject*
PyObject* to_python(PyObject*);
PyObject* from_python(PyObject*, py::Type<PyObject*>);
// Some standard conversions to/from smart pointer types. You can add your own
// from these examples. These are not generated using the friend technique from
// WrappedPointer because:
//
// 1. We want to be able to extend conversion to/from WrappedPointers using
// arbitrary smart pointer types.
//
// 2. It helps with compilation independence. This way, code which creates
// wrappers for functions accepting and returning smart_ptr<T> does not
// have to have already seen the invocation of WrappedType<T>.
//
// Unfortunately, MSVC6 is so incredibly lame that we have to rely on the friend
// technique to auto_generate standard pointer conversions for wrapped
// types. This means that you need to write a non-templated function for each
// specific smart_ptr<T> which you want to convert from_python. For example,
//
// namespace py {
// #ifdef MUST_SUPPORT_MSVC
//
// MyPtr<Foo> from_python(PyObject*p, Type<MyPtr<Foo> >)
// { return smart_ptr_from_python(p, Type<MyPtr<Foo> >(), Type<Foo>());}
// }
//
// MyPtr<Bar> from_python(PyObject*p, Type<MyPtr<Bar> >)
// { return smart_ptr_from_python(p, Type<MyPtr<Bar> >(), Type<Bar>());}
//
// ... // definitions for MyPtr<Baz>, MyPtr<Mumble>, etc.
//
// #else
//
// // Just once for all MyPtr<T>
// template <class T>
// MyPtr<T> from_python(PyObject*p, Type<MyPtr<T> >)
// {
// return smart_ptr_from_python(p, Type<MyPtr<T> >(), Type<T>());
// }
//
// #endif
// }
#if !defined(PY_MSVC6_OR_EARLIER)
template <class T>
boost::shared_ptr<T> from_python(PyObject*p, py::Type<boost::shared_ptr<T> >)
{
return smart_ptr_from_python(p, py::Type<boost::shared_ptr<T> >(), py::Type<T>());
}
#endif
#if 0
template <class T>
PyObject* to_python(std::auto_ptr<T> p)
{
return new py::WrappedPointer<std::auto_ptr<T>, T>(p);
}
template <class T>
PyObject* to_python(boost::shared_ptr<T> p)
{
return new py::WrappedPointer<boost::shared_ptr<T>, T>(p);
}
#endif
//
// inline implementations
//
inline PyObject* to_python(long l)
{
return PyInt_FromLong(l);
}
inline PyObject* to_python(int x)
{
return PyInt_FromLong(x);
}
inline int from_python(PyObject* p, py::Type<const int&>)
{
return from_python(p, py::Type<int>());
}
inline PyObject* to_python(short x)
{
return PyInt_FromLong(x);
}
inline short from_python(PyObject* p, py::Type<const short&>)
{
return from_python(p, py::Type<short>());
}
inline PyObject* to_python(bool b)
{
return PyInt_FromLong(b);
}
inline bool from_python(PyObject* p, py::Type<const bool&>)
{
return from_python(p, py::Type<bool>());
}
inline PyObject* to_python(void)
{
return py::none();
}
// const char*
inline PyObject* to_python(const char* s)
{
return PyString_FromString(s);
}
inline std::string from_python(PyObject* p, py::Type<const std::string&>)
{
return from_python(p, py::Type<std::string>());
}
inline PyObject* to_python(PyObject* p)
{
Py_INCREF(p);
return p;
}
inline PyObject* from_python(PyObject* p, py::Type<PyObject*>)
{
return p;
}
inline unsigned int from_python(PyObject* p, py::Type<const unsigned int&>)
{
return from_python(p, py::Type<unsigned int>());
}
inline unsigned short from_python(PyObject* p, py::Type<const unsigned short&>)
{
return from_python(p, py::Type<unsigned short>());
}
inline unsigned long from_python(PyObject* p, py::Type<const unsigned long&>)
{
return from_python(p, py::Type<unsigned long>());
}
inline long from_python(PyObject* p, py::Type<const long&>)
{
return from_python(p, py::Type<long>());
}
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE
namespace py {
namespace converters { // bringing these into namespace py tended to confuse gcc;
using ::to_python; // they are in namespace py::converters for use by clients
using ::from_python;
}
#else
namespace converters {
using ::py::to_python;
using ::py::from_python;
}
#endif
} // namespace py
#endif

4
py_cpp.bdf Normal file
View File

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

BIN
py_cpp_20001013.zip Normal file

Binary file not shown.

4
py_cpp_d.bdf Normal file
View File

@@ -0,0 +1,4 @@
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

21
py_cpphelp.mak Normal file
View File

@@ -0,0 +1,21 @@
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

2
py_d.cpp Normal file
View File

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

40
pyconfig.h Normal file
View File

@@ -0,0 +1,40 @@
// (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.
#ifndef CONFIG_DWA052200_H_
# define CONFIG_DWA052200_H_
# include <boost/config.hpp>
# include <cstddef>
# ifdef BOOST_NO_OPERATORS_IN_NAMESPACE
# define PY_NO_INLINE_FRIENDS_IN_NAMESPACE 1 // A more accurate name
# define PY_INLINE_FRIEND
# else
# define PY_INLINE_FRIEND ::py
# endif
# if !defined(__GNUC__) && !defined(__MWERKS__) && !defined(__BORLANDC__) && defined(_MSC_VER)
# define PY_COMPILER_IS_MSVC 1
# if _MSC_VER <= 1200
# define PY_MSVC6_OR_EARLIER 1
# endif
# pragma warning (disable : 4786)
# 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)
# define PY_CSTD_
# else
# define PY_CSTD_ std
# endif
#endif // CONFIG_DWA052200_H_

143
pyptr.h Normal file
View File

@@ -0,0 +1,143 @@
// (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.
#ifndef PYPTR_DWA050400_H_
# define PYPTR_DWA050400_H_
# include "pyconfig.h"
# include <boost/operators.hpp>
# include "wrap_python.h"
# include "cast.h"
# include <cassert>
# include "signatures.h"
# include "errors.h"
namespace py {
template <class T>
class PyPtr
: public boost::dereferenceable<PyPtr<T>, T*> // supplies op->
{
public:
PyPtr(const PyPtr& rhs)
: m_p(rhs.m_p)
{
Py_XINCREF(object());
}
#if !defined(PY_MSVC6_OR_EARLIER)
template <class T2>
PyPtr(const PyPtr<T2>& rhs)
: m_p(rhs.object())
{
Py_XINCREF(object());
}
#endif
PyPtr() : m_p(0) {}
// These are two ways of spelling the same thing, that we need to increment
// the reference count on the pointer when we're initialized.
enum NewRef { new_ref, borrowed = new_ref };
enum AllowNull { null_ok };
template <class T2>
explicit PyPtr(T2* x)
: m_p(expect_non_null(x)) {}
template <class T2>
PyPtr(T2* x, NewRef)
: m_p(expect_non_null(x)) { Py_INCREF(object()); }
template <class T2>
PyPtr(T2* x, AllowNull)
: m_p(x) {}
template <class T2>
PyPtr(T2* x, AllowNull, NewRef)
: m_p(x) { Py_XINCREF(object()); }
template <class T2>
PyPtr(T2* x, NewRef, AllowNull)
: m_p(x) { Py_XINCREF(object()); }
#if !defined(PY_MSVC6_OR_EARLIER)
template <class T2>
PyPtr& operator=(const PyPtr<T2>& rhs)
{
Py_XDECREF(object());
m_p = rhs.m_p;
Py_XINCREF(object());
return *this;
}
#endif
PyPtr& operator=(const PyPtr& rhs)
{
Py_XINCREF(static_cast<PyObject*>(rhs.m_p));
Py_XDECREF(object());
m_p = rhs.m_p;
return *this;
}
~PyPtr()
{
Py_XDECREF(m_p);
}
T& operator*() const { return *m_p; }
T* get() const { return m_p; }
T* release()
{
T* p = m_p;
m_p = 0;
return p;
}
void reset()
{ Py_XDECREF(m_p); m_p = 0; }
template <class T2>
void reset(T2* x)
{ Py_XDECREF(m_p); m_p = expect_non_null(x);}
template <class T2>
void reset(T2* x, NewRef)
{ Py_XDECREF(m_p); m_p = expect_non_null(x); Py_INCREF(object()); }
template <class T2>
void reset(T2* x, AllowNull)
{ Py_XDECREF(m_p); m_p = x;}
template <class T2>
void reset(T2* x, AllowNull, NewRef)
{ Py_XDECREF(m_p); m_p = x; Py_XINCREF(object()); }
template <class T2>
void reset(T2* x, NewRef, AllowNull)
{ Py_XDECREF(m_p); m_p = x; Py_XINCREF(object()); }
#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
private:
template<typename Y> friend class shared_ptr;
#endif
inline PyObject* object() const
{ return as_object(m_p); }
T* m_p;
};
typedef PyPtr<PyObject> Ptr;
}
#endif // PYPTR_DWA050400_H_

165
signatures.h Normal file
View File

@@ -0,0 +1,165 @@
// (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 by gen_signatures.py for 5 arguments.
#ifndef SIGNATURES_DWA050900_H_
# define SIGNATURES_DWA050900_H_
# include "pyconfig.h"
namespace py {
// A stand-in for the built-in void. This one can be passed to functions and
// (under MSVC, which has a bug, be used as a default template type parameter).
struct Void {};
// An envelope in which type information can be delivered for the purposes
// of selecting an overloaded from_python() function. This is needed to work
// around MSVC's lack of partial specialiation/ordering. Where normally we'd
// want to form a function call like void f<const T&>(), We instead pass
// Type<const T&> as one of the function parameters to select a particular
// overload.
//
// The Id typedef helps us deal with the lack of partial ordering by generating
// unique types for constructor signatures. In general, Type<T>::Id is Type<T>,
// but Type<Void>::Id is just Void.
template <class T>
struct Type
{
typedef Type Id;
};
template <>
struct Type<Void>
{
typedef Void Id;
};
// These basically encapsulate a chain of types, , used to make the syntax of
// add(Constructor<T1, ...>()) work. We need to produce a unique type for each number
// of non-default parameters to Constructor<>. Q: why not use a recursive
// formulation for infinite extensibility? A: MSVC6 seems to choke on constructs
// that involve recursive template nesting.
//
// Signature chaining
template <class T1, class T2, class T3, class T4, class T5>
struct Signature5 {};
template <class T1, class T2, class T3, class T4>
struct Signature4 {};
template <class T1, class T2, class T3, class T4, class X>
inline Signature5<X, T1, T2, T3, T4> prepend(Type<X>, Signature4<T1, T2, T3, T4>)
{ return Signature5<X, T1, T2, T3, T4>(); }
template <class T1, class T2, class T3>
struct Signature3 {};
template <class T1, class T2, class T3, class X>
inline Signature4<X, T1, T2, T3> prepend(Type<X>, Signature3<T1, T2, T3>)
{ return Signature4<X, T1, T2, T3>(); }
template <class T1, class T2>
struct Signature2 {};
template <class T1, class T2, class X>
inline Signature3<X, T1, T2> prepend(Type<X>, Signature2<T1, T2>)
{ return Signature3<X, T1, T2>(); }
template <class T1>
struct Signature1 {};
template <class T1, class X>
inline Signature2<X, T1> prepend(Type<X>, Signature1<T1>)
{ return Signature2<X, T1>(); }
struct Signature0 {};
template <class X>
inline Signature1<X> prepend(Type<X>, Signature0)
{ return Signature1<X>(); }
// This one terminates the chain. Prepending Void to the head of a Void
// signature results in a Void signature again.
inline Signature0 prepend(Void, Signature0) { return Signature0(); }
template <class A1 = Void, class A2 = Void, class A3 = Void, class A4 = Void, class A5 = Void>
struct Constructor
{
};
// Return value extraction:
// This is just another little envelope for carrying a typedef (see Type,
// above). I could have re-used Type, but that has a very specific purpose. I
// thought this would be clearer.
template <class T>
struct ReturnValue { typedef T Type; };
// free functions
template <class R>
ReturnValue<R> return_value(R (*)()) { return ReturnValue<R>(); }
template <class R, class A1>
ReturnValue<R> return_value(R (*)(A1)) { return ReturnValue<R>(); }
template <class R, class A1, class A2>
ReturnValue<R> return_value(R (*)(A1, A2)) { return ReturnValue<R>(); }
template <class R, class A1, class A2, class A3>
ReturnValue<R> return_value(R (*)(A1, A2, A3)) { return ReturnValue<R>(); }
template <class R, class A1, class A2, class A3, class A4>
ReturnValue<R> return_value(R (*)(A1, A2, A3, A4)) { return ReturnValue<R>(); }
template <class R, class A1, class A2, class A3, class A4, class A5>
ReturnValue<R> return_value(R (*)(A1, A2, A3, A4, A5)) { return ReturnValue<R>(); }
// TODO(?): handle 'const void'
// member functions
template <class R, class T>
ReturnValue<R> return_value(R (T::*)()) { return ReturnValue<R>(); }
template <class R, class T, class A1>
ReturnValue<R> return_value(R (T::*)(A1)) { return ReturnValue<R>(); }
template <class R, class T, class A1, class A2>
ReturnValue<R> return_value(R (T::*)(A1, A2)) { return ReturnValue<R>(); }
template <class R, class T, class A1, class A2, class A3>
ReturnValue<R> return_value(R (T::*)(A1, A2, A3)) { return ReturnValue<R>(); }
template <class R, class T, class A1, class A2, class A3, class A4>
ReturnValue<R> return_value(R (T::*)(A1, A2, A3, A4)) { return ReturnValue<R>(); }
template <class R, class T, class A1, class A2, class A3, class A4, class A5>
ReturnValue<R> return_value(R (T::*)(A1, A2, A3, A4, A5)) { return ReturnValue<R>(); }
template <class R, class T>
ReturnValue<R> return_value(R (T::*)() const) { return ReturnValue<R>(); }
template <class R, class T, class A1>
ReturnValue<R> return_value(R (T::*)(A1) const) { return ReturnValue<R>(); }
template <class R, class T, class A1, class A2>
ReturnValue<R> return_value(R (T::*)(A1, A2) const) { return ReturnValue<R>(); }
template <class R, class T, class A1, class A2, class A3>
ReturnValue<R> return_value(R (T::*)(A1, A2, A3) const) { return ReturnValue<R>(); }
template <class R, class T, class A1, class A2, class A3, class A4>
ReturnValue<R> return_value(R (T::*)(A1, A2, A3, A4) const) { return ReturnValue<R>(); }
template <class R, class T, class A1, class A2, class A3, class A4, class A5>
ReturnValue<R> return_value(R (T::*)(A1, A2, A3, A4, A5) const) { return ReturnValue<R>(); }
}
#endif

53
singleton.h Normal file
View File

@@ -0,0 +1,53 @@
// (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.
#ifndef SINGLETON_DWA051900_H_
# define SINGLETON_DWA051900_H_
# include "pyconfig.h"
namespace py {
struct Empty {};
template <class Derived, class Base = Empty>
struct Singleton : Base
{
typedef Singleton SingletonBase; // Convenience type for derived class constructors
static Derived* singleton();
// Pass-through constructors
Singleton() : Base() {}
template <class A1>
Singleton(const A1& a1) : Base(a1) {}
template <class A1, class A2>
Singleton(const A1& a1, const A2& a2) : Base(a1, a2) {}
template <class A1, class A2, class A3>
Singleton(const A1& a1, const A2& a2, const A3& a3) : Base(a1, a2, a3) {}
template <class A1, class A2, class A3, class A4>
Singleton(const A1& a1, const A2& a2, const A3& a3, const A4& a4) : Base(a1, a2, a3, a4) {}
template <class A1, class A2, class A3, class A4, class A5>
Singleton(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) : Base(a1, a2, a3, a4, a5) {}
};
template <class Derived, class Base>
Derived* Singleton<Derived,Base>::singleton()
{
static Derived x;
return &x;
}
}
#endif

398
subclass.cpp Normal file
View File

@@ -0,0 +1,398 @@
// (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.
#include "subclass.h"
#include "functions.h"
#include "singleton.h"
#include <cstddef>
#include "callback.h"
namespace py {
Instance::Instance(PyTypeObject* class_)
: PythonObject(class_)
{
}
PyObject* Instance::getattr(const char* name, bool use_special_function)
{
Ptr local_attribute = m_name_space.get_item(String(name).reference());
if (local_attribute.get())
return local_attribute.release();
// Check its class.
PyObject* function =
PyObject_GetAttrString(as_object(this->ob_type), const_cast<char*>(name));
Ptr class_attribute;
if (!use_special_function || function != 0)
{
// This will throw if the attribute wasn't found
class_attribute = Ptr(function);
}
else
{
// Suspend the error while we try special methods method (if any).
#if 0
SuspendError suspended_error(SuspendError::discard_new_error);
#else
PyErr_Clear();
#endif
// First we try the special method that comes from concatenating
// "__getattr__" and <name> and 2 trailing underscores. This is an
// extension to regular Python class functionality.
const String specific_getattr_name(detail::getattr_string() + name + "__");
PyObject* getattr_method = PyObject_GetAttr(
as_object(this->ob_type), specific_getattr_name.get());
// Use just the first arg to PyEval_CallFunction if found
char* arg_format = const_cast<char*>("(O)");
// Try for the regular __getattr__ method if not found
if (getattr_method == 0)
{
PyErr_Clear();
getattr_method = PyObject_GetAttrString(
as_object(this->ob_type), const_cast<char*>("__getattr__"));
// Use both args to PyEval_CallFunction
arg_format = const_cast<char*>("(Os)");
}
// 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);
// Call it to get the attribute.
return PyEval_CallFunction(getattr_method, arg_format, this, name);
}
if (!PyCallable_Check(class_attribute.get()))
{
PyErr_Clear();
return class_attribute.release();
}
else
{
return new BoundFunction(Ptr(this, Ptr::borrowed), class_attribute);
}
}
// Instance::setattr -
//
// Implements the setattr() and delattr() functionality for our own Instance
// objects, using the standard Python interface: if value == 0, we are deleting
// the attribute, and returns 0 unless an error occurred.
int Instance::setattr(const char* name, PyObject* value)
{
// Try to find an appropriate "specific" setter or getter method, either
// __setattr__<name>__(value) or __delattr__<name>__(). This is an extension
// to regular Python class functionality.
const String& base_name = value ? detail::setattr_string() : detail::delattr_string();
const String specific_method_name(base_name + name + "__");
Ptr special_method(
PyObject_GetAttr(as_object(this->ob_type), specific_method_name.get()),
Ptr::null_ok);
PyObject* result_object = 0;
if (special_method.get() != 0)
{
// The specific function was found; call it now. Note that if value is
// not included in the format string, it is ignored.
char* format_string = const_cast<char*>(value ? "(OO)" : "(O)");
result_object = PyEval_CallFunction(special_method.get(), format_string, this, value);
}
else
{
// If not found, try the usual __setattr__(name, value) or
// __delattr__(name) functions.
PyErr_Clear();
special_method.reset(
PyObject_GetAttr(as_object(this->ob_type), base_name.get()),
Ptr::null_ok);
if (special_method.get() != 0)
{
// The special function was found; call it now. Note that if value
// is not included in the format string, it is ignored.
char* format_string = const_cast<char*>(value ? "(OsO)" : "(Os)");
result_object = PyEval_CallFunction(
special_method.get(), format_string, this, name, value);
}
}
// If we found an appropriate special method, handle the return value.
if (special_method.get() != 0)
{
expect_and_absorb_non_null(result_object);
return 0;
}
PyErr_Clear(); // Nothing was found; clear the python error state
if (value == 0) // Try to remove the attribute from our name space
{
const int result = PyDict_DelItemString(m_name_space.reference().get(),
const_cast<char*>(name));
if (result < 0)
{
PyErr_Clear();
PyErr_SetString(PyExc_AttributeError, "delete non-existing instance attribute");
}
return result;
}
else // Change the specified item in our name space
{
return PyDict_SetItemString(m_name_space.reference().get(),
const_cast<char*>(name), value);
}
}
PyObject* Instance::call(PyObject* args, PyObject* keywords)
{
return PyEval_CallObjectWithKeywords(
Ptr(getattr("__call__")).get(), // take possession of the result from getattr()
args, keywords);
}
PyObject* Instance::repr()
{
return Callback<PyObject*>::call_method(this, "__repr__");
}
int Instance::compare(PyObject* other)
{
return Callback<int>::call_method(this, "__cmp__", other);
}
PyObject* Instance::str()
{
return Callback<PyObject*>::call_method(this, "__str__");
}
long Instance::hash()
{
return Callback<long>::call_method(this, "__hash__");
}
int Instance::length()
{
return Callback<int>::call_method(this, "__len__");
}
PyObject* Instance::get_subscript(PyObject* key)
{
return Callback<PyObject*>::call_method(this, "__getitem__", key);
}
void Instance::set_subscript(PyObject* key, PyObject* value)
{
if (value == 0)
Callback<void>::call_method(this, "__delitem__", key);
else
Callback<void>::call_method(this, "__setitem__", key, value);
}
PyObject* Instance::get_slice(int start, int finish)
{
return Callback<PyObject*>::call_method(this, "__getslice__", start, finish);
}
void Instance::set_slice(int start, int finish, PyObject* value)
{
if (value == 0)
Callback<void>::call_method(this, "__delslice__", start, finish);
else
Callback<void>::call_method(this, "__setslice__", start, finish, value);
}
namespace {
struct NamedCapability
{
const char* name;
TypeObjectBase::Capability capability;
};
const NamedCapability enablers[] =
{
{ "__hash__", TypeObjectBase::hash },
{ "__cmp__", TypeObjectBase::compare },
{ "__repr__", TypeObjectBase::repr },
{ "__str__", TypeObjectBase::str },
{ "__call__", TypeObjectBase::call },
{ "__getattr__", TypeObjectBase::getattr },
{ "__setattr__", TypeObjectBase::setattr },
{ "__len__", TypeObjectBase::mapping_length },
{ "__len__", TypeObjectBase::sequence_length },
{ "__getitem__", TypeObjectBase::mapping_subscript },
{ "__getitem__", TypeObjectBase::sequence_item },
{ "__setitem__", TypeObjectBase::mapping_ass_subscript },
{ "__setitem__", TypeObjectBase::sequence_ass_item },
{ "__delitem__", TypeObjectBase::mapping_ass_subscript },
{ "__delitem__", TypeObjectBase::sequence_ass_item },
{ "__getslice__", TypeObjectBase::sequence_slice },
{ "__setslice__", TypeObjectBase::sequence_ass_slice },
{ "__delslice__", TypeObjectBase::sequence_ass_slice }
};
bool is_prefix(const char* s1, const char* s2)
{
while (*s1 != 0 && *s2 != 0 && *s1 == *s2)
++s1, ++s2;
return *s1 == 0;
}
bool is_special_name(const char* name)
{
if (name[0] != '_' || name[1] != '_' || name[2] == 0 || name[3] == 0)
return false;
std::size_t name_length = PY_CSTD_::strlen(name);
return name[name_length - 1] == '_' && name[name_length - 2] == '_';
}
}
// Enable any special methods which are enabled in the base class.
void enable_special_methods(TypeObjectBase* derived, const Tuple& bases, const Dict& name_space)
{
detail::AllMethods all_methods;
PY_CSTD_::memset(&all_methods, 0, sizeof(all_methods));
for (std::size_t i = 0; i < bases.size(); ++i)
{
PyTypeObject* base = Downcast<PyTypeObject>(bases[i].get());
for (std::size_t n = 0; n < detail::num_capabilities; ++n)
{
detail::add_capability(n, derived, all_methods, base);
}
}
Ptr keys = name_space.keys();
for (std::size_t j = 0, len = PyList_GET_SIZE(keys.get()); j < len; ++j)
{
const char* name = PyString_AsString(PyList_GetItem(keys.get(), j));
if (!is_special_name(name))
continue;
for (std::size_t i = 0; i < PY_ARRAY_LENGTH(enablers); ++i)
{
if (is_prefix(enablers[i].name + 2, name + 2))
{
detail::add_capability(enablers[i].capability, derived, all_methods, 0);
}
}
}
// Now replace those pointers with a persistent copy
using detail::UniquePodSet;
if (derived->tp_as_buffer)
derived->tp_as_buffer = UniquePodSet::instance().get(*derived->tp_as_buffer);
if (derived->tp_as_number)
derived->tp_as_number = UniquePodSet::instance().get(*derived->tp_as_number);
if (derived->tp_as_sequence)
derived->tp_as_sequence = UniquePodSet::instance().get(*derived->tp_as_sequence);
if (derived->tp_as_mapping)
derived->tp_as_mapping = UniquePodSet::instance().get(*derived->tp_as_mapping);
}
// Enable the special handler for methods of the given name, if any.
void enable_named_method(TypeObjectBase* type_object, const char* name)
{
const std::size_t num_enablers = sizeof(enablers) / sizeof(enablers[0]);
// Make sure this ends with "__" since we'll only compare the head of the
// string. This is done to make the __getattr__<name>__/__setattr__<name>__
// extension work.
if (!is_special_name(name))
return;
for (std::size_t i = 0; i < num_enablers; ++i)
{
if (is_prefix(enablers[i].name + 2, name + 2))
{
type_object->enable(enablers[i].capability);
}
}
}
void add_current_module_name(Dict& name_space)
{
static String module_key("__module__", String::interned);
static String name_key("__name__", String::interned);
Ptr existing_value = name_space.get_item(module_key);
if (existing_value.get() == 0)
{
PyObject* globals = PyEval_GetGlobals();
if (globals != 0) // Why don't we throw in this case? Who knows? This is
{ // what Python does for class objects!
PyObject* module_name = PyDict_GetItem(globals, name_key.get());
if (module_name != 0)
{
name_space[module_key] = Ptr(module_name, Ptr::borrowed);
}
}
}
}
void adjust_slice_indices(PyObject* instance, int& start, int& finish)
{
int length = Callback<int>::call_method(instance, "__len__");
// This is standard Python class behavior.
if (start < 0)
start += length;
if (finish < 0)
finish += length;
// This is not
if (start < 0)
start = 0;
if (finish < 0)
finish = 0;
}
namespace detail {
const String& setattr_string()
{
static String x("__setattr__", String::interned);
return x;
}
const String& getattr_string()
{
static String x("__getattr__", String::interned);
return x;
}
const String& delattr_string()
{
static String x("__delattr__", String::interned);
return x;
}
}
} // namespace py

371
subclass.h Normal file
View File

@@ -0,0 +1,371 @@
// (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.
#ifndef SUBCLASS_DWA051500_H_
# define SUBCLASS_DWA051500_H_
# include "pyconfig.h"
# include "newtypes.h"
# include "objects.h"
# include "singleton.h"
# include <boost/utility.hpp>
# include "py.h"
# include "callback.h"
namespace py {
// A simple type which acts something like a built-in Python class instance.
// TODO: implement all the special methods, like __call__, __getattr__, etc.,
// and the other special attributes, like __dict__.
class Instance : public PythonObject
{
public:
Instance(PyTypeObject* class_);
// Standard Python functions.
PyObject* repr();
int compare(PyObject*);
PyObject* str();
long hash();
PyObject* call(PyObject* args, PyObject* keywords);
PyObject* getattr(const char* name, bool use_special_function = true);
int setattr(const char* name, PyObject* value);
// Mapping methods
int length();
PyObject* get_subscript(PyObject* key);
void set_subscript(PyObject* key, PyObject* value);
// Sequence methods
PyObject* get_slice(int start, int finish);
void set_slice(int start, int finish, PyObject* value);
private: // noncopyable, without the size bloat
Instance(const Instance&);
void operator=(const Instance&);
private:
Dict m_name_space;
};
template <class T> class MetaClass;
// A type which acts a lot like a built-in Python class. T is the instance type,
// so Class<Instance> is a very simple "class-alike".
template <class T>
class Class
: public Getattrable<Setattrable<TypeObject<T> > >
{
public:
Class(MetaClass<T>* meta_class, String name, Tuple bases, const Dict& name_space);
Tuple bases() const;
String name() const;
Dict& dict();
// Standard Python functions.
PyObject* getattr(const char* name);
int setattr(const char* name, PyObject* value);
PyObject* call(PyObject* args, PyObject* keywords);
protected:
void add_base(Ptr base);
private: // Implement mapping methods on instances
PyObject* instance_repr(PyObject*) const;
int instance_compare(PyObject*, PyObject* other) const;
PyObject* instance_str(PyObject*) const;
long instance_hash(PyObject*) const;
int instance_mapping_length(PyObject*) const;
PyObject* instance_mapping_subscript(PyObject*, PyObject*) const;
int instance_mapping_ass_subscript(PyObject*, PyObject*, PyObject*) const;
private: // Implement sequence methods on instances
int instance_sequence_length(PyObject*) const;
PyObject* instance_sequence_item(PyObject* instance, int n) const;
int instance_sequence_ass_item(PyObject* instance, int n, PyObject* value) const;
PyObject* instance_sequence_slice(PyObject*, int start, int finish) const;
int instance_sequence_ass_slice(PyObject*, int start, int finish, PyObject* value) const;
private: // Miscellaneous "special" methods
PyObject* instance_call(PyObject* instance, PyObject* args, PyObject* keywords) const;
private: // noncopyable, without the size bloat
Class(const Class<T>&);
void operator=(const Class&);
private:
String m_name;
Tuple m_bases;
Dict m_name_space;
};
// Don't really need to be friends, but are essentially part of the Class interface.
// These operate on TypeObjectBase just to save on code space.
void enable_special_methods(TypeObjectBase*, const Tuple& bases, const Dict& name_space);
void enable_named_method(TypeObjectBase*, const char*);
// The type of a Class<T> object.
template <class T>
class MetaClass
: public Callable<Getattrable<Setattrable<TypeObject<Class<T> > > > >,
boost::noncopyable
{
public:
MetaClass();
// Standard Python functions.
PyObject* call(PyObject* args, PyObject* keywords);
private:
struct TypeObject
: Singleton<TypeObject, Callable<py::TypeObject<MetaClass> > >
{
TypeObject() : SingletonBase(&PyType_Type) {}
};
};
// Add the name of the module currently being loaded to the name_space with the
// key "__module__". If no module is being loaded, or if name_space already has
// a key "__module", has no effect. This is not really a useful public
// interface; it's just used for Class<>::Class() below.
void add_current_module_name(Dict&);
//
// Member function implementations.
//
template <class T>
MetaClass<T>::MetaClass()
: Properties(TypeObject::singleton())
{
}
template <class T>
Class<T>::Class(MetaClass<T>* meta_class, String name, Tuple bases, const Dict& name_space)
: Properties(meta_class, name.c_str()),
m_name(name),
m_bases(bases),
m_name_space(name_space)
{
add_current_module_name(m_name_space);
enable_special_methods(this, bases, name_space);
}
template <class T>
String Class<T>::name() const
{
return m_name;
}
template <class T>
PyObject* Class<T>::getattr(const char* name)
{
Ptr local_attribute = m_name_space.get_item(String(name).reference());
if (local_attribute.get())
return local_attribute.release();
// In case there are no bases...
PyErr_SetString(PyExc_AttributeError, name);
// Check bases
for (std::size_t i = 0; i < m_bases.size(); ++i)
{
if (PyErr_ExceptionMatches(PyExc_AttributeError))
PyErr_Clear(); // we're going to try a base class
else if (PyErr_Occurred())
break; // Other errors count, though!
PyObject* base_attribute = PyObject_GetAttrString(m_bases[i].get(), const_cast<char*>(name));
if (base_attribute != 0)
return base_attribute;
}
return 0;
}
template <class T>
int Class<T>::setattr(const char* name, PyObject* value)
{
if (PyCallable_Check(value))
enable_named_method(this, name);
return PyDict_SetItemString(
m_name_space.reference().get(), const_cast<char*>(name), value);
}
template <class T>
PyObject* Class<T>::call(PyObject* args, PyObject* keywords)
{
PyPtr<T> result(new T(this));
// Getting the init function off the result instance should result in a
// bound method.
PyObject* const init_function = result->getattr("__init__", false);
if (init_function == 0)
{
if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear(); // no __init__? That's legal.
}
else {
return 0; // Something else? Keep the error
}
}
else
{
// Manage the reference to the bound function
Ptr init_function_holder(init_function);
// Declare a Ptr to manage the result of calling __init__ (which should be None).
Ptr init_result(
PyEval_CallObjectWithKeywords(init_function, args, keywords));
}
return result.release();
}
template <class T>
PyObject* Class<T>::instance_repr(PyObject* instance) const
{
return Downcast<T>(instance)->repr();
}
template <class T>
int Class<T>::instance_compare(PyObject* instance, PyObject* other) const
{
return Downcast<T>(instance)->compare(other);
}
template <class T>
PyObject* Class<T>::instance_str(PyObject* instance) const
{
return Downcast<T>(instance)->str();
}
template <class T>
long Class<T>::instance_hash(PyObject* instance) const
{
return Downcast<T>(instance)->hash();
}
template <class T>
int Class<T>::instance_mapping_length(PyObject* instance) const
{
return Downcast<T>(instance)->length();
}
template <class T>
int Class<T>::instance_sequence_length(PyObject* instance) const
{
return Downcast<T>(instance)->length();
}
template <class T>
PyObject* Class<T>::instance_mapping_subscript(PyObject* instance, PyObject* key) const
{
return Downcast<T>(instance)->get_subscript(key);
}
template <class T>
PyObject* Class<T>::instance_sequence_item(PyObject* instance, int n) const
{
Ptr key(to_python(n));
return Downcast<T>(instance)->get_subscript(key.get());
}
template <class T>
int Class<T>::instance_sequence_ass_item(PyObject* instance, int n, PyObject* value) const
{
Ptr key(to_python(n));
Downcast<T>(instance)->set_subscript(key.get(), value);
return 0;
}
template <class T>
int Class<T>::instance_mapping_ass_subscript(PyObject* instance, PyObject* key, PyObject* value) const
{
Downcast<T>(instance)->set_subscript(key, value);
return 0;
}
void adjust_slice_indices(PyObject* instance, int& start, int& finish);
template <class T>
PyObject* Class<T>::instance_sequence_slice(PyObject* instance, int start, int finish) const
{
adjust_slice_indices(instance, start, finish);
return Downcast<T>(instance)->get_slice(start, finish);
}
template <class T>
int Class<T>::instance_sequence_ass_slice(PyObject* instance, int start, int finish, PyObject* value) const
{
adjust_slice_indices(instance, start, finish);
Downcast<T>(instance)->set_slice(start, finish, value);
return 0;
}
template <class T>
PyObject* Class<T>::instance_call(PyObject* instance, PyObject* args, PyObject* keywords) const
{
return Downcast<T>(instance)->call(args, keywords);
}
template <class T>
Dict& Class<T>::dict()
{
return m_name_space;
}
template <class T>
Tuple Class<T>::bases() const
{
return m_bases;
}
template <class T>
void Class<T>::add_base(Ptr base)
{
Tuple new_bases(m_bases.size() + 1);
for (std::size_t i = 0; i < m_bases.size(); ++i)
new_bases.set_item(i, m_bases[i]);
new_bases.set_item(m_bases.size(), base);
m_bases = new_bases;
}
template <class T>
PyObject* MetaClass<T>::call(PyObject* args, PyObject* /*keywords*/)
{
PyObject* name;
PyObject* bases;
PyObject* name_space;
if (!PyArg_ParseTuple(args, const_cast<char*>("O!O!O!"),
&PyString_Type, &name,
&PyTuple_Type, &bases,
&PyDict_Type, &name_space))
{
return 0;
}
return as_object(
new Class<T>(this, String(Ptr(name, Ptr::borrowed)),
Tuple(Ptr(bases, Ptr::borrowed)),
Dict(Ptr(name_space, Ptr::borrowed)))
);
}
namespace detail {
const String& setattr_string();
const String& getattr_string();
const String& delattr_string();
}
} // namespace py
#endif

2
subclass_d.cpp Normal file
View File

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

50
test_example1.py Normal file
View File

@@ -0,0 +1,50 @@
r'''
// (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.
That's it! If we build this shared library and put it on our PYTHONPATH we can
now access our C++ class and function from Python.
>>> import hello
>>> hi_world = hello.world(3)
>>> hi_world.get()
'hi, world'
>>> hello.length(hi_world)
9
We can even make a subclass of hello.world:
>>> class my_subclass(hello.world):
... def get(self):
... return 'hello, world'
...
>>> y = my_subclass(2)
>>> y.get()
'hello, world'
Pretty cool! You can't do that with an ordinary Python extension type!
>>> hello.length(y)
9
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 length() of
12? If so, read on...
'''
from hello import *
def run(args = None):
if args is not None:
import sys
sys.argv = args
import doctest, test_example1
doctest.testmod(test_example1)
if __name__ == '__main__':
run()

423
test_extclass.py Normal file
View File

@@ -0,0 +1,423 @@
r'''
// (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.
Automatic checking of the number and type of arguments. Foo's constructor takes
a single long parameter.
>>> ext = Foo()
Traceback (innermost last):
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
>>> ext = Foo(1)
Call a virtual function. This call takes a trip into C++ where
FooCallback::add_len() looks up the Python "add_len" attribute and finds the
wrapper for FooCallback::default_add_len(), which in turn calls Foo::add_len().
>>> ext.add_len('hello')
6
>>> ext.set(3)
>>> ext.add_len('hello')
8
Call a pure virtual function which should have been overridden, but was not.
>>> ext.call_pure()
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: pure
We can subclass Foo.
>>> class Subclass(Foo):
... def __init__(self, seq):
... Foo.__init__(self, len(seq))
...
... def pure(self):
... return 'not pure anymore!'
...
... def get(self):
... return Foo.add_len(self, '')
...
... def add_len(self, s):
... print 'called add_len()'
... return self.get() + len(s)
...
>>> b = Subclass('yippee')
>>> b.get()
6
>>> b.mumble()
'mumble'
>>> b.call_pure()
'not pure anymore!'
If no __init__ function is defined, the one from the base class takes effect, just
like in a Python class.
>>> class DemonstrateInitPassthru(Foo): pass
...
>>> q = DemonstrateInitPassthru(1)
>>> q.add_len("x")
2
If we don't initialize the base class, we'll get a RuntimeError when we try to
use its methods. The test illustrates the kind of error to expect.
>>> class BadSubclass(Foo):
... def __init__(self): pass
...
>>> barf = BadSubclass()
>>> barf.set(4)
Traceback (innermost last):
...
RuntimeError: __init__ function for extension class 'Foo' was never called.
Here we are tesing that the simple definition procedure used in the C++ demo
file for classes without any virtual functions actually worked.
>>> bar = Bar(3, 4)
>>> bar.first()
3
>>> bar.second()
4
>>> baz = Baz()
We can actually return the wrapped classes by value
>>> baz.pass_bar(bar).first()
3
>>> bar.pass_baz(baz) is baz # A copy of the return value is made.
0
>>> type(bar.pass_baz(baz)) is type(baz)
1
And, yes, we can multiply inherit from these classes.
>>> class MISubclass(Subclass, Bar):
... def __init__(self, s):
... Subclass.__init__(self, s)
... Bar.__init__(self, 0, len(s))
...
>>> mi = MISubclass('xx')
>>> mi.first()
0
>>> mi.second()
2
>>> mi.mumble()
'mumble'
Any object whose class is derived from Bar can be passed to a function expecting
a Bar parameter:
>>> baz.pass_bar(mi).first()
0
But objects not derived from Bar cannot:
>>> baz.pass_bar(baz)
Traceback (innermost last):
...
TypeError: extension class 'Baz' is not derived from '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.
>>> baz_clone = baz.clone()
>>> baz_clone.pass_bar(mi).first()
0
Functions expecting an std::auto_ptr<Baz> parameter will not accept a raw Baz
>>> try: baz.eat_baz(Baz())
... except RuntimeError, err:
... assert re.match("Object of extension class 'Baz' does not wrap <.*>.",
... str(err))
We can pass std::auto_ptr<Baz> where it is expected
>>> baz.eat_baz(baz_clone)
And if the auto_ptr has given up ownership?
# MSVC6 ships with an outdated auto_ptr that doesn't get zeroed out when it
# gives up ownership. If you are using MSVC6 without the new Dinkumware
# library, SGI STL or the STLport, expect this test to crash unless you put
# --broken-auto-ptr on the command line.
>>> if not '--broken-auto-ptr' in sys.argv:
... try: baz_clone.clone()
... except RuntimeError, err:
... assert re.match('Converting from python, pointer or smart pointer to <.*> is NULL.', str(err))
Polymorphism also works:
>>> polymorphic_foo = baz.create_foo()
>>> polymorphic_foo.call_pure()
'this was never pure!'
>>> baz.get_foo_value(polymorphic_foo)
1000
Special member functions in action
>>> m = StringMap()
__getitem__(<unknown key>)
>>> m[1]
Traceback (innermost last):
File "<stdin>", line 1, in ?
KeyError: 1
__setitem__()
>>> m[1] = 'hello'
__getitem__(<known key>)
>>> m[1]
'hello'
__delitem__(<known key>)
>>> del m[1]
>>> m[1] # prove that it's gone
Traceback (innermost last):
File "<stdin>", line 1, in ?
KeyError: 1
__delitem__(<unknown key>)
>>> del m[2]
Traceback (innermost last):
File "<stdin>", line 1, in ?
KeyError: 2
__length__()
>>> len(m)
0
>>> m[3] = 'farther'
>>> len(m)
1
Check for sequence/mapping confusion:
>>> for x in m:
... print x
...
Traceback (innermost last):
File "<stdin>", line 1, in ?
KeyError: 0
Overloading tests:
>>> r = Range(3)
>>> print str(r)
(3, 3)
>>> r.start
3
>>> r.finish
3
>>> r.__len__()
0
>>> r.__len__(4)
>>> r.finish
7
>>> try: r = Range('yikes')
... except TypeError, e:
... assert re.match(
... 'No overloaded functions match [(]Range, string[)]\. Candidates are:\n.*\n.*',
... str(e))
Sequence tests:
>>> len(Range(3, 10))
7
>>> map(lambda x:x, Range(3, 10))
[3, 4, 5, 6, 7, 8, 9]
>>> map(lambda x:x, Range(3, 10)[-2:])
[8, 9]
>>> map(lambda x:x, Range(3, 10)[:-4])
[3, 4, 5]
>>> map(lambda x:x, Range(3, 10)[4:])
[7, 8, 9]
>>> map(lambda x:x, Range(3, 10)[4:100])
[7, 8, 9]
>>> map(lambda x:x, Range(3, 10)[20:])
[]
>>> map(lambda x:x, Range(3, 10)[0:4])
[3, 4, 5, 6]
delete non-existent attribute:
del m.foobar
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: delete non-existing instance attribute
Testing __getattr__ and __getattr__<name>:
>>> n = IntPair(1, 2)
>>> n.first
1
>>> n.second
2
>>> n.third
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: third
Testing __setattr__ and __setattr__<name>:
>>> n.first = 33 # N.B __setattr__first sets first to
>>> n.first # the negative of its argument.
-33
>>> n.second = 66
>>> n.second
66
Testing __delattr__ and __delattr__<name>:
>>> del n.first
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: first can't be deleted!
>>> del n.second
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: Attributes can't be deleted!
>>> del n.third
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: Attributes can't be deleted!
# Now show that we can override it.
>>> class IntTriple(IntPair):
... def __getattr__(self, s):
... if s in ['first', 'second']:
... return IntPair.__getattr__(self, s)
... elif s == 'third':
... return 3
... else:
... raise AttributeError(s)
...
... # Also show that __setattr__ is supported
... def __setattr__(self, name, value):
... raise AttributeError('no writable attributes')
...
>>> p = IntTriple(0, 1)
>>> p.first
0
>>> p.second
1
>>> p.third
3
>>> p.bax
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: bax
>>> p.third = 'yes'
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: no writable attributes
>>> del p.third
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: Attributes can't be deleted!
demonstrate def_readonly, def_read_write:
>>> sp = StringPair("hello", "world")
>>> sp.first # first is read-only
'hello'
>>> first_string(sp) # prove that we're not just looking in sp's __dict__
'hello'
>>> sp.first = 'hi' # we're not allowed to change it
Traceback (innermost last):
File "<string>", line 1, in ?
AttributeError: 'first' attribute is read-only
>>> first_string(sp) # prove that it hasn't changed
'hello'
>>> sp.second # second is read/write
'world'
>>> second_string(sp)
'world'
>>> sp.second = 'universe' # set the second attribute
>>> sp.second
'universe'
>>> second_string(sp) # this proves we didn't just set it in sp's __dict__
'universe'
some __str__ and __repr__ tests:
>>> sp
('hello', 'universe')
>>> repr(sp)
"('hello', 'universe')"
>>> str(sp)
"('hello', 'universe')"
Range has a __str__ function but not a __repr__ function
>>> range = Range(5, 20)
>>> str(range)
'(5, 20)'
>>> assert re.match('<Range object at [0-9a-f]+>', repr(range))
__hash__ and __cmp__ tests:
# Range has both __hash__ and __cmp__, thus is hashable
>>> colors = { Range(3,4): 'blue', Range(7,9): 'red' }
>>> colors[Range(3,4)]
'blue'
# StringPair has only __cmp__
>>> { StringPair('yo', 'eddy'): 1 }
Traceback (innermost last):
File "<string>", line 1, in ?
TypeError: unhashable type
# But it can be sorted
>>> stringpairs = [ StringPair('yo', 'eddy'), StringPair('yo', 'betty'), sp ]
>>> stringpairs.sort()
>>> stringpairs
[('hello', 'universe'), ('yo', 'betty'), ('yo', 'eddy')]
make_pair is a global function in the module.
>>> couple = make_pair(3,12)
>>> couple.first
3
>>> couple.second
12
Testing __call__:
>>> couple2 = make_pair(3, 7)
>>> comparator = CompareIntPair()
>>> comparator(couple, couple)
0
>>> comparator(couple, couple2)
0
>>> comparator(couple2, couple)
1
'''
from demo import *
import string
import re
import sys
def run(args = None):
if args is not None:
sys.argv = args
import doctest, test_extclass
doctest.testmod(test_extclass)
if __name__ == '__main__':
run()

63
todo.txt Normal file
View File

@@ -0,0 +1,63 @@
Better python and C++ exception handling/error reporting.
long long support
use Python generic numeric coercion in from_python() for C++ numeric types
Document error-handling
Consider renaming PyPtr to Reference.
Report Cygwin linker memory issues
handle more arguments
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.
Handle polymorphism (passing a Wrapped<Derived> as a Base*).
Specializations of Caller<> for commmon combinations of argument types (?)
Documentation:
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
exception handling
building
dealing with non-const reference/pointer parameters
Boost remarks:
> > One of us is completely nuts ;->. How can I move the test
> > (is_prefix(enablers[i].name + 2, name + 2)) outside the loop if it
depends
> > on the loop index, i?
> >
> name += 2;
> for()
> {
> if (is_prefix(enablers[i].name + 2, name))
> }
I see now. I guess I should stop pussyfooting and either go for optimization
or clarity here, eh?
------
> Re: Dict
> Why abbreviate this? Code is read 5 or 6 times for every time its
> written. The few extra characters don't affect compile time or program
> speed. It's part of my personal goal of write what you mean, name them
what
> they are.
I completely agree. Abbrevs rub me the wrong way, 2 ;->
-------
Later:
keyword and varargs?
Put explicit Type<> arguments at the beginnings of overloads, to make them look more like template instance specifications.
Known bugs
can't handle 'const void' return values
Who returns 'const void'? I did it once, by mistake ;)

41
vc6_prj/ReadMe.txt Normal file
View File

@@ -0,0 +1,41 @@
========================================================================
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.
/////////////////////////////////////////////////////////////////////////////

8
vc6_prj/StdAfx.cpp Normal file
View File

@@ -0,0 +1,8 @@
// 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

24
vc6_prj/StdAfx.h Normal file
View File

@@ -0,0 +1,24 @@
// 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_)

6
vc6_prj/test_demo.py Normal file
View File

@@ -0,0 +1,6 @@
import demo # Get demo imported now so test_extclass won't look in its own directory
import os
os.chdir('..')
import test_extclass
test_extclass.run(['-v', '--broken-auto-ptr'])

6
vc6_prj/test_hello.py Normal file
View File

@@ -0,0 +1,6 @@
import hello # Get demo imported now so test_extclass won't look in its own directory
import os
os.chdir('..')
import test_example1
test_example1.run(["-v"])

13
vc6_prj/vc6_prj.cpp Normal file
View File

@@ -0,0 +1,13 @@
// 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;
}

157
vc6_prj/vc6_prj.dsp Normal file
View File

@@ -0,0 +1,157 @@
# Microsoft Developer Studio Project File - Name="vc6_prj" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=vc6_prj - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "vc6_prj.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "vc6_prj.mak" CFG="vc6_prj - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "vc6_prj - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "vc6_prj - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "vc6_prj - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 1
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VC6_PRJ_EXPORTS" /Yu"stdafx.h" /FD /c
# ADD CPP /nologo /MT /W3 /GR /GX /Ox /Ot /Og /Oi /Os /Gy /I "c:\boost" /I "c:\tools\python\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VC6_PRJ_EXPORTS" /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"demo.dll" /libpath:"c:/tools/python/libs"
# SUBTRACT LINK32 /pdb:none /debug
!ELSEIF "$(CFG)" == "vc6_prj - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VC6_PRJ_EXPORTS" /Yu"stdafx.h" /FD /GZ /c
# ADD CPP /nologo /MTd /Gm /GR /GX /ZI /Od /I "c:\boost" /I "c:\tools\python\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VC6_PRJ_EXPORTS" /FR /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"demo_d.dll" /pdbtype:sept /libpath:"c:/tools/python/src/pcbuild"
!ENDIF
# Begin Target
# Name "vc6_prj - Win32 Release"
# Name "vc6_prj - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=..\extclass.cpp
!IF "$(CFG)" == "vc6_prj - Win32 Release"
!ELSEIF "$(CFG)" == "vc6_prj - Win32 Debug"
# ADD CPP /I "c:\tools\python\src\include" /D "DEBUG_PYTHON"
!ENDIF
# End Source File
# Begin Source File
SOURCE=..\extclass_demo.cpp
# End Source File
# Begin Source File
SOURCE=..\functions.cpp
# End Source File
# Begin Source File
SOURCE=..\init_function.cpp
# End Source File
# Begin Source File
SOURCE=..\module.cpp
# End Source File
# Begin Source File
SOURCE=..\newtypes.cpp
# End Source File
# Begin Source File
SOURCE=..\objects.cpp
# End Source File
# Begin Source File
SOURCE=..\py.cpp
# End Source File
# Begin Source File
SOURCE=..\subclass.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\StdAfx.h
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# Begin Source File
SOURCE=.\ReadMe.txt
# End Source File
# End Target
# End Project

29
vc6_prj/vc6_prj.dsw Normal file
View File

@@ -0,0 +1,29 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "vc6_prj"=.\vc6_prj.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

BIN
vc6_prj/vc6_prj.opt Normal file

Binary file not shown.

61
wrap_python.h Normal file
View File

@@ -0,0 +1,61 @@
#ifdef _DEBUG
# ifndef DEBUG_PYTHON
# undef _DEBUG // Don't let Python force the debug library just because we're debugging.
# define DEBUG_UNDEFINED_FROM_WRAP_PYTHON_H
# endif
#endif
//
// Some things we need in order to get Python.h to work with compilers other
// than MSVC on Win32
//
#if defined(_WIN32)
# ifdef __GNUC__
typedef int pid_t;
# define WORD_BIT 32
# define hypot _hypot
# include <stdio.h>
# define HAVE_CLOCK
# define HAVE_STRFTIME
# define HAVE_STRERROR
# define NT_THREADS
# define WITH_THREAD
# ifndef NETSCAPE_PI
# define USE_SOCKET
# endif
# ifdef USE_DL_IMPORT
# define DL_IMPORT(RTYPE) __declspec(dllimport) RTYPE
# endif
# ifdef USE_DL_EXPORT
# define DL_IMPORT(RTYPE) __declspec(dllexport) RTYPE
# define DL_EXPORT(RTYPE) __declspec(dllexport) RTYPE
# endif
# define HAVE_LONG_LONG 1
# define LONG_LONG long long
# elif defined(__MWERKS__)
# ifndef _MSC_VER
# define PY_MSC_VER_DEFINED_FROM_WRAP_PYTHON_H 1
# define _MSC_VER 900
# endif
# endif
#endif // _WIN32
#include <Python.h>
#ifdef PY_MSC_VER_DEFINED_FROM_WRAP_PYTHON_H
# undef _MSC_VER
#endif
#ifdef DEBUG_UNDEFINED_FROM_WRAP_PYTHON_H
# undef DEBUG_UNDEFINED_FROM_WRAP_PYTHON_H
# define _DEBUG
#endif