From 12a881ead5c8dae7201c2c8560199b3d5232cd38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20K=C3=B6the?= Date: Fri, 13 Oct 2000 13:49:34 +0000 Subject: [PATCH] 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] --- .gitattributes | 96 ++++ base_object.h | 62 +++ callback.h | 83 +++ caller.h | 507 +++++++++++++++++++ cast.h | 79 +++ class_wrapper.h | 87 ++++ demo.bdf | 4 + demo_d.bdf | 4 + doctest.py | 1112 +++++++++++++++++++++++++++++++++++++++++ doxyfile | 708 ++++++++++++++++++++++++++ errors.h | 30 ++ example1.bdf | 4 + example1.cpp | 54 ++ extclass.cpp | 343 +++++++++++++ extclass.h | 439 ++++++++++++++++ extclass_d.cpp | 3 + extclass_demo.cpp | 382 ++++++++++++++ extclass_demo.h | 231 +++++++++ extclass_demo.py | 23 + extclass_demo_d.cpp | 2 + extclass_pygen.h | 80 +++ functions.cpp | 161 ++++++ functions.h | 175 +++++++ functions_d.cpp | 2 + gcc.mak | 40 ++ gen_all.py | 26 + gen_callback.py | 71 +++ gen_caller.py | 138 +++++ gen_extclass.py | 81 +++ gen_function.py | 184 +++++++ gen_init_function.py | 82 +++ gen_signatures.py | 152 ++++++ gen_singleton.py | 58 +++ init_function.cpp | 36 ++ init_function.h | 184 +++++++ init_function_d.cpp | 2 + makefile | 48 ++ module.cpp | 39 ++ module.h | 43 ++ module_d.cpp | 2 + newtypes.cpp | 628 +++++++++++++++++++++++ newtypes.h | 264 ++++++++++ newtypes_d.cpp | 2 + none.h | 21 + objects.cpp | 454 +++++++++++++++++ objects.h | 233 +++++++++ objects_d.cpp | 2 + py.cpp | 204 ++++++++ py.h | 266 ++++++++++ py_cpp.bdf | 4 + py_cpp_20001013.zip | Bin 0 -> 104507 bytes py_cpp_d.bdf | 4 + py_cpphelp.mak | 21 + py_d.cpp | 2 + pyconfig.h | 40 ++ pyptr.h | 143 ++++++ signatures.h | 165 ++++++ singleton.h | 53 ++ subclass.cpp | 398 +++++++++++++++ subclass.h | 371 ++++++++++++++ subclass_d.cpp | 2 + test_example1.py | 50 ++ test_extclass.py | 423 ++++++++++++++++ todo.txt | 63 +++ vc6_prj/ReadMe.txt | 41 ++ vc6_prj/StdAfx.cpp | 8 + vc6_prj/StdAfx.h | 24 + vc6_prj/test_demo.py | 6 + vc6_prj/test_hello.py | 6 + vc6_prj/vc6_prj.cpp | 13 + vc6_prj/vc6_prj.dsp | 157 ++++++ vc6_prj/vc6_prj.dsw | 29 ++ vc6_prj/vc6_prj.opt | Bin 0 -> 64000 bytes wrap_python.h | 61 +++ 74 files changed, 10015 insertions(+) create mode 100644 .gitattributes create mode 100644 base_object.h create mode 100644 callback.h create mode 100644 caller.h create mode 100644 cast.h create mode 100644 class_wrapper.h create mode 100644 demo.bdf create mode 100644 demo_d.bdf create mode 100644 doctest.py create mode 100644 doxyfile create mode 100644 errors.h create mode 100644 example1.bdf create mode 100644 example1.cpp create mode 100644 extclass.cpp create mode 100644 extclass.h create mode 100644 extclass_d.cpp create mode 100644 extclass_demo.cpp create mode 100644 extclass_demo.h create mode 100644 extclass_demo.py create mode 100644 extclass_demo_d.cpp create mode 100644 extclass_pygen.h create mode 100644 functions.cpp create mode 100644 functions.h create mode 100644 functions_d.cpp create mode 100644 gcc.mak create mode 100644 gen_all.py create mode 100644 gen_callback.py create mode 100644 gen_caller.py create mode 100644 gen_extclass.py create mode 100644 gen_function.py create mode 100644 gen_init_function.py create mode 100644 gen_signatures.py create mode 100644 gen_singleton.py create mode 100644 init_function.cpp create mode 100644 init_function.h create mode 100644 init_function_d.cpp create mode 100644 makefile create mode 100644 module.cpp create mode 100644 module.h create mode 100644 module_d.cpp create mode 100644 newtypes.cpp create mode 100644 newtypes.h create mode 100644 newtypes_d.cpp create mode 100644 none.h create mode 100644 objects.cpp create mode 100644 objects.h create mode 100644 objects_d.cpp create mode 100644 py.cpp create mode 100644 py.h create mode 100644 py_cpp.bdf create mode 100644 py_cpp_20001013.zip create mode 100644 py_cpp_d.bdf create mode 100644 py_cpphelp.mak create mode 100644 py_d.cpp create mode 100644 pyconfig.h create mode 100644 pyptr.h create mode 100644 signatures.h create mode 100644 singleton.h create mode 100644 subclass.cpp create mode 100644 subclass.h create mode 100644 subclass_d.cpp create mode 100644 test_example1.py create mode 100644 test_extclass.py create mode 100644 todo.txt create mode 100644 vc6_prj/ReadMe.txt create mode 100644 vc6_prj/StdAfx.cpp create mode 100644 vc6_prj/StdAfx.h create mode 100644 vc6_prj/test_demo.py create mode 100644 vc6_prj/test_hello.py create mode 100644 vc6_prj/vc6_prj.cpp create mode 100644 vc6_prj/vc6_prj.dsp create mode 100644 vc6_prj/vc6_prj.dsw create mode 100644 vc6_prj/vc6_prj.opt create mode 100644 wrap_python.h diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..3e84d7c7 --- /dev/null +++ b/.gitattributes @@ -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 diff --git a/base_object.h b/base_object.h new file mode 100644 index 00000000..e9183178 --- /dev/null +++ b/base_object.h @@ -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 + +namespace py { + +// BaseObject - adds a constructor and non-virtual destructor to a +// base Python type (e.g. PyObject, PyTypeObject). +template +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 PythonObject; +typedef BaseObject PythonType; + + +// +// Class template member function implementations +// +template +BaseObject::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 +inline BaseObject::~BaseObject() +{ + Py_DECREF(ob_type); +} + +} + +#endif BASE_OBJECT_DWA051600_H_ diff --git a/callback.h b/callback.h new file mode 100644 index 00000000..a88b03f8 --- /dev/null +++ b/callback.h @@ -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 +struct Callback +{ + static R call_method(PyObject* self, const char* name) + { return from_python(expect_non_null(PyEval_CallMethod(self, const_cast(name), const_cast("()"))), Type()); } + + template + static R call_method(PyObject* self, const char* name, const A1& a1) + { return from_python(expect_non_null(PyEval_CallMethod(self, const_cast(name), const_cast("(N)"), to_python(a1))), Type()); } + + template + 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(name), const_cast("(NN)"), to_python(a1), to_python(a2))), Type()); } + + template + 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(name), const_cast("(NNN)"), to_python(a1), to_python(a2), to_python(a3))), Type()); } + + template + 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(name), const_cast("(NNNN)"), to_python(a1), to_python(a2), to_python(a3), to_python(a4))), Type()); } + + template + 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(name), const_cast("(NNNNN)"), to_python(a1), to_python(a2), to_python(a3), to_python(a4), to_python(a5))), Type()); } + +}; + +// This specialization wouldn't be needed, but MSVC6 doesn't correctly allow the following: +// void g(); +// void f() { return g(); } +template <> +struct Callback +{ static void call_method(PyObject* self, const char* name) + { expect_and_absorb_non_null(PyEval_CallMethod(self, const_cast(name), const_cast("()"))); } + + template + static void call_method(PyObject* self, const char* name, const A1& a1) + { expect_and_absorb_non_null(PyEval_CallMethod(self, const_cast(name), const_cast("(N)"), to_python(a1))); } + + template + 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(name), const_cast("(NN)"), to_python(a1), to_python(a2))); } + + template + 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(name), const_cast("(NNN)"), to_python(a1), to_python(a2), to_python(a3))); } + + template + 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(name), const_cast("(NNNN)"), to_python(a1), to_python(a2), to_python(a3), to_python(a4))); } + + template + 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(name), const_cast("(NNNNN)"), to_python(a1), to_python(a2), to_python(a3), to_python(a4), to_python(a5))); } + +}; + +} // namespace py + +#endif // CALLBACK_DWA_052100_H_ diff --git a/caller.h b/caller.h new file mode 100644 index 00000000..7a2e74ca --- /dev/null +++ b/caller.h @@ -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 +# include "signatures.h" +# include "none.h" + +namespace py { + +// Calling C++ from Python +template +struct Caller +{ + template + static PyObject* call(R (T::*pmf)(), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + if (!PyArg_ParseTuple(args, const_cast("O"), &self)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)()); + } + + template + static PyObject* call(R (T::*pmf)(A1), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + if (!PyArg_ParseTuple(args, const_cast("OO"), &self, &a1)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)(from_python(a1, Type()))); + } + + template + static PyObject* call(R (T::*pmf)(A1, A2), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + if (!PyArg_ParseTuple(args, const_cast("OOO"), &self, &a1, &a2)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()))); + } + + template + 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("OOOO"), &self, &a1, &a2, &a3)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()))); + } + + template + 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("OOOOO"), &self, &a1, &a2, &a3, &a4)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type()))); + } + + template + 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("OOOOOO"), &self, &a1, &a2, &a3, &a4, &a5)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type()), + from_python(a5, Type()))); + } + + + template + static PyObject* call(R (T::*pmf)() const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + if (!PyArg_ParseTuple(args, const_cast("O"), &self)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)()); + } + + template + static PyObject* call(R (T::*pmf)(A1) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + if (!PyArg_ParseTuple(args, const_cast("OO"), &self, &a1)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)(from_python(a1, Type()))); + } + + template + 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("OOO"), &self, &a1, &a2)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()))); + } + + template + 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("OOOO"), &self, &a1, &a2, &a3)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()))); + } + + template + 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("OOOOO"), &self, &a1, &a2, &a3, &a4)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type()))); + } + + template + 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("OOOOOO"), &self, &a1, &a2, &a3, &a4, &a5)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type()), + from_python(a5, Type()))); + } + + // Free functions + static PyObject* call(R (*f)(), PyObject* args, PyObject* /* keywords */ ) { + if (!PyArg_ParseTuple(args, const_cast(""))) + return 0; + return to_python(f()); + } + + template + static PyObject* call(R (*f)(A1), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + if (!PyArg_ParseTuple(args, const_cast("O"), &a1)) + return 0; + return to_python(f(from_python(a1, Type()))); + } + + template + static PyObject* call(R (*f)(A1, A2), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + if (!PyArg_ParseTuple(args, const_cast("OO"), &a1, &a2)) + return 0; + return to_python(f(from_python(a1, Type()), + from_python(a2, Type()))); + } + + template + static PyObject* call(R (*f)(A1, A2, A3), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + if (!PyArg_ParseTuple(args, const_cast("OOO"), &a1, &a2, &a3)) + return 0; + return to_python(f(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()))); + } + + template + 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("OOOO"), &a1, &a2, &a3, &a4)) + return 0; + return to_python(f(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type()))); + } + + template + 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("OOOOO"), &a1, &a2, &a3, &a4, &a5)) + return 0; + return to_python(f(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type()), + from_python(a5, Type()))); + } + +}; + +template <> +struct Caller +{ + template + static PyObject* call(void (T::*pmf)(), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + if (!PyArg_ParseTuple(args, const_cast("O"), &self)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(); + return none(); + } + + template + static PyObject* call(void (T::*pmf)(A1), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + if (!PyArg_ParseTuple(args, const_cast("OO"), &self, &a1)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(from_python(a1, Type())); + return none(); + } + + template + static PyObject* call(void (T::*pmf)(A1, A2), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + if (!PyArg_ParseTuple(args, const_cast("OOO"), &self, &a1, &a2)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(from_python(a1, Type()), + from_python(a2, Type())); + return none(); + } + + template + 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("OOOO"), &self, &a1, &a2, &a3)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type())); + return none(); + } + + template + 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("OOOOO"), &self, &a1, &a2, &a3, &a4)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type())); + return none(); + } + + template + 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("OOOOOO"), &self, &a1, &a2, &a3, &a4, &a5)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type()), + from_python(a5, Type())); + return none(); + } + + + template + static PyObject* call(void (T::*pmf)() const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + if (!PyArg_ParseTuple(args, const_cast("O"), &self)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(); + return none(); + } + + template + static PyObject* call(void (T::*pmf)(A1) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + if (!PyArg_ParseTuple(args, const_cast("OO"), &self, &a1)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(from_python(a1, Type())); + return none(); + } + + template + 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("OOO"), &self, &a1, &a2)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(from_python(a1, Type()), + from_python(a2, Type())); + return none(); + } + + template + 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("OOOO"), &self, &a1, &a2, &a3)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type())); + return none(); + } + + template + 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("OOOOO"), &self, &a1, &a2, &a3, &a4)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type())); + return none(); + } + + template + 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("OOOOOO"), &self, &a1, &a2, &a3, &a4, &a5)) + return 0; + T& target = from_python(self, Type()); + (target.*pmf)(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type()), + from_python(a5, Type())); + return none(); + } + + + // Free functions + static PyObject* call(void (*f)(), PyObject* args, PyObject* /* keywords */ ) { + if (!PyArg_ParseTuple(args, const_cast(""))) + return 0; + f(); + return none(); + } + + template + static PyObject* call(void (*f)(A1), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + if (!PyArg_ParseTuple(args, const_cast("O"), &a1)) + return 0; + f(from_python(a1, Type())); + return none(); + } + + template + static PyObject* call(void (*f)(A1, A2), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + if (!PyArg_ParseTuple(args, const_cast("OO"), &a1, &a2)) + return 0; + f(from_python(a1, Type()), + from_python(a2, Type())); + return none(); + } + + template + static PyObject* call(void (*f)(A1, A2, A3), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + if (!PyArg_ParseTuple(args, const_cast("OOO"), &a1, &a2, &a3)) + return 0; + f(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type())); + return none(); + } + + template + 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("OOOO"), &a1, &a2, &a3, &a4)) + return 0; + f(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type())); + return none(); + } + + template + 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("OOOOO"), &a1, &a2, &a3, &a4, &a5)) + return 0; + f(from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type()), + from_python(a5, Type())); + return none(); + } + +}; + +} + +#endif diff --git a/cast.h b/cast.h new file mode 100644 index 00000000..d9f1bd16 --- /dev/null +++ b/cast.h @@ -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 + +namespace py { + +// The default way of converting a PyObject* or PyTypeObject* to a T* +template +struct DowncastTraits +{ + template + static T* cast(U* p) { return static_cast(p); } +}; + +inline PyTypeObject* as_base_object(const PyTypeObject*, PyObject* p) +{ + return reinterpret_cast(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(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(p); } + +// If I didn't have to support stupid MSVC6 we could just use a simple template function: +// template T* downcast(PyObject*). +template +struct Downcast : boost::dereferenceable, T*> +{ + Downcast(PyObject* p) + : m_p(DowncastTraits::cast(as_base_object((T*)0, p))) + {} + + Downcast(const PyObject* p) + : m_p(DowncastTraits::cast(as_base_object((const T*)0, p))) + {} + + Downcast(PyTypeObject* p) + : m_p(DowncastTraits::cast(p)) + {} + + Downcast(const PyTypeObject* p) + : m_p(DowncastTraits::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_ diff --git a/class_wrapper.h b/class_wrapper.h new file mode 100644 index 00000000..f0c342c2 --- /dev/null +++ b/class_wrapper.h @@ -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 ClassWrapper + : PyExtensionClassConverters // Works around MSVC6.x/GCC2.95.2 bug described below +{ + public: + ClassWrapper(Module& module, const char* name) + : m_class(new ExtensionClass(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 + void def(Constructor 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 + 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 + void def(Fn fn, const char* name, DefaultFn default_fn) + { m_class->def(fn, name, default_fn); } + + // Provide a function which implements x., reading from the given + // member (pm) of the T instance + template + void def_getter(MemberType T::*pm, const char* name) + { m_class->def_getter(pm, name); } + + // Provide a function which implements assignment to x., writing to + // the given member (pm) of the T instance + template + 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 + 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 + void def_read_write(MemberType T::*pm, const char* name) + { m_class->def_read_write(pm, name); } + private: + PyPtr > m_class; +#if 0 // def PY_MSVC6_OR_EARLIER + PyExtensionClassConverters 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_ diff --git a/demo.bdf b/demo.bdf new file mode 100644 index 00000000..638c16e5 --- /dev/null +++ b/demo.bdf @@ -0,0 +1,4 @@ +TargetName=demo +TargetType=dll +SourceFiles=extclass_demo.cpp +Libs=py_cpp python utils diff --git a/demo_d.bdf b/demo_d.bdf new file mode 100644 index 00000000..92699e0c --- /dev/null +++ b/demo_d.bdf @@ -0,0 +1,4 @@ +TargetName=demo_d +TargetType=dll +SourceFiles=extclass_demo_d.cpp +Libs=py_cpp_d python_d utils diff --git a/doctest.py b/doctest.py new file mode 100644 index 00000000..892f2ab5 --- /dev/null +++ b/doctest.py @@ -0,0 +1,1112 @@ +# Module doctest version 0.9.4 +# Released to the public domain 27-Mar-1999, +# by Tim Peters (tim_one@email.msn.com). + +# Provided as-is; use at your own risk; no warranty; no promises; enjoy! + +"""Module doctest -- a framework for running examples in docstrings. + +NORMAL USAGE + +In normal use, end each module M with: + +def _test(): + import doctest, M # replace M with your module's name + return doctest.testmod(M) # ditto + +if __name__ == "__main__": + _test() + +Then running the module as a script will cause the examples in the +docstrings to get executed and verified: + +python M.py + +This won't display anything unless an example fails, in which case +the failing example(s) and the cause(s) of the failure(s) are printed +to stdout (why not stderr? because stderr is a lame hack <0.2 wink>), +and the final line of output is "Test failed.". + +Run it with the -v switch instead: + +python M.py -v + +and a detailed report of all examples tried is printed to stdout, along +with assorted summaries at the end. + +You can force verbose mode by passing "verbose=1" to testmod, or prohibit +it by passing "verbose=0". In either of those cases, sys.argv is not +examined by testmod. + +In any case, testmod returns a 2-tuple of ints (f, t), where f is the +number of docstring examples that failed and t is the total number of +docstring examples attempted. + + +WHICH DOCSTRINGS ARE EXAMINED? + ++ M.__doc__. + ++ f.__doc__ for all functions f in M.__dict__.values(), except those + with private names. + ++ C.__doc__ for all classes C in M.__dict__.values(), except those with + private names. + ++ If M.__test__ exists and "is true", it must be a dict, and + each entry maps a (string) name to a function object, class object, or + string. Function and class object docstrings found from M.__test__ + are searched even if the name is private, and strings are searched + directly as if they were docstrings. In output, a key K in M.__test__ + appears with name + .__test__.K + +Any classes found are recursively searched similarly, to test docstrings +in their contained methods and nested classes. Private names reached +from M's globals are skipped, but all names reached from M.__test__ are +searched. + +By default, a name is considered to be private if it begins with an +underscore (like "_my_func") but doesn't both begin and end with (at +least) two underscores (like "__init__"). You can change the default +by passing your own "isprivate" function to testmod. + +If you want to test docstrings in objects with private names too, stuff +them into an M.__test__ dict, or see ADVANCED USAGE below (e.g., pass your +own isprivate function to Tester's constructor, or call the rundoc method +of a Tester instance). + +Warning: imports can cause trouble; e.g., if you do + +from XYZ import XYZclass + +then XYZclass is a name in M.__dict__ too, and doctest has no way to +know that XYZclass wasn't *defined* in M. So it may try to execute the +examples in XYZclass's docstring, and those in turn may require a +different set of globals to work correctly. I prefer to do "import *"- +friendly imports, a la + +import XYY +_XYZclass = XYZ.XYZclass +del XYZ + +and then the leading underscore stops testmod from going nuts. You may +prefer the method in the next section. + + +WHAT'S THE EXECUTION CONTEXT? + +By default, each time testmod finds a docstring to test, it uses a +*copy* of M's globals (so that running tests on a module doesn't change +the module's real globals, and so that one test in M can't leave behind +crumbs that accidentally allow another test to work). This means +examples can freely use any names defined at top-level in M. It also +means that sloppy imports (see above) can cause examples in external +docstrings to use globals inappropriate for them. + +You can force use of your own dict as the execution context by passing +"globs=your_dict" to testmod instead. Presumably this would be a copy +of M.__dict__ merged with the globals from other imported modules. + + +WHAT IF I WANT TO TEST A WHOLE PACKAGE? + +Piece o' cake, provided the modules do their testing from docstrings. +Here's the test.py I use for the world's most elaborate Rational/ +floating-base-conversion pkg (which I'll distribute some day): + +from Rational import Cvt +from Rational import Format +from Rational import machprec +from Rational import Rat +from Rational import Round +from Rational import utils + +modules = (Cvt, + Format, + machprec, + Rat, + Round, + utils) + +def _test(): + import doctest + import sys + verbose = "-v" in sys.argv + for mod in modules: + doctest.testmod(mod, verbose=verbose, report=0) + doctest.master.summarize() + +if __name__ == "__main__": + _test() + +IOW, it just runs testmod on all the pkg modules. testmod remembers the +names and outcomes (# of failures, # of tries) for each item it's seen, +and passing "report=0" prevents it from printing a summary in verbose +mode. Instead, the summary is delayed until all modules have been +tested, and then "doctest.master.summarize()" forces the summary at the +end. + +So this is very nice in practice: each module can be tested individually +with almost no work beyond writing up docstring examples, and collections +of modules can be tested too as a unit with no more work than the above. + + +WHAT ABOUT EXCEPTIONS? + +No problem, as long as the only output generated by the example is the +traceback itself. For example: + + >>> 1/0 + Traceback (innermost last): + File "", line 1, in ? + ZeroDivisionError: integer division or modulo + >>> + +Note that only the exception type and value are compared (specifically, +only the last line in the traceback). + + +ADVANCED USAGE + +doctest.testmod() captures the testing policy I find most useful most +often. You may want other policies. + +testmod() actually creates a local instance of class doctest.Tester, +runs appropriate methods of that class, and merges the results into +global Tester instance doctest.master. + +You can create your own instances of doctest.Tester, and so build your +own policies, or even run methods of doctest.master directly. See +doctest.Tester.__doc__ for details. + + +SO WHAT DOES A DOCSTRING EXAMPLE LOOK LIKE ALREADY!? + +Oh ya. It's easy! In most cases a copy-and-paste of an interactive +console session works fine -- just make sure the leading whitespace +is rigidly consistent (you can mix tabs and spaces if you're too lazy +to do it right, but doctest is not in the business of guessing what +you think a tab means). + + >>> # comments are ignored + >>> x = 12 + >>> x + 12 + >>> if x == 13: + ... print "yes" + ... else: + ... print "no" + ... print "NO" + ... print "NO!!!" + ... + no + NO + NO!!! + >>> + +Any expected output must immediately follow the final ">>>" or "..." +line containing the code, and the expected output (if any) extends +to the next ">>>" or all-whitespace line. That's it. + +Bummers: + ++ Expected output cannot contain an all-whitespace line, since such a + line is taken to signal the end of expected output. + ++ Output to stdout is captured, but not output to stderr (exception + tracebacks are captured via a different means). + ++ If you continue a line via backslashing in an interactive session, + or for any other reason use a backslash, you need to double the + backslash in the docstring version. This is simply because you're + in a string, and so the backslash must be escaped for it to survive + intact. Like: + +>>> if "yes" == \\ +... "y" + \\ +... "es": # in the source code you'll see the doubled backslashes +... print 'yes' +yes + +The starting column doesn't matter: + +>>> assert "Easy!" + >>> import math + >>> math.floor(1.9) + 1.0 + +and as many leading whitespace characters are stripped from the expected +output as appeared in the initial ">>>" line that triggered it. + +If you execute this very file, the examples above will be found and +executed, leading to this output in verbose mode: + +Running doctest.__doc__ +Trying: 1/0 +Expecting: +Traceback (innermost last): + File "", line 1, in ? +ZeroDivisionError: integer division or modulo +ok +Trying: x = 12 +Expecting: nothing +ok +Trying: x +Expecting: 12 +ok +Trying: +if x == 13: + print "yes" +else: + print "no" + print "NO" + print "NO!!!" +Expecting: +no +NO +NO!!! +ok +... and a bunch more like that, with this summary at the end: + +5 items had no tests: + doctest.Tester.__init__ + doctest.Tester.run__test__ + doctest.Tester.summarize + doctest.run_docstring_examples + doctest.testmod +12 items passed all tests: + 8 tests in doctest + 6 tests in doctest.Tester + 10 tests in doctest.Tester.merge + 7 tests in doctest.Tester.rundict + 3 tests in doctest.Tester.rundoc + 3 tests in doctest.Tester.runstring + 2 tests in doctest.__test__._TestClass + 2 tests in doctest.__test__._TestClass.__init__ + 2 tests in doctest.__test__._TestClass.get + 1 tests in doctest.__test__._TestClass.square + 2 tests in doctest.__test__.string + 7 tests in doctest.is_private +53 tests in 17 items. +53 passed and 0 failed. +Test passed. +""" + +# 0,0,1 06-Mar-1999 +# initial version posted +# 0,0,2 06-Mar-1999 +# loosened parsing: +# cater to stinkin' tabs +# don't insist on a blank after PS2 prefix +# so trailing "... " line from a compound stmt no longer +# breaks if the file gets whitespace-trimmed +# better error msgs for inconsistent leading whitespace +# 0,9,1 08-Mar-1999 +# exposed the Tester class and added client methods +# plus docstring examples of their use (eww - head-twisting!) +# fixed logic error in reporting total # of tests & failures +# added __test__ support to testmod (a pale reflection of Christian +# Tismer's vision ...) +# removed the "deep" argument; fiddle __test__ instead +# simplified endcase logic for extracting tests, and running them. +# before, if no output was expected but some was produced +# anyway via an eval'ed result, the discrepancy wasn't caught +# made TestClass private and used __test__ to get at it +# many doc updates +# speed _SpoofOut for long expected outputs +# 0,9,2 09-Mar-1999 +# throw out comments from examples, enabling use of the much simpler +# exec compile(... "single") ... +# for simulating the runtime; that barfs on comment-only lines +# used the traceback module to do a much better job of reporting +# exceptions +# run __doc__ values thru str(), "just in case" +# privateness of names now determined by an overridable "isprivate" +# function +# by default a name now considered to be private iff it begins with +# an underscore but doesn't both begin & end with two of 'em; so +# e.g. Class.__init__ etc are searched now -- as they always +# should have been +# 0,9,3 18-Mar-1999 +# added .flush stub to _SpoofOut (JPython buglet diagnosed by +# Hugh Emberson) +# repaired ridiculous docs about backslashes in examples +# minor internal changes +# changed source to Unix line-end conventions +# moved __test__ logic into new Tester.run__test__ method +# 0,9,4 27-Mar-1999 +# report item name and line # in failing examples +# 0,9,5 29-Jun-1999 +# allow straightforward exceptions in examples - thanks to Mark Hammond! +# 0,9,5,1 31-Mar-2000 +# break cyclic references to functions which are defined in docstrings, +# avoiding cyclic trash +# 0,9,5,2 11-Apr-2000 +# made module argument to testmod optional; it runs testmod on the __main__ +# module in that case. + +__version__ = 0, 9, 5 + +import types +_FunctionType = types.FunctionType +_ClassType = types.ClassType +_ModuleType = types.ModuleType +_StringType = types.StringType +del types + +import string +_string_find = string.find +_string_join = string.join +_string_split = string.split +_string_rindex = string.rindex +del string + +import re +PS1 = ">>>" +PS2 = "..." +_isPS1 = re.compile(r"(\s*)" + re.escape(PS1)).match +_isPS2 = re.compile(r"(\s*)" + re.escape(PS2)).match +_isEmpty = re.compile(r"\s*$").match +_isComment = re.compile(r"\s*#").match +del re + +# Extract interactive examples from a string. Return a list of triples, +# (source, outcome, lineno). "source" is the source code, and ends +# with a newline iff the source spans more than one line. "outcome" is +# the expected output if any, else an empty string. When not empty, +# outcome always ends with a newline. "lineno" is the line number, +# 0-based wrt the start of the string, of the first source line. + +def _extract_examples(s): + isPS1, isPS2 = _isPS1, _isPS2 + isEmpty, isComment = _isEmpty, _isComment + examples = [] + lines = _string_split(s, "\n") + i, n = 0, len(lines) + while i < n: + line = lines[i] + i = i + 1 + m = isPS1(line) + if m is None: + continue + j = m.end(0) # beyond the prompt + if isEmpty(line, j) or isComment(line, j): + # a bare prompt or comment -- not interesting + continue + lineno = i - 1 + if line[j] != " ": + raise ValueError("line " + `lineno` + " of docstring lacks " + "blank after " + PS1 + ": " + line) + j = j + 1 + blanks = m.group(1) + nblanks = len(blanks) + # suck up this and following PS2 lines + source = [] + while 1: + source.append(line[j:]) + line = lines[i] + m = isPS2(line) + if m: + if m.group(1) != blanks: + raise ValueError("inconsistent leading whitespace " + "in line " + `i` + " of docstring: " + line) + i = i + 1 + else: + break + if len(source) == 1: + source = source[0] + else: + # get rid of useless null line from trailing empty "..." + if source[-1] == "": + del source[-1] + source = _string_join(source, "\n") + "\n" + # suck up response + if isPS1(line) or isEmpty(line): + expect = "" + else: + expect = [] + while 1: + if line[:nblanks] != blanks: + raise ValueError("inconsistent leading whitespace " + "in line " + `i` + " of docstring: " + line) + expect.append(line[nblanks:]) + i = i + 1 + line = lines[i] + if isPS1(line) or isEmpty(line): + break + expect = _string_join(expect, "\n") + "\n" + examples.append( (source, expect, lineno) ) + return examples + +# Capture stdout when running examples. + +class _SpoofOut: + def __init__(self): + self.clear() + def write(self, s): + self.buf.append(s) + def get(self): + return _string_join(self.buf, "") + def clear(self): + self.buf = [] + def flush(self): + # JPython calls flush + pass + +# Display some tag-and-msg pairs nicely, keeping the tag and its msg +# on the same line when that makes sense. + +def _tag_out(printer, *tag_msg_pairs): + for tag, msg in tag_msg_pairs: + printer(tag + ":") + msg_has_nl = msg[-1:] == "\n" + msg_has_two_nl = msg_has_nl and \ + _string_find(msg, "\n") < len(msg) - 1 + if len(tag) + len(msg) < 76 and not msg_has_two_nl: + printer(" ") + else: + printer("\n") + printer(msg) + if not msg_has_nl: + printer("\n") + +# Run list of examples, in context globs. "out" can be used to display +# stuff to "the real" stdout, and fakeout is an instance of _SpoofOut +# that captures the examples' std output. Return (#failures, #tries). + +def _run_examples_inner(out, fakeout, examples, globs, verbose, name): + import sys, traceback + OK, BOOM, FAIL = range(3) + NADA = "nothing" + stderr = _SpoofOut() + failures = 0 + for source, want, lineno in examples: + if verbose: + _tag_out(out, ("Trying", source), + ("Expecting", want or NADA)) + fakeout.clear() + try: + exec compile(source, "", "single") in globs + got = fakeout.get() + state = OK + except: + # See whether the exception was expected. + if _string_find(want, "Traceback (innermost last):\n") == 0: + # Only compare exception type and value - the rest of + # the traceback isn't necessary. + want = _string_split(want, '\n')[-2] + '\n' + exc_type, exc_val, exc_tb = sys.exc_info() + got = traceback.format_exception_only(exc_type, exc_val)[0] + state = OK + else: + # unexpected exception + stderr.clear() + traceback.print_exc(file=stderr) + state = BOOM + + if state == OK: + if got == want: + if verbose: + out("ok\n") + continue + state = FAIL + + assert state in (FAIL, BOOM) + failures = failures + 1 + out("*" * 65 + "\n") + _tag_out(out, ("Failure in example", source)) + out("from line #" + `lineno` + " of " + name + "\n") + if state == FAIL: + _tag_out(out, ("Expected", want or NADA), ("Got", got)) + else: + assert state == BOOM + _tag_out(out, ("Exception raised", stderr.get())) + return failures, len(examples) + +# Run list of examples, in context globs. Return (#failures, #tries). + +def _run_examples(examples, globs, verbose, name): + import sys + saveout = sys.stdout + try: + sys.stdout = fakeout = _SpoofOut() + x = _run_examples_inner(saveout.write, fakeout, examples, + globs, verbose, name) + finally: + sys.stdout = saveout + return x + +def run_docstring_examples(f, globs, verbose=0, name="NoName"): + """f, globs, verbose=0, name="NoName" -> run examples from f.__doc__. + + Use dict globs as the globals for execution. + Return (#failures, #tries). + + If optional arg verbose is true, print stuff even if there are no + failures. + Use string name in failure msgs. + """ + + try: + doc = f.__doc__ + if not doc: + # docstring empty or None + return 0, 0 + # just in case CT invents a doc object that has to be forced + # to look like a string <0.9 wink> + doc = str(doc) + except: + return 0, 0 + + e = _extract_examples(doc) + if not e: + return 0, 0 + return _run_examples(e, globs, verbose, name) + +def is_private(prefix, base): + """prefix, base -> true iff name prefix + "." + base is "private". + + Prefix may be an empty string, and base does not contain a period. + Prefix is ignored (although functions you write conforming to this + protocol may make use of it). + Return true iff base begins with an (at least one) underscore, but + does not both begin and end with (at least) two underscores. + + >>> is_private("a.b", "my_func") + 0 + >>> is_private("____", "_my_func") + 1 + >>> is_private("someclass", "__init__") + 0 + >>> is_private("sometypo", "__init_") + 1 + >>> is_private("x.y.z", "_") + 1 + >>> is_private("_x.y.z", "__") + 0 + >>> is_private("", "") # senseless but consistent + 0 + """ + + return base[:1] == "_" and not base[:2] == "__" == base[-2:] + +class Tester: + """Class Tester -- runs docstring examples and accumulates stats. + +In normal use, function doctest.testmod() hides all this from you, +so use that if you can. Create your own instances of Tester to do +fancier things. + +Methods: + runstring(s, name) + Search string s for examples to run; use name for logging. + Return (#failures, #tries). + + rundoc(object, name=None) + Search object.__doc__ for examples to run; use name (or + object.__name__) for logging. Return (#failures, #tries). + + rundict(d, name) + Search for examples in docstrings in all of d.values(); use name + for logging. Return (#failures, #tries). + + run__test__(d, name) + Treat dict d like module.__test__. Return (#failures, #tries). + + summarize(verbose=None) + Display summary of testing results, to stdout. Return + (#failures, #tries). + + merge(other) + Merge in the test results from Tester instance "other". + +>>> from doctest import Tester +>>> t = Tester(globs={'x': 42}, verbose=0) +>>> t.runstring(r''' +... >>> x = x * 2 +... >>> print x +... 42 +... ''', 'XYZ') +***************************************************************** +Failure in example: print x +from line #2 of XYZ +Expected: 42 +Got: 84 +(1, 2) +>>> t.runstring(">>> x = x * 2\\n>>> print x\\n84\\n", 'example2') +(0, 2) +>>> t.summarize() +1 items had failures: + 1 of 2 in XYZ +***Test Failed*** 1 failures. +(1, 4) +>>> t.summarize(verbose=1) +1 items passed all tests: + 2 tests in example2 +1 items had failures: + 1 of 2 in XYZ +4 tests in 2 items. +3 passed and 1 failed. +***Test Failed*** 1 failures. +(1, 4) +>>> +""" + + def __init__(self, mod=None, globs=None, verbose=None, + isprivate=None): + """mod=None, globs=None, verbose=None, isprivate=None + +See doctest.__doc__ for an overview. + +Optional keyword arg "mod" is a module, whose globals are used for +executing examples. If not specified, globs must be specified. + +Optional keyword arg "globs" gives a dict to be used as the globals +when executing examples; if not specified, use the globals from +module mod. + +In either case, a copy of the dict is used for each docstring +examined. + +Optional keyword arg "verbose" prints lots of stuff if true, only +failures if false; by default, it's true iff "-v" is in sys.argv. + +Optional keyword arg "isprivate" specifies a function used to determine +whether a name is private. The default function is doctest.is_private; +see its docs for details. +""" + + if mod is None and globs is None: + raise TypeError("Tester.__init__: must specify mod or globs") + if mod is not None and type(mod) is not _ModuleType: + raise TypeError("Tester.__init__: mod must be a module; " + + `mod`) + if globs is None: + globs = mod.__dict__ + self.globs = globs + + if verbose is None: + import sys + verbose = "-v" in sys.argv + self.verbose = verbose + + if isprivate is None: + isprivate = is_private + self.isprivate = isprivate + + self.name2ft = {} # map name to (#failures, #trials) pair + + def runstring(self, s, name): + """ + s, name -> search string s for examples to run, logging as name. + + Use string name as the key for logging the outcome. + Return (#failures, #examples). + + >>> t = Tester(globs={}, verbose=1) + >>> test = r''' + ... # just an example + ... >>> x = 1 + 2 + ... >>> x + ... 3 + ... ''' + >>> t.runstring(test, "Example") + Running string Example + Trying: x = 1 + 2 + Expecting: nothing + ok + Trying: x + Expecting: 3 + ok + 0 of 2 examples failed in string Example + (0, 2) + """ + + if self.verbose: + print "Running string", name + f = t = 0 + e = _extract_examples(s) + if e: + globs = self.globs.copy() + f, t = _run_examples(e, globs, self.verbose, name) + globs.clear() # DWA - break cyclic references to functions defined in docstrings + if self.verbose: + print f, "of", t, "examples failed in string", name + self.__record_outcome(name, f, t) + return f, t + + def rundoc(self, object, name=None): + """ + object, name=None -> search object.__doc__ for examples to run. + + Use optional string name as the key for logging the outcome; + by default use object.__name__. + Return (#failures, #examples). + If object is a class object, search recursively for method + docstrings too. + object.__doc__ is examined regardless of name, but if object is + a class, whether private names reached from object are searched + depends on the constructor's "isprivate" argument. + + >>> t = Tester(globs={}, verbose=0) + >>> def _f(): + ... '''Trivial docstring example. + ... >>> assert 2 == 2 + ... ''' + ... return 32 + ... + >>> t.rundoc(_f) # expect 0 failures in 1 example + (0, 1) + """ + + if name is None: + try: + name = object.__name__ + except AttributeError: + raise ValueError("Tester.rundoc: name must be given " + "when object.__name__ doesn't exist; " + `object`) + if self.verbose: + print "Running", name + ".__doc__" + globs = self.globs.copy() + f, t = run_docstring_examples(object, globs, + self.verbose, name) + globs.clear() # DWA - break cyclic references to functions defined in docstrings + + if self.verbose: + print f, "of", t, "examples failed in", name + ".__doc__" + self.__record_outcome(name, f, t) + if type(object) is _ClassType: + f2, t2 = self.rundict(object.__dict__, name) + f = f + f2 + t = t + t2 + return f, t + + def rundict(self, d, name): + """ + d. name -> search for docstring examples in all of d.values(). + + For k, v in d.items() such that v is a function or class, + do self.rundoc(v, name + "." + k). Whether this includes + objects with private names depends on the constructor's + "isprivate" argument. + Return aggregate (#failures, #examples). + + >>> def _f(): + ... '''>>> assert 1 == 1 + ... ''' + >>> def g(): + ... '''>>> assert 2 != 1 + ... ''' + >>> d = {"_f": _f, "g": g} + >>> t = Tester(globs={}, verbose=0) + >>> t.rundict(d, "rundict_test") # _f is skipped + (0, 1) + >>> t = Tester(globs={}, verbose=0, isprivate=lambda x,y: 0) + >>> t.rundict(d, "rundict_test_pvt") # both are searched + (0, 2) + """ + + if not hasattr(d, "items"): + raise TypeError("Tester.rundict: d must support .items(); " + + `d`) + f = t = 0 + for thisname, value in d.items(): + if type(value) in (_FunctionType, _ClassType): + f2, t2 = self.__runone(value, name + "." + thisname) + f = f + f2 + t = t + t2 + return f, t + + def run__test__(self, d, name): + """d, name -> Treat dict d like module.__test__. + + Return (#failures, #tries). + See testmod.__doc__ for details. + """ + + failures = tries = 0 + prefix = name + "." + savepvt = self.isprivate + try: + self.isprivate = lambda *args: 0 + for k, v in d.items(): + thisname = prefix + k + if type(v) is _StringType: + f, t = self.runstring(v, thisname) + elif type(v) in (_FunctionType, _ClassType): + f, t = self.rundoc(v, thisname) + else: + raise TypeError("Tester.run__test__: values in " + "dict must be strings, functions " + "or classes; " + `v`) + failures = failures + f + tries = tries + t + finally: + self.isprivate = savepvt + return failures, tries + + def summarize(self, verbose=None): + """ + verbose=None -> summarize results, return (#failures, #tests). + + Print summary of test results to stdout. + Optional arg 'verbose' controls how wordy this is. By + default, use the verbose setting established by the + constructor. + """ + + if verbose is None: + verbose = self.verbose + notests = [] + passed = [] + failed = [] + totalt = totalf = 0 + for x in self.name2ft.items(): + name, (f, t) = x + assert f <= t + totalt = totalt + t + totalf = totalf + f + if t == 0: + notests.append(name) + elif f == 0: + passed.append( (name, t) ) + else: + failed.append(x) + if verbose: + if notests: + print len(notests), "items had no tests:" + notests.sort() + for thing in notests: + print " ", thing + if passed: + print len(passed), "items passed all tests:" + passed.sort() + for thing, count in passed: + print " %3d tests in %s" % (count, thing) + if failed: + print len(failed), "items had failures:" + failed.sort() + for thing, (f, t) in failed: + print " %3d of %3d in %s" % (f, t, thing) + if verbose: + print totalt, "tests in", len(self.name2ft), "items." + print totalt - totalf, "passed and", totalf, "failed." + if totalf: + print "***Test Failed***", totalf, "failures." + elif verbose: + print "Test passed." + return totalf, totalt + + def merge(self, other): + """ + other -> merge in test results from the other Tester instance. + + If self and other both have a test result for something + with the same name, the (#failures, #tests) results are + summed, and a warning is printed to stdout. + + >>> from doctest import Tester + >>> t1 = Tester(globs={}, verbose=0) + >>> t1.runstring(''' + ... >>> x = 12 + ... >>> print x + ... 12 + ... ''', "t1example") + (0, 2) + >>> + >>> t2 = Tester(globs={}, verbose=0) + >>> t2.runstring(''' + ... >>> x = 13 + ... >>> print x + ... 13 + ... ''', "t2example") + (0, 2) + >>> common = ">>> assert 1 + 2 == 3\\n" + >>> t1.runstring(common, "common") + (0, 1) + >>> t2.runstring(common, "common") + (0, 1) + >>> t1.merge(t2) + *** Tester.merge: 'common' in both testers; summing outcomes. + >>> t1.summarize(1) + 3 items passed all tests: + 2 tests in common + 2 tests in t1example + 2 tests in t2example + 6 tests in 3 items. + 6 passed and 0 failed. + Test passed. + (0, 6) + >>> + """ + + d = self.name2ft + for name, (f, t) in other.name2ft.items(): + if d.has_key(name): + print "*** Tester.merge: '" + name + "' in both" \ + " testers; summing outcomes." + f2, t2 = d[name] + f = f + f2 + t = t + t2 + d[name] = f, t + + def __record_outcome(self, name, f, t): + if self.name2ft.has_key(name): + print "*** Warning: '" + name + "' was tested before;", \ + "summing outcomes." + f2, t2 = self.name2ft[name] + f = f + f2 + t = t + t2 + self.name2ft[name] = f, t + + def __runone(self, target, name): + if "." in name: + i = _string_rindex(name, ".") + prefix, base = name[:i], name[i+1:] + else: + prefix, base = "", base + if self.isprivate(prefix, base): + return 0, 0 + return self.rundoc(target, name) + +master = None + +def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None, + report=1): + """m=None, name=None, globs=None, verbose=None, isprivate=None, report=1 + + Test examples in docstrings in functions and classes reachable from + module m, starting with m.__doc__. Private names are skipped. + + Also test examples reachable from dict m.__test__ if it exists and is + not None. m.__dict__ maps names to functions, classes and strings; + function and class docstrings are tested even if the name is private; + strings are tested directly, as if they were docstrings. + + Return (#failures, #tests). + + See doctest.__doc__ for an overview. + + Optional keyword arg "name" gives the name of the module; by default + use m.__name__. + + Optional keyword arg "globs" gives a dict to be used as the globals + when executing examples; by default, use m.__dict__. A copy of this + dict is actually used for each docstring, so that each docstring's + examples start with a clean slate. + + Optional keyword arg "verbose" prints lots of stuff if true, prints + only failures if false; by default, it's true iff "-v" is in sys.argv. + + Optional keyword arg "isprivate" specifies a function used to + determine whether a name is private. The default function is + doctest.is_private; see its docs for details. + + Optional keyword arg "report" prints a summary at the end when true, + else prints nothing at the end. In verbose mode, the summary is + detailed, else very brief (in fact, empty if all tests passed). + + Advanced tomfoolery: testmod runs methods of a local instance of + class doctest.Tester, then merges the results into (or creates) + global Tester instance doctest.master. Methods of doctest.master + can be called directly too, if you want to do something unusual. + Passing report=0 to testmod is especially useful then, to delay + displaying a summary. Invoke doctest.master.summarize(verbose) + when you're done fiddling. + """ + + global master + + if m is None: + import sys + # DWA - m will still be None if this wasn't invoked from the command + # line, in which case the following TypeError is about as good an error + # as we should expect + m = sys.modules.get('__main__') + + if type(m) is not _ModuleType: + raise TypeError("testmod: module required; " + `m`) + if name is None: + name = m.__name__ + tester = Tester(m, globs=globs, verbose=verbose, isprivate=isprivate) + failures, tries = tester.rundoc(m, name) + f, t = tester.rundict(m.__dict__, name) + failures = failures + f + tries = tries + t + if hasattr(m, "__test__"): + testdict = m.__test__ + if testdict: + if not hasattr(testdict, "items"): + raise TypeError("testmod: module.__test__ must support " + ".items(); " + `testdict`) + f, t = tester.run__test__(testdict, name + ".__test__") + failures = failures + f + tries = tries + t + if report: + tester.summarize() + if master is None: + master = tester + else: + master.merge(tester) + return failures, tries + +class _TestClass: + """ + A pointless class, for sanity-checking of docstring testing. + + Methods: + square() + get() + + >>> _TestClass(13).get() + _TestClass(-12).get() + 1 + >>> hex(_TestClass(13).square().get()) + '0xa9' + """ + + def __init__(self, val): + """val -> _TestClass object with associated value val. + + >>> t = _TestClass(123) + >>> print t.get() + 123 + """ + + self.val = val + + def square(self): + """square() -> square TestClass's associated value + + >>> _TestClass(13).square().get() + 169 + """ + + self.val = self.val ** 2 + return self + + def get(self): + """get() -> return TestClass's associated value. + + >>> x = _TestClass(-42) + >>> print x.get() + -42 + """ + + return self.val + +__test__ = {"_TestClass": _TestClass, + "string": r""" + Example of a string object, searched as-is. + >>> x = 1; y = 2 + >>> x + y, x * y + (3, 2) + """ + } + +def _test(): + import doctest + return doctest.testmod(doctest) + +if __name__ == "__main__": + _test() diff --git a/doxyfile b/doxyfile new file mode 100644 index 00000000..5b7bbd25 --- /dev/null +++ b/doxyfile @@ -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 , where +# is the value of the INPUT_FILTER tag, and 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 = diff --git a/errors.h b/errors.h new file mode 100644 index 00000000..8dee0fd0 --- /dev/null +++ b/errors.h @@ -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 +T* expect_non_null(T* x) +{ + if (x == 0) + throw ErrorAlreadySet(); + return x; +} + +} // namespace py + +#endif // ERRORS_DWA052500_H_ diff --git a/example1.bdf b/example1.bdf new file mode 100644 index 00000000..72fdc159 --- /dev/null +++ b/example1.bdf @@ -0,0 +1,4 @@ +TargetName=example1 +TargetType=dll +SourceFiles=example1.cpp +Libs=py_cpp python utils diff --git a/example1.cpp b/example1.cpp new file mode 100644 index 00000000..11f8f683 --- /dev/null +++ b/example1.cpp @@ -0,0 +1,54 @@ +#include + +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 + +// Python requires an exported function called init 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 world_class(hello, "world"); + + // Add the __init__ function + world_class.def(py::Constructor()); + // 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 +extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) +{ + return 1; +} +#endif // _WIN32 diff --git a/extclass.cpp b/extclass.cpp new file mode 100644 index 00000000..a1734760 --- /dev/null +++ b/extclass.cpp @@ -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 + +namespace py { + +ExtensionInstance* get_extension_instance(PyObject* p) +{ + // The object's type will just be some Class 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(p); +} + +void +ExtensionInstance::add_implementation(std::auto_ptr 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* extension_meta_class() +{ + static MetaClass result; + return &result; +} + +typedef Class 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(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* target_class) +{ + if (instance->ob_type == target_class) + return true; + else + { + return is_subclass( + Downcast >(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* 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* 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* 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( + extension_meta_class(), String(name), Tuple(), Dict()) +{ +} + +void ExtensionClassBase::add_method(Function* method, const char* name) +{ + add_method(PyPtr(method), name); +} + +void ExtensionClassBase::add_method(PyPtr 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* target = (bases().size() == 0) + ? this + : Downcast >(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(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 factory(bool selector) { +// return boost::shared_ptr(selector ? new Base : new Derived); +// } +// +// Normally we would use the same Python ExtensionClass object to represent both +// Base and boost::shared_ptr, 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::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 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 method, const char* name) +{ + if (bases().size() == 0) + { + Class* new_base + = new Class( + 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 setter(setter_); + add_method(setter, (detail::setattr_string() + name + "__").c_str()); +} + +void ExtensionClassBase::add_getter_method(Function* getter_, const char* name) +{ + PyPtr getter(getter_); + add_method(getter, (detail::getattr_string() + name + "__").c_str()); +} + +} // namespace py diff --git a/extclass.h b/extclass.h new file mode 100644 index 00000000..3a3be054 --- /dev/null +++ b/extclass.h @@ -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 +# include "none.h" +# include "objects.h" +# include "functions.h" +# include +# include "init_function.h" +# include +# include + +namespace py { + +// forward declarations +class ExtensionInstance; +template class InstanceHolder; +template class InstanceValueHolder; +template class InstancePtrHolder; + +MetaClass* extension_meta_class(); +ExtensionInstance* get_extension_instance(PyObject* p); +void report_missing_instance_data(ExtensionInstance*, Class*, const std::type_info&); +void report_missing_ptr_data(ExtensionInstance*, Class*, const std::type_info&); +void report_missing_class_object(const std::type_info&); +void report_released_smart_pointer(const std::type_info&); + +template +struct ExtensionClassFromPython +{ +}; + +template +T* check_non_null(T* p) +{ + if (p == 0) + report_released_smart_pointer(typeid(T)); + return p; +} + +template class HeldInstance; + +class ExtensionClassBase : public Class +{ + public: + ExtensionClassBase(const char* name); + protected: + void add_method(PyPtr method, const char* name); + void add_default_method(PyPtr 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 ClassRegistry +{ + public: + static Class* class_object() + { return static_class_object; } + static void register_class(py::Class*); + static void unregister_class(py::Class*); + private: + static py::Class* 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. +template > +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 result(create_instance(false)); + result->add_implementation( + std::auto_ptr( + new py::InstanceValueHolder(result.get(), x))); + return result.release(); + } +#else + friend py::Type py_holder_type(const T&) + { return py::Type(); } +#endif + + PyExtensionClassConverters() {} + + // Convert to T* + friend T* from_python(PyObject* obj, py::Type) + { + // Downcast to an ExtensionInstance, then find the actual T + py::ExtensionInstance* self = py::get_extension_instance(obj); + typedef std::vector::const_iterator Iterator; + for (Iterator p = self->wrapped_objects().begin(); + p != self->wrapped_objects().end(); ++p) + { + py::InstanceHolder* held = dynamic_cast*>(*p); + if (held != 0) + return held->target(); + } + py::report_missing_instance_data(self, py::ClassRegistry::class_object(), typeid(T)); + throw py::ArgumentError(); + } + + // Convert to PtrType, where PtrType can be dereferenced to obtain a T. + template + static PtrType& ptr_from_python(PyObject* obj, py::Type) + { + // Downcast to an ExtensionInstance, then find the actual T + py::ExtensionInstance* self = py::get_extension_instance(obj); + typedef std::vector::const_iterator Iterator; + for (Iterator p = self->wrapped_objects().begin(); + p != self->wrapped_objects().end(); ++p) + { + py::InstancePtrHolder* held = + dynamic_cast*>(*p); + if (held != 0) + return held->ptr(); + } + py::report_missing_ptr_data(self, py::ClassRegistry::class_object(), typeid(T)); + throw py::ArgumentError(); + } + + template + static PyObject* ptr_to_python(PtrType x) + { + py::PyPtr result(create_instance(true)); + result->add_implementation( + std::auto_ptr( + new py::InstancePtrHolder(x))); + return result.release(); + } + + static py::PyPtr create_instance(bool seek_base) + { + if (py::ClassRegistry::class_object() == 0) + py::report_missing_class_object(typeid(T)); + + py::Class* class_ + = seek_base && py::ClassRegistry::class_object()->bases().size() > 0 + ? py::Downcast >( + py::ClassRegistry::class_object()->bases()[0].get()).get() + : py::ClassRegistry::class_object(); + + return py::PyPtr(new py::ExtensionInstance(class_)); + } + + + // Convert to const T* + friend const T* from_python(PyObject* p, py::Type) + { return from_python(p, py::Type()); } + + // Convert to T& + friend T& from_python(PyObject* p, py::Type) + { return *py::check_non_null(from_python(p, py::Type())); } + + // Convert to const T& + friend const T& from_python(PyObject* p, py::Type) + { return from_python(p, py::Type()); } + + // Convert to T + friend const T& from_python(PyObject* p, py::Type) + { return from_python(p, py::Type()); } + + friend std::auto_ptr& from_python(PyObject* p, py::Type&>) + { return ptr_from_python(p, py::Type >()); } + + friend std::auto_ptr& from_python(PyObject* p, py::Type >) + { return ptr_from_python(p, py::Type >()); } + + friend const std::auto_ptr& from_python(PyObject* p, py::Type&>) + { return ptr_from_python(p, py::Type >()); } + + friend PyObject* to_python(std::auto_ptr x) + { return ptr_to_python(x); } + + friend boost::shared_ptr& from_python(PyObject* p, py::Type&>) + { return ptr_from_python(p, py::Type >()); } + + friend boost::shared_ptr& from_python(PyObject* p, py::Type >) + { return ptr_from_python(p, py::Type >()); } + + friend const boost::shared_ptr& from_python(PyObject* p, py::Type&>) + { return ptr_from_python(p, py::Type >()); } + + friend PyObject* to_python(boost::shared_ptr x) + { return ptr_to_python(x); } +}; + +#ifndef BOOST_MSVC +template +py::InstanceHolderBase* +py_copy_to_new_value_holder(py::ExtensionInstance* p, const T& x, py::Type) +{ + return new py::InstanceValueHolder(p, x); +} + +template +PyObject* to_python(const T& x) +{ + py::PyPtr result( + PyExtensionClassConverters::create_instance(false)); + result->add_implementation( + std::auto_ptr( + 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 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 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 ExtensionClass + : public PyExtensionClassConverters, // 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 + void def(Constructor) + { add_constructor( // the following incantation builds a Signature1, + prepend(Type::Id(), // Signature2, ... constructor. It _should_ all + prepend(Type::Id(), // get optimized away. Just another workaround + prepend(Type::Id(), // for the lack of partial specialization in MSVC + prepend(Type::Id(), + prepend(Type::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 + 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 + 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., reading from the given + // member (pm) of the T instance + template + void def_getter(MemberType T::*pm, const char* name) + { + this->add_getter_method(new GetterFunction(pm), name); + } + + // Provide a function which implements assignment to x., writing to + // the given member (pm) of the T instance + template + void def_setter(MemberType T::*pm, const char* name) + { + this->add_setter_method(new SetterFunction(pm), name); + } + + // Expose the given member (pm) of the T instance as a read-only attribute + template + 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 + void def_read_write(MemberType T::*pm, const char* name) + { + this->def_getter(pm, name); + this->def_setter(pm, name); + } + + private: + typedef InstanceValueHolder Holder; + + template + void add_constructor(Signature sig) + { + this->add_constructor_object(InitFunction::create(sig)); + } +}; + +#include "extclass_pygen.h" + +template +class InstancePtrHolder : public InstanceHolder +{ + 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 holder); + + typedef std::vector WrappedObjects; + const WrappedObjects& wrapped_objects() const + { return m_wrapped_objects; } + private: + WrappedObjects m_wrapped_objects; +}; + +// +// Template function implementations +// + +template +ExtensionClass::ExtensionClass() + : ExtensionClassBase(typeid(T).name()) +{ + ClassRegistry::register_class(this); +} + +template +ExtensionClass::ExtensionClass(const char* name) + : ExtensionClassBase(name) +{ + ClassRegistry::register_class(this); +} + +template +ExtensionClass::~ExtensionClass() +{ + ClassRegistry::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* ClassRegistry::static_class_object; + +template +inline void ClassRegistry::register_class(Class* p) +{ + // You're not expected to create more than one of these! + assert(static_class_object == 0); + static_class_object = p; +} + +template +inline void ClassRegistry::unregister_class(Class* 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_ diff --git a/extclass_d.cpp b/extclass_d.cpp new file mode 100644 index 00000000..6963607e --- /dev/null +++ b/extclass_d.cpp @@ -0,0 +1,3 @@ +#define DEBUG_PYTHON +#include "extclass.cpp" + diff --git a/extclass_demo.cpp b/extclass_demo.cpp new file mode 100644 index 00000000..dd6c13f5 --- /dev/null +++ b/extclass_demo.cpp @@ -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 // 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::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::call_method(m_self, "pure"); +} + +// The initializer for ExtensionClass 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") // optional +{ + def(py::Constructor()); + 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") // optional +{ + def(py::Constructor()); + def(&Bar::first, "first"); + def(&Bar::second, "second"); + def(&Bar::pass_baz, "pass_baz"); +} + +BazPythonClass::BazPythonClass() + : py::ExtensionClass("Baz") // optional +{ + def(py::Constructor()); + 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") +{ + def(py::Constructor()); + 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") +{ + def(py::Constructor()); + 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 ::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(m_x); +} + +boost::shared_ptr Baz::create_foo() +{ + return boost::shared_ptr(new DerivedFromFoo(0)); +} + +// We can accept smart pointer parameters +int Baz::get_foo_value(boost::shared_ptr 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->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 string_pair(m, "StringPair"); + string_pair.def(py::Constructor()); + 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(m, "Range"); + range.def(py::Constructor()); + range.def(py::Constructor()); + 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); +} + +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") +{ + def(py::Constructor()); + def(&CompareIntPair::operator(), "__call__"); +} + +} // namespace extclass_demo + + +#if defined(_WIN32) +# include +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 diff --git a/extclass_demo.h b/extclass_demo.h new file mode 100644 index 00000000..f4cd6a88 --- /dev/null +++ b/extclass_demo.h @@ -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 +# include +# include +# include +# include +# include + +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 clone() { return std::auto_ptr(new Baz(*this)); } + + // This illustrates creating a polymorphic derived class of Foo + virtual boost::shared_ptr create_foo(); + + // We can accept smart pointer parameters + virtual int get_foo_value(boost::shared_ptr); + + // Show what happens in python when we take ownership from an auto_ptr + virtual void eat_baz(std::auto_ptr); +}; + +typedef std::map StringMap; +typedef std::pair IntPair; + +IntPair make_pair(int, int); + +typedef std::less CompareIntPair; +typedef std::pair 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 { 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 { BarPythonClass(); }; +struct BazPythonClass : py::ExtensionClass { BazPythonClass(); }; + +struct StringMapPythonClass + : py::ExtensionClass +{ + 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 +{ + 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 +{ + CompareIntPairPythonClass(); +}; + +} // namespace extclass_demo + +#endif // EXTCLASS_DEMO_DWA052200_H_ diff --git a/extclass_demo.py b/extclass_demo.py new file mode 100644 index 00000000..df882bed --- /dev/null +++ b/extclass_demo.py @@ -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) + diff --git a/extclass_demo_d.cpp b/extclass_demo_d.cpp new file mode 100644 index 00000000..d0201cda --- /dev/null +++ b/extclass_demo_d.cpp @@ -0,0 +1,2 @@ +#define DEBUG_PYTHON +#include "extclass_demo.cpp" diff --git a/extclass_pygen.h b/extclass_pygen.h new file mode 100644 index 00000000..a63fe12d --- /dev/null +++ b/extclass_pygen.h @@ -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 with a +// single template parameter only. See ExtensionClass, above. +template +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 + HeldInstance(PyObject* p, const A1& a1) : T(a1), m_self(p) {} + template + HeldInstance(PyObject* p, const A1& a1, const A2& a2) : T(a1, a2), m_self(p) {} + template + HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3) : T(a1, a2, a3), m_self(p) {} + template + HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4) : T(a1, a2, a3, a4), m_self(p) {} + template + 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 InstanceHolder : public InstanceHolderBase +{ +public: + virtual Held *target() = 0; +}; + +template +class InstanceValueHolder : public InstanceHolder +{ +public: + Held* target() { return &m_held; } + Wrapper* value_target() { return &m_held; } + + InstanceValueHolder(ExtensionInstance* p) : + m_held(p) {} + template + InstanceValueHolder(ExtensionInstance* p, const A1& a1) : + m_held(p, a1) {} + template + InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2) : + m_held(p, a1, a2) {} + template + InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3) : + m_held(p, a1, a2, a3) {} + template + InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4) : + m_held(p, a1, a2, a3, a4) {} + template + 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 diff --git a/functions.cpp b/functions.cpp new file mode 100644 index 00000000..7d1f2c8e --- /dev/null +++ b/functions.cpp @@ -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 > > +{ + TypeObject() : SingletonBase(&PyType_Type) {} +}; + + +void Function::add_to_namespace(PyPtr 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(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 > > +{ + TypeObject() : SingletonBase(&PyType_Type) {} + +private: // TypeObject 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; + +} diff --git a/functions.h b/functions.h new file mode 100644 index 00000000..7a2e2b66 --- /dev/null +++ b/functions.h @@ -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 +# include "objects.h" +# include "base_object.h" +# include + +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 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 m_overloads; +}; + +template +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::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, where X is the actual return type of pmf. +template +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(pmf); +} + +// Create and return a new member function object wrapping the given +// pointer-to-member function +template +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 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 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 +PyObject* GetterFunction::do_call( + PyObject* args, PyObject* /* keywords */) const +{ + PyObject* self; + if (!PyArg_ParseTuple(args, const_cast("O"), &self)) + return 0; + + return to_python( + from_python(self, Type())->*m_pm); +} + +template +PyObject* SetterFunction::do_call( + PyObject* args, PyObject* /* keywords */) const +{ + PyObject* self; + PyObject* value; + if (!PyArg_ParseTuple(args, const_cast("OO"), &self, &value)) + return 0; + + typedef typename boost::call_traits::const_reference ExtractType; + from_python(self, Type())->*m_pm + = from_python(value, Type()); + + return none(); +} + +} + +#endif // FUNCTIONS_DWA051400_H_ diff --git a/functions_d.cpp b/functions_d.cpp new file mode 100644 index 00000000..2522b504 --- /dev/null +++ b/functions_d.cpp @@ -0,0 +1,2 @@ +#define DEBUG_PYTHON +#include "functions.cpp" diff --git a/gcc.mak b/gcc.mak new file mode 100644 index 00000000..00cea9e5 --- /dev/null +++ b/gcc.mak @@ -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 diff --git a/gen_all.py b/gen_all.py new file mode 100644 index 00000000..744e936b --- /dev/null +++ b/gen_all.py @@ -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) + + diff --git a/gen_callback.py b/gen_callback.py new file mode 100644 index 00000000..44aa6a60 --- /dev/null +++ b/gen_callback.py @@ -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(name), const_cast("(%(N%))")%(, to_python(a%n)%))%3; } + +''' + non_void = ('R', 'return from_python(expect_non_null(', '), Type())') + 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 +struct Callback +{ +""" % args + + gen_functions(call_method, args, 'R', 'return from_python(expect_non_null(', '), Type())') + + +"""}; + +// This specialization wouldn't be needed, but MSVC6 doesn't correctly allow the following: +// void g(); +// void f() { return g(); } +template <> +struct Callback +{ +""" + + 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) + + diff --git a/gen_caller.py b/gen_caller.py new file mode 100644 index 00000000..1f81b091 --- /dev/null +++ b/gen_caller.py @@ -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 +# include "signatures.h" +# include "none.h" + +namespace py { + +// Calling C++ from Python +template +struct Caller +{ +''', +''' +''', +''' // Free functions +''', +'''}; + +template <> +struct Caller +{ +''', +''' +''', +''' + // Free functions +''', +'''}; + +} + +#endif +''') + +#' + +member_function = ''' template + static PyObject* call(%1 (T::*pmf)(%(A%n%:, %))%2, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; +%( PyObject* a%n; +%) if (!PyArg_ParseTuple(args, const_cast("O%(O%)"), &self%(, &a%n%))) + return 0; + T& target = from_python(self, Type()); + %3(target.*pmf)(%(from_python(a%n, Type())%:, + %))%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("%(O%)")%(, &a%n%))) + return 0; + %2f(%(from_python(a%n, Type())%:, + %))%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) + + diff --git a/gen_extclass.py b/gen_extclass.py new file mode 100644 index 00000000..04c2c555 --- /dev/null +++ b/gen_extclass.py @@ -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 with a +// single template parameter only. See ExtensionClass, above. +template +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 InstanceHolder : public InstanceHolderBase +{ +public: + virtual Held *target() = 0; +}; + +template +class InstanceValueHolder : public InstanceHolder +{ +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) diff --git a/gen_function.py b/gen_function.py new file mode 100644 index 00000000..b2b22ab8 --- /dev/null +++ b/gen_function.py @@ -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 + ... static PyObject* call( %1(T::*pmf)(%(A%n%:, %))%2, PyObject* args, PyObject* /* keywords */ ) { + ... PyObject* self; + ... %( PyObject* a%n; + ... %) if (!PyArg_ParseTuple(args, const_cast("O%(O%)"), &self%(, &a%n%))) + ... return 0; + ... T& target = from_python(self, Type()); + ... %3to_python((target.*pmf)(%( + ... from_python(a%n, Type())%:,%) + ... ));%4 + ... }''' + + >>> print gen_function(template, 0, 'R ', '', 'return ', '') + template + static PyObject* call( R (T::*pmf)(), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + if (!PyArg_ParseTuple(args, const_cast("O"), &self)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)( + )); + } + + >>> print gen_function(template, 2, 'R ', '', 'return ', '') + template + static PyObject* call( R (T::*pmf)(A1, A2), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + if (!PyArg_ParseTuple(args, const_cast("OOO"), &self, &a1, &a2)) + return 0; + T& target = from_python(self, Type()); + return to_python((target.*pmf)( + from_python(a1, Type()), + from_python(a2, Type()) + )); + } + + >>> print gen_function(template, 3, 'void ', ' const', '', '\n'+8*' ' + 'return none();') + template + 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("OOOO"), &self, &a1, &a2, &a3)) + return 0; + T& target = from_python(self, Type()); + to_python((target.*pmf)( + from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()) + )); + 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() diff --git a/gen_init_function.py b/gen_init_function.py new file mode 100644 index 00000000..3dd66aa9 --- /dev/null +++ b/gen_init_function.py @@ -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 + +namespace py { + +class ExtensionInstance; +class InstanceHolderBase; + +class Init; +""" + + gen_functions('template struct Init%x;\n', args) + + """ +template +struct InitFunction +{ +""" + gen_functions("""%{ + template <%(class A%n%:, %)> +%} static Init* create(Signature%x%{<%(A%n%:, %)>%}) + { return new Init%x; } +""", 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 +struct Init%x : Init +{ + virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* args, PyObject* /*keywords*/) const + { + %(PyObject* a%n; + %)if (!PyArg_ParseTuple(args, const_cast("%(O%)")%(, &a%n%))) + throw ArgumentError(); + return new T(self%(, + from_python(a%n, Type())%) + ); + } + 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) + diff --git a/gen_signatures.py b/gen_signatures.py new file mode 100644 index 00000000..d9395171 --- /dev/null +++ b/gen_signatures.py @@ -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 +# static inline Signature%1 prepend(Type) +# { return Signature%1(); }""", +# 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 prepend(Type, Signature%x%{<%(T%n%:, %)>%}) + { return Signature%1(); } + +""", 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(), We instead pass +// Type 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::Id is Type, +// but Type::Id is just Void. +template +struct Type +{ + typedef Type Id; +}; + +template <> +struct Type +{ + typedef Void Id; +}; + +// These basically encapsulate a chain of types, , used to make the syntax of +// add(Constructor()) 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 +struct ReturnValue { typedef T Type; }; + +// free functions""" + + gen_functions(""" +template +ReturnValue return_value(R (*)(%(A%n%:, %))) { return ReturnValue(); } +""", args) + + + +""" +// TODO(?): handle 'const void' + +// member functions""" + + gen_functions(""" +template +ReturnValue return_value(R (T::*)(%(A%n%:, %))) { return ReturnValue(); } +""", args) + + + gen_functions(""" +template +ReturnValue return_value(R (T::*)(%(A%n%:, %)) const) { return ReturnValue(); } +""", args) + + + """ +} + +#endif +""") + +if __name__ == '__main__': + import sys + + if len(sys.argv) == 1: + args = 5 + else: + args = int(sys.argv[1]) + + print gen_signatures(args) + diff --git a/gen_singleton.py b/gen_singleton.py new file mode 100644 index 00000000..ed2e97d6 --- /dev/null +++ b/gen_singleton.py @@ -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 +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 +Derived* Singleton::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) diff --git a/init_function.cpp b/init_function.cpp new file mode 100644 index 00000000..33add6ce --- /dev/null +++ b/init_function.cpp @@ -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 + +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(args[0].get()); + + Tuple ctor_args = args.slice(1, args.size()); + + std::auto_ptr result( + create_holder(self, ctor_args.get(), keywords)); + + self->add_implementation(result); + return none(); +} + +} diff --git a/init_function.h b/init_function.h new file mode 100644 index 00000000..c22c627f --- /dev/null +++ b/init_function.h @@ -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 + +namespace py { + +class ExtensionInstance; +class InstanceHolderBase; + +class Init; +template struct Init0; +template struct Init1; +template struct Init2; +template struct Init3; +template struct Init4; +template struct Init5; + +template +struct InitFunction +{ + static Init* create(Signature0) + { return new Init0; } + + template + static Init* create(Signature1) + { return new Init1; } + + template + static Init* create(Signature2) + { return new Init2; } + + template + static Init* create(Signature3) + { return new Init3; } + + template + static Init* create(Signature4) + { return new Init4; } + + template + static Init* create(Signature5) + { return new Init5; } +}; + +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 +struct Init0 : Init +{ + virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* args, PyObject* /*keywords*/) const + { + if (!PyArg_ParseTuple(args, const_cast(""))) + throw ArgumentError(); + return new T(self + ); + } + const char* description() const + { return typeid(void (*)()).name(); } +}; + +template +struct Init1 : Init +{ + virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* args, PyObject* /*keywords*/) const + { + PyObject* a1; + if (!PyArg_ParseTuple(args, const_cast("O"), &a1)) + throw ArgumentError(); + return new T(self, + from_python(a1, Type()) + ); + } + const char* description() const + { return typeid(void (*)(A1)).name(); } +}; + +template +struct Init2 : Init +{ + virtual InstanceHolderBase* create_holder(ExtensionInstance* self, PyObject* args, PyObject* /*keywords*/) const + { + PyObject* a1; + PyObject* a2; + if (!PyArg_ParseTuple(args, const_cast("OO"), &a1, &a2)) + throw ArgumentError(); + return new T(self, + from_python(a1, Type()), + from_python(a2, Type()) + ); + } + const char* description() const + { return typeid(void (*)(A1, A2)).name(); } +}; + +template +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("OOO"), &a1, &a2, &a3)) + throw ArgumentError(); + return new T(self, + from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()) + ); + } + const char* description() const + { return typeid(void (*)(A1, A2, A3)).name(); } +}; + +template +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("OOOO"), &a1, &a2, &a3, &a4)) + throw ArgumentError(); + return new T(self, + from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type()) + ); + } + const char* description() const + { return typeid(void (*)(A1, A2, A3, A4)).name(); } +}; + +template +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("OOOOO"), &a1, &a2, &a3, &a4, &a5)) + throw ArgumentError(); + return new T(self, + from_python(a1, Type()), + from_python(a2, Type()), + from_python(a3, Type()), + from_python(a4, Type()), + from_python(a5, Type()) + ); + } + const char* description() const + { return typeid(void (*)(A1, A2, A3, A4, A5)).name(); } +}; + +} + +#endif // INIT_FUNCTION_DWA052000_H_ diff --git a/init_function_d.cpp b/init_function_d.cpp new file mode 100644 index 00000000..fc40d2d2 --- /dev/null +++ b/init_function_d.cpp @@ -0,0 +1,2 @@ +#define DEBUG_PYTHON +#include "init_function.cpp" diff --git a/makefile b/makefile new file mode 100644 index 00000000..f86b11cf --- /dev/null +++ b/makefile @@ -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 \ No newline at end of file diff --git a/module.cpp b/module.cpp new file mode 100644 index 00000000..d63c9cf6 --- /dev/null +++ b/module.cpp @@ -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(name), initial_methods)) +{ +} + +void +Module::add(Function* x, const char* name) +{ + PyPtr 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(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 } }; + +} diff --git a/module.h b/module.h new file mode 100644 index 00000000..29b98fdf --- /dev/null +++ b/module.h @@ -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 + void def(Fn fn, const char* name) + { + add(new_wrapped_function(fn), name); + } + private: + PyObject* m_module; + static PyMethodDef initial_methods[1]; +}; + +} + +#endif diff --git a/module_d.cpp b/module_d.cpp new file mode 100644 index 00000000..a57ebca0 --- /dev/null +++ b/module_d.cpp @@ -0,0 +1,2 @@ +#define DEBUG_PYTHON +#include "module.cpp" diff --git a/newtypes.cpp b/newtypes.cpp new file mode 100644 index 00000000..3c169e9c --- /dev/null +++ b/newtypes.cpp @@ -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 +#include +#include +#include +#include "objects.h" + +namespace py { + +namespace detail { +UniquePodSet& UniquePodSet::instance() +{ + static UniquePodSet me; + return me; +} + +struct UniquePodSet::Compare +{ + bool operator()(const std::pair& x1, + const std::pair& 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(buffer), + static_cast(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(p->first); + } +} +} // namespace detail + +template +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(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(instance->ob_type) + ->instance_repr(instance); + } + catch(...) + { + handle_exception(); + return 0; + } +} + +static int do_instance_compare(PyObject* instance, PyObject* other) +{ + try + { + return static_cast(instance->ob_type) + ->instance_compare(instance, other); + } + catch(...) + { + handle_exception(); + return -1; + } +} + +static PyObject* do_instance_str(PyObject* instance) +{ + try + { + return static_cast(instance->ob_type) + ->instance_str(instance); + } + catch(...) + { + handle_exception(); + return 0; + } +} + +static long do_instance_hash(PyObject* instance) +{ + try + { + return static_cast(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(instance->ob_type) + ->instance_call(instance, args, keywords); + } + catch(...) + { + handle_exception(); + return 0; + } +} + +static void do_instance_dealloc(PyObject* instance) +{ + try + { + static_cast(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(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(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(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(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(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(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(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(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(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(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(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(instance->ob_type) + ->instance_sequence_ass_slice(instance, start, finish, value); + } + catch(...) + { + handle_exception(); + return -1; + } +} + +} + +namespace detail { +template 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::type, field), \ + Dispatch(do_instance_##field), \ + sizeof(category_type::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( + reinterpret_cast(src_) + c.offset1) : 0; + + char** const dest = reinterpret_cast( + reinterpret_cast(dest_) + c.offset1); + + if (c.substructure_size == 0) + { + if (src == 0 || +#if defined(__MWERKS__) && __MWERKS__ <= 0x4000 + ((const Dispatch*)src) +#else + reinterpret_cast(src) +#endif + != 0) { + *reinterpret_cast(dest) = c.dispatch; + } + } + else + { + if (src == 0 || + *src != 0 && *reinterpret_cast(*src + c.offset2) != 0) + { + *dest = reinterpret_cast(&all_methods) + c.allmethods_offset; + *reinterpret_cast(*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(me); + + if (c.substructure_size == 0) + { + // Stuff the dispatch function directly into the PyTypeObject + *reinterpret_cast(base_address + c.offset1) = c.dispatch; + return; + } + + const char*& sub_structure = *reinterpret_cast(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(reinterpret_cast(&sub) + c.offset2) = c.dispatch; + + // Retrieve the unique dynamically-allocated substructure and stuff it into + // the PyTypeObject. + sub_structure = static_cast( + 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() +{ +} + +} diff --git a/newtypes.h b/newtypes.h new file mode 100644 index 00000000..94382232 --- /dev/null +++ b/newtypes.h @@ -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 > > +// { +// 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 +# include + +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 TypeObject : public TypeObjectBase +{ + public: + typedef T Instance; + + TypeObject(PyTypeObject* type_type, const char* name = 0) + : TypeObjectBase(type_type) + { + this->tp_name = const_cast(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 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 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 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 +void TypeObject::instance_dealloc(PyObject* instance) const +{ + this->dealloc(Downcast(instance).get()); +} + +template +void TypeObject::dealloc(T* instance) const +{ + delete instance; +} + +// Callable +template +Callable::Callable(PyTypeObject* type_type, const char* name) + : Base(type_type, name) +{ + this->enable(call); +} + +template +PyObject* Callable::instance_call(PyObject* instance, PyObject* args, PyObject* kw) const +{ + return Downcast(instance)->call(args, kw); +} + +// Getattrable +template +Getattrable::Getattrable(PyTypeObject* type_type, const char* name) + : Base(type_type, name) +{ + this->enable(getattr); +} + +template +PyObject* Getattrable::instance_getattr(PyObject* instance, const char* name) const +{ + return Downcast(instance)->getattr(name); +} + +// Setattrable +template +Setattrable::Setattrable(PyTypeObject* type_type, const char* name) + : Base(type_type, name) +{ + this->enable(setattr); +} + +template +int Setattrable::instance_setattr(PyObject* instance, const char* name, PyObject* value) const +{ + return Downcast(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 Holder; + typedef std::vector Storage; + public: + static UniquePodSet& instance(); + ~UniquePodSet(); + + template + T* get(const T& x) + { + char* base = const_cast( + reinterpret_cast(&x)); + return const_cast( + static_cast( + 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 + inline void countof_validate(T* const, T* const*); + + template + inline int countof_validate(const void*, T); +} + +} + +#endif // TYPES_DWA051800_H_ diff --git a/newtypes_d.cpp b/newtypes_d.cpp new file mode 100644 index 00000000..12318b83 --- /dev/null +++ b/newtypes_d.cpp @@ -0,0 +1,2 @@ +#define DEBUG_PYTHON +#include "newtypes.cpp" diff --git a/none.h b/none.h new file mode 100644 index 00000000..10232958 --- /dev/null +++ b/none.h @@ -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_ diff --git a/objects.cpp b/objects.cpp new file mode 100644 index 00000000..63c7a52e --- /dev/null +++ b/objects.cpp @@ -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 +T object_from_python(PyObject* p, Type) +{ + 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 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 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 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 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(pos)), + Ptr::new_ref); +} + +void Tuple::set_item(std::size_t pos, const Ptr& rhs) +{ + int failed = PyTuple_SetItem( + get(), static_cast(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(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(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) +{ +} + +} diff --git a/objects.h b/objects.h new file mode 100644 index 00000000..c2d1eabe --- /dev/null +++ b/objects.h @@ -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); + +inline py::Tuple from_python(PyObject* p, py::Type) +{ + return from_python(p, py::Type()); +} + +PyObject* to_python(const py::List&); +py::List from_python(PyObject* p, py::Type); + +inline py::List from_python(PyObject* p, py::Type) +{ + return from_python(p, py::Type()); +} + +PyObject* to_python(const py::String&); +py::String from_python(PyObject* p, py::Type); + +inline py::String from_python(PyObject* p, py::Type) +{ + return from_python(p, py::Type()); +} + +PyObject* to_python(const py::Dict&); +py::Dict from_python(PyObject* p, py::Type); + +inline py::Dict from_python(PyObject* p, py::Type) +{ + return from_python(p, py::Type()); +} + +#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE +namespace py { +#endif + +} +#endif diff --git a/objects_d.cpp b/objects_d.cpp new file mode 100644 index 00000000..95e19316 --- /dev/null +++ b/objects_d.cpp @@ -0,0 +1,2 @@ +#define DEBUG_PYTHON +#include "objects.cpp" diff --git a/py.cpp b/py.cpp new file mode 100644 index 00000000..a64e27c5 --- /dev/null +++ b/py.cpp @@ -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 + +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) +{ + // 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) +{ + // 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 +T integer_from_python(PyObject* p, py::Type) +{ + const long long_result = from_python(p, py::Type()); + + try + { + return boost::numeric_cast(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 +PyObject* integer_to_python(T value) +{ + long value_as_long; + + try + { + value_as_long = boost::numeric_cast(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 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 type) +{ + return integer_from_python(p, type); +} + +short from_python(PyObject* p, py::Type type) +{ + return integer_from_python(p, type); +} + +float from_python(PyObject* p, py::Type) +{ + return static_cast(from_python(p, py::Type())); +} + +PyObject* to_python(unsigned short i) +{ + return integer_to_python(i); +} + +unsigned short from_python(PyObject* p, py::Type 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 type) +{ + return integer_from_python(p, type); +} + +void from_python(PyObject* p, py::Type) +{ + 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* 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) +{ + return std::string(from_python(p, py::Type())); +} + +bool from_python(PyObject* p, py::Type) +{ + int value = from_python(p, py::Type()); + if (value == 0) + return false; + return true; +} + +#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE +namespace py { +#endif + +} + diff --git a/py.h b/py.h new file mode 100644 index 00000000..2cf89807 --- /dev/null +++ b/py.h @@ -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 +# include "errors.h" +# include + +namespace py { + +template 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 +inline void decref(T* p) +{ + char* const raw_p = reinterpret_cast(p); + char* const p_base = raw_p - offsetof(PyObject, ob_refcnt); + decref_impl(reinterpret_cast(p_base)); +} + +template +inline void xdecref(T* p) +{ + char* const raw_p = reinterpret_cast(p); + char* const p_base = raw_p - offsetof(PyObject, ob_refcnt); + xdecref_impl(reinterpret_cast(p_base)); +} + +#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE +} +#endif + +// +// Converters +// +PyObject* to_python(long); +long from_python(PyObject* p, py::Type); +long from_python(PyObject* p, py::Type); + +PyObject* to_python(unsigned long); +unsigned long from_python(PyObject* p, py::Type); +unsigned long from_python(PyObject* p, py::Type); + +PyObject* to_python(int); +int from_python(PyObject*, py::Type); +int from_python(PyObject*, py::Type); + +PyObject* to_python(unsigned int); +unsigned int from_python(PyObject*, py::Type); +unsigned int from_python(PyObject*, py::Type); + +PyObject* to_python(short); +short from_python(PyObject*, py::Type); +short from_python(PyObject*, py::Type); + +PyObject* to_python(unsigned short); +unsigned short from_python(PyObject*, py::Type); +unsigned short from_python(PyObject*, py::Type); + +PyObject* to_python(float); +float from_python(PyObject*, py::Type); +float from_python(PyObject*, py::Type); + +PyObject* to_python(double); +double from_python(PyObject*, py::Type); +double from_python(PyObject*, py::Type); + +PyObject* to_python(bool); +bool from_python(PyObject*, py::Type); +bool from_python(PyObject*, py::Type); + +PyObject* to_python(void); +void from_python(PyObject*, py::Type); + +PyObject* to_python(const char* s); +const char* from_python(PyObject*, py::Type); + +PyObject* to_python(const std::string& s); +std::string from_python(PyObject*, py::Type); +std::string from_python(PyObject*, py::Type); + +// For when your C++ function really wants to pass/return a PyObject* +PyObject* to_python(PyObject*); +PyObject* from_python(PyObject*, py::Type); + +// 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 does not +// have to have already seen the invocation of WrappedType. +// + +// 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 which you want to convert from_python. For example, +// +// namespace py { +// #ifdef MUST_SUPPORT_MSVC +// +// MyPtr from_python(PyObject*p, Type >) +// { return smart_ptr_from_python(p, Type >(), Type());} +// } +// +// MyPtr from_python(PyObject*p, Type >) +// { return smart_ptr_from_python(p, Type >(), Type());} +// +// ... // definitions for MyPtr, MyPtr, etc. +// +// #else +// +// // Just once for all MyPtr +// template +// MyPtr from_python(PyObject*p, Type >) +// { +// return smart_ptr_from_python(p, Type >(), Type()); +// } +// +// #endif +// } + +#if !defined(PY_MSVC6_OR_EARLIER) +template +boost::shared_ptr from_python(PyObject*p, py::Type >) +{ + return smart_ptr_from_python(p, py::Type >(), py::Type()); +} +#endif + +#if 0 +template +PyObject* to_python(std::auto_ptr p) +{ + return new py::WrappedPointer, T>(p); +} + +template +PyObject* to_python(boost::shared_ptr p) +{ + return new py::WrappedPointer, 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) +{ + return from_python(p, py::Type()); +} + +inline PyObject* to_python(short x) +{ + return PyInt_FromLong(x); +} + +inline short from_python(PyObject* p, py::Type) +{ + return from_python(p, py::Type()); +} + +inline PyObject* to_python(bool b) +{ + return PyInt_FromLong(b); +} + +inline bool from_python(PyObject* p, py::Type) +{ + return from_python(p, py::Type()); +} + +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) +{ + return from_python(p, py::Type()); +} + +inline PyObject* to_python(PyObject* p) +{ + Py_INCREF(p); + return p; +} + +inline PyObject* from_python(PyObject* p, py::Type) +{ + return p; +} + +inline unsigned int from_python(PyObject* p, py::Type) +{ + return from_python(p, py::Type()); +} + +inline unsigned short from_python(PyObject* p, py::Type) +{ + return from_python(p, py::Type()); +} + +inline unsigned long from_python(PyObject* p, py::Type) +{ + return from_python(p, py::Type()); +} + +inline long from_python(PyObject* p, py::Type) +{ + return from_python(p, py::Type()); +} + + +#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 diff --git a/py_cpp.bdf b/py_cpp.bdf new file mode 100644 index 00000000..32be8441 --- /dev/null +++ b/py_cpp.bdf @@ -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 diff --git a/py_cpp_20001013.zip b/py_cpp_20001013.zip new file mode 100644 index 0000000000000000000000000000000000000000..420d2bc4b7e4098439b30e4fcbc865b8d0682bce GIT binary patch literal 104507 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 literal 0 HcmV?d00001 diff --git a/py_cpp_d.bdf b/py_cpp_d.bdf new file mode 100644 index 00000000..e8598e51 --- /dev/null +++ b/py_cpp_d.bdf @@ -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 diff --git a/py_cpphelp.mak b/py_cpphelp.mak new file mode 100644 index 00000000..f0367ac0 --- /dev/null +++ b/py_cpphelp.mak @@ -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 diff --git a/py_d.cpp b/py_d.cpp new file mode 100644 index 00000000..59a6c210 --- /dev/null +++ b/py_d.cpp @@ -0,0 +1,2 @@ +#define DEBUG_PYTHON +#include "py.cpp" diff --git a/pyconfig.h b/pyconfig.h new file mode 100644 index 00000000..35a6bf1e --- /dev/null +++ b/pyconfig.h @@ -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 +# include + +# 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_ diff --git a/pyptr.h b/pyptr.h new file mode 100644 index 00000000..6c62a03f --- /dev/null +++ b/pyptr.h @@ -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 +# include "wrap_python.h" +# include "cast.h" +# include +# include "signatures.h" +# include "errors.h" + +namespace py { + +template +class PyPtr + : public boost::dereferenceable, T*> // supplies op-> +{ +public: + PyPtr(const PyPtr& rhs) + : m_p(rhs.m_p) + { + Py_XINCREF(object()); + } + +#if !defined(PY_MSVC6_OR_EARLIER) + template + PyPtr(const PyPtr& 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 + explicit PyPtr(T2* x) + : m_p(expect_non_null(x)) {} + + template + PyPtr(T2* x, NewRef) + : m_p(expect_non_null(x)) { Py_INCREF(object()); } + + template + PyPtr(T2* x, AllowNull) + : m_p(x) {} + + template + PyPtr(T2* x, AllowNull, NewRef) + : m_p(x) { Py_XINCREF(object()); } + + template + PyPtr(T2* x, NewRef, AllowNull) + : m_p(x) { Py_XINCREF(object()); } + +#if !defined(PY_MSVC6_OR_EARLIER) + template + PyPtr& operator=(const PyPtr& rhs) + { + Py_XDECREF(object()); + m_p = rhs.m_p; + Py_XINCREF(object()); + return *this; + } +#endif + + PyPtr& operator=(const PyPtr& rhs) + { + Py_XINCREF(static_cast(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 + void reset(T2* x) + { Py_XDECREF(m_p); m_p = expect_non_null(x);} + + template + void reset(T2* x, NewRef) + { Py_XDECREF(m_p); m_p = expect_non_null(x); Py_INCREF(object()); } + + template + void reset(T2* x, AllowNull) + { Py_XDECREF(m_p); m_p = x;} + + template + void reset(T2* x, AllowNull, NewRef) + { Py_XDECREF(m_p); m_p = x; Py_XINCREF(object()); } + + template + 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 friend class shared_ptr; +#endif + + inline PyObject* object() const + { return as_object(m_p); } + + T* m_p; +}; + +typedef PyPtr Ptr; + +} + +#endif // PYPTR_DWA050400_H_ diff --git a/signatures.h b/signatures.h new file mode 100644 index 00000000..08913b77 --- /dev/null +++ b/signatures.h @@ -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(), We instead pass +// Type 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::Id is Type, +// but Type::Id is just Void. +template +struct Type +{ + typedef Type Id; +}; + +template <> +struct Type +{ + typedef Void Id; +}; + +// These basically encapsulate a chain of types, , used to make the syntax of +// add(Constructor()) 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 +struct Signature5 {}; + +template +struct Signature4 {}; + +template +inline Signature5 prepend(Type, Signature4) + { return Signature5(); } + +template +struct Signature3 {}; + +template +inline Signature4 prepend(Type, Signature3) + { return Signature4(); } + +template +struct Signature2 {}; + +template +inline Signature3 prepend(Type, Signature2) + { return Signature3(); } + +template +struct Signature1 {}; + +template +inline Signature2 prepend(Type, Signature1) + { return Signature2(); } + +struct Signature0 {}; + +template +inline Signature1 prepend(Type, Signature0) + { return Signature1(); } + + +// 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 +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 +struct ReturnValue { typedef T Type; }; + +// free functions +template +ReturnValue return_value(R (*)()) { return ReturnValue(); } + +template +ReturnValue return_value(R (*)(A1)) { return ReturnValue(); } + +template +ReturnValue return_value(R (*)(A1, A2)) { return ReturnValue(); } + +template +ReturnValue return_value(R (*)(A1, A2, A3)) { return ReturnValue(); } + +template +ReturnValue return_value(R (*)(A1, A2, A3, A4)) { return ReturnValue(); } + +template +ReturnValue return_value(R (*)(A1, A2, A3, A4, A5)) { return ReturnValue(); } + +// TODO(?): handle 'const void' + +// member functions +template +ReturnValue return_value(R (T::*)()) { return ReturnValue(); } + +template +ReturnValue return_value(R (T::*)(A1)) { return ReturnValue(); } + +template +ReturnValue return_value(R (T::*)(A1, A2)) { return ReturnValue(); } + +template +ReturnValue return_value(R (T::*)(A1, A2, A3)) { return ReturnValue(); } + +template +ReturnValue return_value(R (T::*)(A1, A2, A3, A4)) { return ReturnValue(); } + +template +ReturnValue return_value(R (T::*)(A1, A2, A3, A4, A5)) { return ReturnValue(); } + +template +ReturnValue return_value(R (T::*)() const) { return ReturnValue(); } + +template +ReturnValue return_value(R (T::*)(A1) const) { return ReturnValue(); } + +template +ReturnValue return_value(R (T::*)(A1, A2) const) { return ReturnValue(); } + +template +ReturnValue return_value(R (T::*)(A1, A2, A3) const) { return ReturnValue(); } + +template +ReturnValue return_value(R (T::*)(A1, A2, A3, A4) const) { return ReturnValue(); } + +template +ReturnValue return_value(R (T::*)(A1, A2, A3, A4, A5) const) { return ReturnValue(); } + +} + +#endif diff --git a/singleton.h b/singleton.h new file mode 100644 index 00000000..5e510df7 --- /dev/null +++ b/singleton.h @@ -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 +struct Singleton : Base +{ + typedef Singleton SingletonBase; // Convenience type for derived class constructors + + static Derived* singleton(); + + // Pass-through constructors + Singleton() : Base() {} + + template + Singleton(const A1& a1) : Base(a1) {} + + template + Singleton(const A1& a1, const A2& a2) : Base(a1, a2) {} + + template + Singleton(const A1& a1, const A2& a2, const A3& a3) : Base(a1, a2, a3) {} + + template + Singleton(const A1& a1, const A2& a2, const A3& a3, const A4& a4) : Base(a1, a2, a3, a4) {} + + template + Singleton(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) : Base(a1, a2, a3, a4, a5) {} + +}; + +template +Derived* Singleton::singleton() +{ + static Derived x; + return &x; +} + +} + +#endif diff --git a/subclass.cpp b/subclass.cpp new file mode 100644 index 00000000..3f1d25ec --- /dev/null +++ b/subclass.cpp @@ -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 +#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(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 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("(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("__getattr__")); + + // Use both args to PyEval_CallFunction + arg_format = const_cast("(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____(value) or __delattr____(). 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(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(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(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(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::call_method(this, "__repr__"); +} + +int Instance::compare(PyObject* other) +{ + return Callback::call_method(this, "__cmp__", other); +} + +PyObject* Instance::str() +{ + return Callback::call_method(this, "__str__"); +} + +long Instance::hash() +{ + return Callback::call_method(this, "__hash__"); +} + +int Instance::length() +{ + return Callback::call_method(this, "__len__"); +} + +PyObject* Instance::get_subscript(PyObject* key) +{ + return Callback::call_method(this, "__getitem__", key); +} + +void Instance::set_subscript(PyObject* key, PyObject* value) +{ + if (value == 0) + Callback::call_method(this, "__delitem__", key); + else + Callback::call_method(this, "__setitem__", key, value); +} + +PyObject* Instance::get_slice(int start, int finish) +{ + return Callback::call_method(this, "__getslice__", start, finish); +} + +void Instance::set_slice(int start, int finish, PyObject* value) +{ + if (value == 0) + Callback::call_method(this, "__delslice__", start, finish); + else + Callback::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(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____/__setattr____ + // 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::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 diff --git a/subclass.h b/subclass.h new file mode 100644 index 00000000..4d745bc0 --- /dev/null +++ b/subclass.h @@ -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 +# 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 MetaClass; + +// A type which acts a lot like a built-in Python class. T is the instance type, +// so Class is a very simple "class-alike". +template +class Class + : public Getattrable > > +{ + public: + Class(MetaClass* 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&); + 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 object. +template +class MetaClass + : public Callable > > > >, + boost::noncopyable +{ + public: + MetaClass(); + + // Standard Python functions. + PyObject* call(PyObject* args, PyObject* keywords); + private: + + struct TypeObject + : Singleton > > + { + 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 +MetaClass::MetaClass() + : Properties(TypeObject::singleton()) +{ +} + +template +Class::Class(MetaClass* 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 +String Class::name() const +{ + return m_name; +} + +template +PyObject* Class::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(name)); + if (base_attribute != 0) + return base_attribute; + } + return 0; +} + +template +int Class::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(name), value); +} + +template +PyObject* Class::call(PyObject* args, PyObject* keywords) +{ + PyPtr 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 +PyObject* Class::instance_repr(PyObject* instance) const +{ + return Downcast(instance)->repr(); +} + +template +int Class::instance_compare(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->compare(other); +} + +template +PyObject* Class::instance_str(PyObject* instance) const +{ + return Downcast(instance)->str(); +} + +template +long Class::instance_hash(PyObject* instance) const +{ + return Downcast(instance)->hash(); +} + +template +int Class::instance_mapping_length(PyObject* instance) const +{ + return Downcast(instance)->length(); +} + +template +int Class::instance_sequence_length(PyObject* instance) const +{ + return Downcast(instance)->length(); +} + +template +PyObject* Class::instance_mapping_subscript(PyObject* instance, PyObject* key) const +{ + return Downcast(instance)->get_subscript(key); +} + +template +PyObject* Class::instance_sequence_item(PyObject* instance, int n) const +{ + Ptr key(to_python(n)); + return Downcast(instance)->get_subscript(key.get()); +} + +template +int Class::instance_sequence_ass_item(PyObject* instance, int n, PyObject* value) const +{ + Ptr key(to_python(n)); + Downcast(instance)->set_subscript(key.get(), value); + return 0; +} + +template +int Class::instance_mapping_ass_subscript(PyObject* instance, PyObject* key, PyObject* value) const +{ + Downcast(instance)->set_subscript(key, value); + return 0; +} + +void adjust_slice_indices(PyObject* instance, int& start, int& finish); + +template +PyObject* Class::instance_sequence_slice(PyObject* instance, int start, int finish) const +{ + adjust_slice_indices(instance, start, finish); + return Downcast(instance)->get_slice(start, finish); +} + +template +int Class::instance_sequence_ass_slice(PyObject* instance, int start, int finish, PyObject* value) const +{ + adjust_slice_indices(instance, start, finish); + Downcast(instance)->set_slice(start, finish, value); + return 0; +} + +template +PyObject* Class::instance_call(PyObject* instance, PyObject* args, PyObject* keywords) const +{ + return Downcast(instance)->call(args, keywords); +} + +template +Dict& Class::dict() +{ + return m_name_space; +} + +template +Tuple Class::bases() const +{ + return m_bases; +} + +template +void Class::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 +PyObject* MetaClass::call(PyObject* args, PyObject* /*keywords*/) +{ + PyObject* name; + PyObject* bases; + PyObject* name_space; + + if (!PyArg_ParseTuple(args, const_cast("O!O!O!"), + &PyString_Type, &name, + &PyTuple_Type, &bases, + &PyDict_Type, &name_space)) + { + return 0; + } + + return as_object( + new Class(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 diff --git a/subclass_d.cpp b/subclass_d.cpp new file mode 100644 index 00000000..cfd27dc2 --- /dev/null +++ b/subclass_d.cpp @@ -0,0 +1,2 @@ +#define DEBUG_PYTHON +#include "subclass.cpp" diff --git a/test_example1.py b/test_example1.py new file mode 100644 index 00000000..0e3a9a18 --- /dev/null +++ b/test_example1.py @@ -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() diff --git a/test_extclass.py b/test_extclass.py new file mode 100644 index 00000000..1912b237 --- /dev/null +++ b/test_extclass.py @@ -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 "", line 1, in ? + TypeError: function requires exactly 1 argument; 0 given + + >>> ext = Foo('foo') + Traceback (innermost last): + File "", 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 "", 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 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 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__() + >>> m[1] + Traceback (innermost last): + File "", line 1, in ? + KeyError: 1 + +__setitem__() + + >>> m[1] = 'hello' + +__getitem__() + >>> m[1] + 'hello' + +__delitem__() + >>> del m[1] + >>> m[1] # prove that it's gone + Traceback (innermost last): + File "", line 1, in ? + KeyError: 1 + +__delitem__() + >>> del m[2] + Traceback (innermost last): + File "", 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 "", 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 "", line 1, in ? + AttributeError: delete non-existing instance attribute + +Testing __getattr__ and __getattr__: + + >>> n = IntPair(1, 2) + >>> n.first + 1 + >>> n.second + 2 + >>> n.third + Traceback (innermost last): + File "", line 1, in ? + AttributeError: third + +Testing __setattr__ and __setattr__: + >>> 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__: + >>> del n.first + Traceback (innermost last): + File "", line 1, in ? + AttributeError: first can't be deleted! + >>> del n.second + Traceback (innermost last): + File "", line 1, in ? + AttributeError: Attributes can't be deleted! + >>> del n.third + Traceback (innermost last): + File "", 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 "", line 1, in ? + AttributeError: bax + >>> p.third = 'yes' + Traceback (innermost last): + File "", line 1, in ? + AttributeError: no writable attributes + >>> del p.third + Traceback (innermost last): + File "", 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 "", 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('', 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 "", 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() diff --git a/todo.txt b/todo.txt new file mode 100644 index 00000000..d8250858 --- /dev/null +++ b/todo.txt @@ -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 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 ;) diff --git a/vc6_prj/ReadMe.txt b/vc6_prj/ReadMe.txt new file mode 100644 index 00000000..a6b8aea4 --- /dev/null +++ b/vc6_prj/ReadMe.txt @@ -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. + + +///////////////////////////////////////////////////////////////////////////// diff --git a/vc6_prj/StdAfx.cpp b/vc6_prj/StdAfx.cpp new file mode 100644 index 00000000..5a711fb5 --- /dev/null +++ b/vc6_prj/StdAfx.cpp @@ -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 diff --git a/vc6_prj/StdAfx.h b/vc6_prj/StdAfx.h new file mode 100644 index 00000000..9bd983af --- /dev/null +++ b/vc6_prj/StdAfx.h @@ -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 + +// 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_) diff --git a/vc6_prj/test_demo.py b/vc6_prj/test_demo.py new file mode 100644 index 00000000..eb68e4a2 --- /dev/null +++ b/vc6_prj/test_demo.py @@ -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']) + diff --git a/vc6_prj/test_hello.py b/vc6_prj/test_hello.py new file mode 100644 index 00000000..6da3c4bc --- /dev/null +++ b/vc6_prj/test_hello.py @@ -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"]) + diff --git a/vc6_prj/vc6_prj.cpp b/vc6_prj/vc6_prj.cpp new file mode 100644 index 00000000..7d098f47 --- /dev/null +++ b/vc6_prj/vc6_prj.cpp @@ -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; +} + diff --git a/vc6_prj/vc6_prj.dsp b/vc6_prj/vc6_prj.dsp new file mode 100644 index 00000000..1348d8da --- /dev/null +++ b/vc6_prj/vc6_prj.dsp @@ -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 diff --git a/vc6_prj/vc6_prj.dsw b/vc6_prj/vc6_prj.dsw new file mode 100644 index 00000000..0e48beed --- /dev/null +++ b/vc6_prj/vc6_prj.dsw @@ -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> +{{{ +}}} + +############################################################################### + diff --git a/vc6_prj/vc6_prj.opt b/vc6_prj/vc6_prj.opt new file mode 100644 index 0000000000000000000000000000000000000000..fa7645c378df09f9fb320b53392817a8a635c4c8 GIT binary patch literal 64000 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 +# 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 + +#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 +