From 2e5d8dbff7620d618c1e1fccb679452a3501eb15 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sun, 26 Nov 2000 15:49:26 +0000 Subject: [PATCH] This commit was generated by cvs2svn to compensate for changes in r711, which included commits to RCS files with non-trunk default branches. [SVN r8328] --- .gitattributes | 96 ++ doc/#index.html# | 212 +++ doc/building.html | 43 + doc/comparisons.html | 220 +++ doc/enums.html | 94 ++ doc/example1.html | 129 ++ doc/extending.html | 73 + doc/index.html | 211 +++ doc/inheritance.html | 165 +++ doc/overloading.html | 156 ++ doc/overriding.html | 195 +++ doc/pointers.html | 145 ++ doc/special.html | 888 ++++++++++++ doc/template.html | 26 + doc/under-the-hood.html | 62 + example/example1.cpp | 54 + example/example1.py | 50 + include/boost/python/callback.hpp | 829 +++++++++++ include/boost/python/caller.hpp | 1279 +++++++++++++++++ include/boost/python/class_builder.hpp | 156 ++ include/boost/python/classes.hpp | 527 +++++++ include/boost/python/conversions.hpp | 325 +++++ include/boost/python/detail/base_object.hpp | 62 + include/boost/python/detail/cast.hpp | 81 ++ include/boost/python/detail/config.hpp | 56 + .../boost/python/detail/extension_class.hpp | 834 +++++++++++ include/boost/python/detail/functions.hpp | 306 ++++ include/boost/python/detail/init_function.hpp | 507 +++++++ include/boost/python/detail/none.hpp | 21 + include/boost/python/detail/signatures.hpp | 251 ++++ include/boost/python/detail/singleton.hpp | 68 + include/boost/python/detail/types.hpp | 389 +++++ include/boost/python/detail/wrap_python.hpp | 61 + include/boost/python/errors.hpp | 30 + include/boost/python/module_builder.hpp | 53 + include/boost/python/objects.hpp | 334 +++++ include/boost/python/operators.hpp | 504 +++++++ include/boost/python/reference.hpp | 173 +++ src/classes.cpp | 884 ++++++++++++ src/conversions.cpp | 241 ++++ src/extension_class.cpp | 683 +++++++++ src/functions.cpp | 167 +++ src/gen_all.py | 26 + src/gen_callback.py | 124 ++ src/gen_caller.py | 138 ++ src/gen_extclass.py | 830 +++++++++++ src/gen_function.py | 184 +++ src/gen_init_function.py | 166 +++ src/gen_signatures.py | 158 ++ src/gen_singleton.py | 58 + src/init_function.cpp | 36 + src/module_builder.cpp | 52 + src/objects.cpp | 485 +++++++ src/types.cpp | 1097 ++++++++++++++ test/comprehensive.cpp | 1133 +++++++++++++++ test/comprehensive.hpp | 231 +++ test/comprehensive.py | 1087 ++++++++++++++ 57 files changed, 17445 insertions(+) create mode 100644 .gitattributes create mode 100644 doc/#index.html# create mode 100644 doc/building.html create mode 100644 doc/comparisons.html create mode 100644 doc/enums.html create mode 100644 doc/example1.html create mode 100644 doc/extending.html create mode 100644 doc/index.html create mode 100644 doc/inheritance.html create mode 100644 doc/overloading.html create mode 100644 doc/overriding.html create mode 100644 doc/pointers.html create mode 100644 doc/special.html create mode 100644 doc/template.html create mode 100644 doc/under-the-hood.html create mode 100644 example/example1.cpp create mode 100644 example/example1.py create mode 100644 include/boost/python/callback.hpp create mode 100644 include/boost/python/caller.hpp create mode 100644 include/boost/python/class_builder.hpp create mode 100644 include/boost/python/classes.hpp create mode 100644 include/boost/python/conversions.hpp create mode 100644 include/boost/python/detail/base_object.hpp create mode 100644 include/boost/python/detail/cast.hpp create mode 100644 include/boost/python/detail/config.hpp create mode 100644 include/boost/python/detail/extension_class.hpp create mode 100644 include/boost/python/detail/functions.hpp create mode 100644 include/boost/python/detail/init_function.hpp create mode 100644 include/boost/python/detail/none.hpp create mode 100644 include/boost/python/detail/signatures.hpp create mode 100644 include/boost/python/detail/singleton.hpp create mode 100644 include/boost/python/detail/types.hpp create mode 100644 include/boost/python/detail/wrap_python.hpp create mode 100644 include/boost/python/errors.hpp create mode 100644 include/boost/python/module_builder.hpp create mode 100644 include/boost/python/objects.hpp create mode 100644 include/boost/python/operators.hpp create mode 100644 include/boost/python/reference.hpp create mode 100644 src/classes.cpp create mode 100644 src/conversions.cpp create mode 100644 src/extension_class.cpp create mode 100644 src/functions.cpp create mode 100644 src/gen_all.py create mode 100644 src/gen_callback.py create mode 100644 src/gen_caller.py create mode 100644 src/gen_extclass.py create mode 100644 src/gen_function.py create mode 100644 src/gen_init_function.py create mode 100644 src/gen_signatures.py create mode 100644 src/gen_singleton.py create mode 100644 src/init_function.cpp create mode 100644 src/module_builder.cpp create mode 100644 src/objects.cpp create mode 100644 src/types.cpp create mode 100644 test/comprehensive.cpp create mode 100644 test/comprehensive.hpp create mode 100644 test/comprehensive.py 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/doc/#index.html# b/doc/#index.html# new file mode 100644 index 00000000..ff5946a8 --- /dev/null +++ b/doc/#index.html# @@ -0,0 +1,212 @@ + + + + py_cpp Python/C++ binding documentation + +

+ c++boost.gif (8819 bytes) py_cpp* +

+ +

+ The source code for py_cpp, including a MSVC demo project is available here. + +

Synopsis

+

+ py_cpp is a system for quickly and easily interfacing C++ code with Python such that the Python interface is + very similar to the C++ interface. It is designed to be minimally + intrusive on your C++ design. In most cases, you should not have to alter + your C++ classes in any way in order to use them with py_cpp. The system + should simply “reflect” your C++ classes and functions into + Python. The major features of py_cpp include support for: +

+among others. + + +

Supported Platforms

+

py_cpp has been tested in the following configurations: + +

+ +

Py_cpp requires the boost libraries, and is + has been accepted for inclusion into the boost libraries pending “boostification“ + (completion of the documentation, change in some naming conventions and + resolution of some namespace issues). + +

Credits

+ + +

Table of Contents

+ +
    +
  1. A Brief Introduction to writing Python + extension modules + +
  2. Comparisons between py_cpp and other + systems for extending Python + +
  3. A Simple Example Using py_cpp + +
  4. Overridable Virtual Functions + +
  5. Function Overloading + +
  6. Inheritance + +
  7. Special Method and Operator Support + +
  8. A Peek Under the Hood + +
  9. Building a Module with Py_cpp + +
  10. Advanced Topics + +
      +
    1. class_builder<> + +
    2. enums + +
    3. References + +
    4. Pointers and Smart Pointers + +
    5. Built-in Python Types + +
    6. Other Extension Types + +
    7. Templates +
    + +
+ +

+ More sophisticated examples are given in + extclass_demo.cpp, extclass_demo.h, and + test_extclass.py in the source code + archive. There's much more here, and much more documentation to + come... +

+ Questions should be directed to the boost mailing list. + +

Naming Contest

+ +

+ Yes, I know py_cpp is a lousy name. Problem is, the best names my puny + imagination can muster (IDLE and GRAIL) are taken, so I'm holding a + naming contest. First prize? You get to pick the name<0.2wink> and + you will be credited in the documentation. Names that have been suggested + so far include: +

+ Please post or send me your suggestions!
+
+ +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided “as is” without + express or implied warranty, and with no claim as to its suitability for + any purpose. +

+ Updated: Nov 26, 2000 + + diff --git a/doc/building.html b/doc/building.html new file mode 100644 index 00000000..9c0452e7 --- /dev/null +++ b/doc/building.html @@ -0,0 +1,43 @@ + + + Building a Module with Py_cpp + +

+

+ c++boost.gif (8819 bytes)Building a Module with Py_cpp +

+

+ Right now, the only supported configuration is one in which the py_cpp + source files are statically linked with the source for your extension + module. You may first build them into a library and link it with your + extension module source, but the effect is the same as compiling all + the source files together. Some users have successfully built the + py_cpp sources into a shared library, and support for a shared library + build is planned, but not yet implemented. The py_cpp source files are: +

+
+extclass.cpp
+functions.cpp
+init_function.cpp
+module.cpp
+newtypes.cpp
+objects.cpp
+py.cpp
+subclass.cpp
+         
+
+

+ Previous: A Peek Under the Hood + Up: Top +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided “as + is” without express or implied warranty, and with no claim as to + its suitability for any purpose. +

+ Updated: Nov 26, 2000 +

+ diff --git a/doc/comparisons.html b/doc/comparisons.html new file mode 100644 index 00000000..d0c31d9f --- /dev/null +++ b/doc/comparisons.html @@ -0,0 +1,220 @@ + + + Comparisons with Other Systems + +
+

+ c++boost.gif (8819 bytes)Comparisons with + Other Systems +

+ +

CXX

+

+ Like py_cpp, CXX attempts to + provide a C++-oriented interface to Python. In most cases, like py_cpp, + it relieves the user from worrying about reference-counts. As far as I + can tell, there is no support for subclassing C++ extension types in + Python. An even more-significant difference is that a user's C++ code is + still basically “dealing with Python objects”, though they are wrapped + in C++ classes. This means such jobs as argument parsing and conversion + are still left to be done explicitly by the user. + +

+ CXX claims to interoperate well with the C++ Standard Library + (a.k.a. STL) by providing iterators into Python Lists and Dictionaries, + but the claim is unfortunately unsupportable. The problem is that in + general, access to Python sequence and mapping elements through + iterators requires the use of proxy objects as the return value of + iterator dereference operations. This usage conflicts with the basic + ForwardIterator requirements in + section 24.1.3 of the standard (dereferencing must produce a + reference). Although you may be able to use these iterators with some + operations in some standard library implementations, it is neither + guaranteed to work nor portable. + +

+ As far as I can tell, CXX enables one to write what is essentially + idiomatic Python code in C++, manipulating Python objects through the + same fully-generic interfaces we use in Python. I think it would be fair + to say that while you're not programming directly to the “bare + metal” with CXX, in comparison to py_cpp, it presents a low-level + interface to Python. That use is also supported by the py_cpp object + wrappers. + +

+ Paul F. Dubois, the original + author of CXX, has told me that what I've described is only half of the + picture with CXX, but I never understood his explanation well-enough to + fill in the other half. Here is his response to the commentary above: + +

+“My intention with CXX was not to do what you are doing. It was to enable a +person to write an extension directly in C++ rather than C. I figured others had +the wrapping business covered. I thought maybe CXX would provide an easier +target language for those making wrappers, but I never explored +that.”
-Paul Dubois +
+ +

SWIG

+

+ SWIG is an impressively mature tool + for exporting an existing ANSI 'C' interface into various scripting + languages. Swig relies on a parser to read your source code and produce + additional source code files which can be compiled into a Python (or + Perl or Tcl) extension module. It has been successfully used to create + many Python extension modules. Like py_cpp, SWIG is trying to allow an + existing interface to be wrapped with little or no change to the + existing code. The documentation says “SWIG parses a form of ANSI C + syntax that has been extended with a number of special directives. As a + result, interfaces are usually built by grabbing a header file and + tweaking it a little bit.” For C++ interfaces, the tweaking has often + proven to amount to more than just a little bit. One user + writes: + +

“The problem with swig (when I used it) is that it + couldnt handle templates, didnt do func overloading properly etc. For + ANSI C libraries this was fine. But for usual C++ code this was a + problem. Simple things work. But for anything very complicated (or + realistic), one had to write code by hand. I believe py_cpp doesn't have + this problem[sic]... IMHO overloaded functions are very important to + wrap correctly.”
-Prabhu Ramachandran +
+ +

+ By contrast, py_cpp doesn't attempt to parse C++ - the problem is simply + too complex to do correctly. Technically, one does + write code by hand to use py_cpp. The goal, however, has been to make + that code nearly as simple as listing the names of the classes and + member functions you want to expose in Python. + +

SIP

+

+ SIP + is a system similar to SWIG, though seemingly more + C++-oriented. The author says that like py_cpp, SIP supports overriding + extension class member functions in Python subclasses. It appears to + have been designed specifically to directly support some features of + PyQt/PyKDE, which is its primary client. Documentation is almost + entirely missing at the time of this writing, so a detailed comparison + is difficult. + +

ILU

+

+ ILU + is a very ambitious project which tries to describe a module's interface + (types and functions) in terms of an Interface + Specification Language (ISL) so that it can be uniformly interfaced + to a wide range of computer languages, including Common Lisp, C++, C, + Modula-3, and Python. ILU can parse the ISL to generate a C++ language + header file describing the interface, of which the user is expected to + provide an implementation. Unlike py_cpp, this means that the system + imposes implementation details on your C++ code at the deepest level. It + is worth noting that some of the C++ names generated by ILU are supposed + to be reserved to the C++ implementation. It is unclear from the + documentation whether ILU supports overriding C++ virtual functions in Python. + +

GRAD

+

+ GRAD + is another very ambitious project aimed at generating Python wrappers for + interfaces written in “legacy languages”, among which C++ is the first one + implemented. Like SWIG, it aims to parse source code and automatically + generate wrappers, though it appears to take a more sophisticated approach + to parsing in general and C++ in particular, so it should do a much better + job with C++. It appears to support function overloading. The + documentation is missing a lot of information I'd like to see, so it is + difficult to give an accurate and fair assessment. I am left with the + following questions: +

+

+ Anyone in the possession of the answers to these questions will earn my + gratitude for a write-up ;-) + +

Zope ExtensionClasses

+

+ + ExtensionClasses in Zope use the same underlying mechanism as py_cpp + to support subclassing of extension types in Python, including + multiple-inheritance. Both systems support pickling/unpickling of + extension class instances in very similar ways. Both systems rely on the + same “Don + Beaudry Hack” that also inspired Don's MESS System. +

+ The major differences are: +

+

+ Previous: A Brief Introduction to writing Python Extension Modules + Next: A Simple Example Using py_cpp + Up: Top +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided “as is” without + express or implied warranty, and with no claim as to its suitability + for any purpose. +

+ Updated: Nov 26, 2000 +

+ diff --git a/doc/enums.html b/doc/enums.html new file mode 100644 index 00000000..57aef35f --- /dev/null +++ b/doc/enums.html @@ -0,0 +1,94 @@ + + + Wrapping enums + +
+

+ c++boost.gif (8819 bytes)Wrapping enums +

+

Because there is in general no way to deduce that a value of arbitrary type T +is an enumeration constant, py_cpp cannot automatically convert enum values to +and from Python. To handle this case, you need to decide how you want the enum +to show up in Python (since Python doesn't have enums). Once you have done that, +you can write some simple from_python() and +to_python() functions. + +

If you are satisfied with a Python int as a way to represent your enum +values, py_cpp provides a shorthand for these functions. You just need to +instantiate boost::python::enum_as_int_converters<EnumType> where +EnumType is your enumerated type. There are two convenient ways to do this: + +

    +
  1. +
    +// drop into namespace python and explicitly instantiate
    +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // this is a gcc 2.95.2 bug workaround
    +  template class enum_as_int_converters;
    +BOOST_PYTHON_END_CONVERSION_NAMESPACE
    +
    +
    +
  2. +// instantiate as base class in any namespace
    +struct EnumTypeConverters
    +    : boost::python::py_enum_as_int_converters
    +{
    +};
    +
    +
+ +

Either of the above is equivalent to the following declarations: +

+BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // this is a gcc 2.95.2 bug workaround
+
+  MyEnumType from_python(PyObject* x, boost::python::type<MyEnumType>)
+  {
+      return static_cast<MyEnum>(
+        from_python(x, boost::python::type<long>()));
+  }
+
+  MyEnumType from_python(PyObject* x, boost::python::type<const MyEnumType&>)
+  {
+      return static_cast<MyEnum>(
+        from_python(x, boost::python::type<long>()));
+  }
+
+  PyObject* to_python(MyEnumType x)
+  {
+      return to_python(static_cast<long>(x));
+  }
+BOOST_PYTHON_END_CONVERSION_NAMESPACE
+
+ +

This technique defines the conversions of +MyEnumType in terms of the conversions for the built-in + long type. + +You may also want to add a bunch of lines like this to your module +initialization: + +

+mymodule.add(boost::python::to_python(enum_value_1), "enum_value_1");
+mymodule.add(boost::python::to_python(enum_value_2), "enum_value_2");
+...
+
+ +You can also add these to an extension class definition, if your enum happens to +be local to a class and you want the analogous interface in Python: + +
+my_class.add(boost::python::to_python(enum_value_1), "enum_value_1");
+my_class.add(boost::python::to_python(enum_value_2), "enum_value_2");
+...
+
+

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided “as + is” without express or implied warranty, and with no claim as to + its suitability for any purpose. +

+ Updated: Nov 26, 2000 +

+ diff --git a/doc/example1.html b/doc/example1.html new file mode 100644 index 00000000..9bf9e280 --- /dev/null +++ b/doc/example1.html @@ -0,0 +1,129 @@ + + + A Simple Example Using py_cpp + +
+

+ + +

+

+ A Simple Example Using py_cpp +

+

+ Suppose we have the following C++ API which we want to expose in + Python: +

+
+#include <string>
+
+namespace hello {
+  class world
+  {
+   public:
+      world(int);
+      ~world();
+      std::string get() const { return "hi, world"; }
+    ...
+  };
+  std::size_t length(const world& x) { return std::strlen(x.get()); }
+}
+
+
+
+

+ Here is the C++ code for a python module called hello + which exposes the API using py_cpp: +

+
+#include 
+// Python requires an exported function called init<module-name> in every
+// extension module. This is where we build the module contents.
+extern "C"
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void inithello()
+{
+    try
+    {
+       // create an object representing this extension module
+       boost::python::module_builder hello("hello");
+       // Create the Python type object for our extension class
+       boost::python::class_builder<hello::world> world_class(hello, "world");
+       // Add the __init__ function
+       world_class.def(boost::python::constructor<int>());
+       // Add a regular member function
+       world_class.def(&hello::world::get, "get");
+       // Add a regular function to the module
+       hello.def(hello::length, "length");
+    }
+    catch(...)
+    {
+       boost::python::handle_exception();    // Deal with the exception for Python
+    }
+}
+// Win32 DLL boilerplate
+#if defined(_WIN32)
+#include <windows.h>
+extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID)
+{
+    return 1;
+}
+#endif // _WIN32
+
+
+

+ 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(4)
+>>> 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... +

+ Previous: Comparisons with other systems Next: Overridable virtual functions Up: + Top +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +

+ Updated: Nov 26, 2000 +

+ diff --git a/doc/extending.html b/doc/extending.html new file mode 100644 index 00000000..47341389 --- /dev/null +++ b/doc/extending.html @@ -0,0 +1,73 @@ + + + + A Brief Introduction to writing Python extension modules + +

+ c++boost.gif (8819 bytes) +

+

+ A Brief Introduction to writing Python extension modules +

+

+ Interfacing any language to Python involves building a module which can + be loaded by the Python interpreter, but which isn't written in Python. + This is known as an extension module. Many of the built-in Python + libraries are constructed in 'C' this way; Python even supplies its + fundamental + types using the same mechanism. An extension module can be statically + linked with the Python interpreter, but it more commonly resides in a + shared library or DLL. +

+ As you can see from The Python Extending + and Embedding Tutorial, writing an extension module normally means + worrying about +

+ This last item typically occupies a great deal of code in an extension + module. Remember that Python is a completely dynamic language. A callable + object receives its arguments in a tuple; it is up to that object to + extract those arguments from the tuple, check their types, and raise + appropriate exceptions. There are numerous other tedious details that need + to be managed; too many to mention here. Py_cpp is designed to lift most of + that burden.
+
+ +

+ Another obstacle that most people run into eventually when extending + Python is that there's no way to make a true Python class in an extension + module. The typical solution is to create a new Python type in the + extension module, and then write an additional module in 100% Python. The + Python module defines a Python class which dispatches to an instance of + the extension type, which it contains. This allows users to write + subclasses of the class in the Python module, almost as though they were + sublcassing the extension type. Aside from being tedious, it's not really + the same as having a true class, because there's no way for the user to + override a method of the extension type which is called from the + extension module. Py_cpp solves this problem by taking advantage of Python's metaclass + feature to provide objects which look, walk, and hiss almost exactly + like regular Python classes. Py_cpp classes are actually cleaner than + Python classes in some subtle ways; a more detailed discussion will + follow (someday).

+

Next: Comparisons with Other Systems Up: Top

+

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability for + any purpose.

+ diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 00000000..7ed767a1 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,211 @@ + + + + py_cpp Python/C++ binding documentation + +

+ c++boost.gif (8819 bytes) py_cpp* +

+ +

+ The source code for py_cpp, including a MSVC demo project is available here. + +

Synopsis

+

+ py_cpp is a system for quickly and easily interfacing C++ code with Python such that the Python interface is + very similar to the C++ interface. It is designed to be minimally + intrusive on your C++ design. In most cases, you should not have to alter + your C++ classes in any way in order to use them with py_cpp. The system + should simply “reflect” your C++ classes and functions into + Python. The major features of py_cpp include support for: +

+among others. + + +

Supported Platforms

+

py_cpp has been tested in the following configurations: + +

+ +

Py_cpp requires the boost libraries, and is + has been accepted for inclusion into the boost libraries pending “boostification“ + (completion of the documentation, change in some naming conventions and + resolution of some namespace issues). + +

Credits

+ + +

Table of Contents

+ +
    +
  1. A Brief Introduction to writing Python + extension modules + +
  2. Comparisons between py_cpp and other + systems for extending Python + +
  3. A Simple Example Using py_cpp + +
  4. Overridable Virtual Functions + +
  5. Function Overloading + +
  6. Inheritance + +
  7. Special Method and Operator Support + +
  8. A Peek Under the Hood + +
  9. Building a Module with Py_cpp + +
  10. Advanced Topics + +
      +
    1. class_builder<> + +
    2. enums + +
    3. References + +
    4. Pointers and Smart Pointers + +
    5. Built-in Python Types + +
    6. Other Extension Types + +
    7. Templates +
    + +
+ +

+ More sophisticated examples are given in + extclass_demo.cpp, extclass_demo.h, and + test_extclass.py in the source code + archive. There's much more here, and much more documentation to + come... +

+ Questions should be directed to the boost mailing list. + +

Naming Contest

+ +

+ Yes, I know py_cpp is a lousy name. Problem is, the best names my puny + imagination can muster (IDLE and GRAIL) are taken, so I'm holding a + naming contest. First prize? You get to pick the name<0.2wink> and + you will be credited in the documentation. Names that have been suggested + so far include: +

+ Please post or send me your suggestions!
+
+ +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided “as is” without + express or implied warranty, and with no claim as to its suitability for + any purpose. +

+ Updated: Nov 26, 2000 + diff --git a/doc/inheritance.html b/doc/inheritance.html new file mode 100644 index 00000000..2dd8071b --- /dev/null +++ b/doc/inheritance.html @@ -0,0 +1,165 @@ + + + Inheritance + +

+

+ c++boost.gif (8819 bytes)Inheritance +

+ +

Inheritance in Python

+ +

+ Py_cpp extension classes support single and multiple-inheritance in + Python, just like regular Python classes. You can mix built-in Python + classes with py_cpp extension classes in a derived class' tuple of + bases. Whenever a py_cpp extension class is among the bases for a new + class in Python, the result is an extension class: +

+
+>>> class MyPythonClass:
+...     def f(): return 'MyPythonClass.f()'
+...
+>>> import my_extension_module
+>>> class Derived(my_extension_module.MyExtensionClass, MyPythonClass):
+...     '''This is an extension class'''
+...     pass
+...
+>>> x = Derived()
+>>> x.f()
+'MyPythonClass.f()'
+>>> x.g()
+'MyExtensionClass.g()'
+
+
+ +

Reflecting C++ Inheritance Relationships

+

+ Py_cpp also allows us to represent C++ inheritance relationships so that + wrapped derived classes may be passed where values, pointers, or + references to a base class are expected as arguments. The + declare_base member function of + class_builder<> is used to establish the relationship + between base and derived classes: + +

+
+#include <memory> // for std::auto_ptr<>
+
+struct Base {
+    virtual ~Base() {}
+    virtual const char* name() const { return "Base"; }
+};
+
+struct Derived : Base {
+    Derived() : x(-1) {}
+    virtual const char* name() const { return "Derived"; }
+    int x;
+};
+
+std::auto_ptr<Base> derived_as_base() {
+    return std::auto_ptr<Base>(new Derived);
+}
+
+const char* get_name(const Base& b) {
+    return b.name();
+}
+
+int get_derived_x(const Derived& d) {
+    return d.x;
+}
+    
+#include +extern "C" +#ifdef _WIN32 +__declspec(dllexport) +#endif +void initmy_module() +{ +    try +    { +       boost::python::module_builder my_module("my_module"); + +       boost::python::class_builder<Base> base_class(my_module, "Base"); +       base_class.def(boost::python::constructor<void>()); + +       boost::python::class_builder<Derived> derived_class(my_module, "Derived"); +       derived_class.def(boost::python::constructor<void>()); + // Establish the inheritance relationship between Base and Derived + derived_class.declare_base(base_class); + + my_module.def(derived_as_base, "derived_as_base"); + my_module.def(get_name, "get_name"); + my_module.def(get_derived_x, "get_derived_x"); +    } +    catch(...) +    { +       boost::python::handle_exception();    // Deal with the exception for Python +    } +} +
+
+ +

+ Then, in Python: +

+
+>>> from my_module import *
+>>> base = Base()
+>>> derived = Derived()
+>>> get_name(base)
+'Base'
+
objects of wrapped class Derived may be passed where Base is expected
+>>> get_name(derived) 
+'Derived'
+
objects of wrapped class Derived can be passed where Derived is +expected but where type information has been lost.
+>>> get_derived_x(derived_as_base()) 
+-1
+
+
+ +

Inheritance Without Virtual Functions

+ +

+ If for some reason your base class has no virtual functions but you still want + to represent the inheritance relationship between base and derived classes, + pass the special symbol boost::python::without_downcast as the 2nd parameter + to declare_base: + +

+
+struct Base2 {};
+struct Derived2 { int f(); };
+
+ ... +   boost::python::class_builder<Base> base2_class(my_module, "Base2"); +   base2_class.def(boost::python::constructor<void>()); + +   boost::python::class_builder<Derived2> derived2_class(my_module, "Derived2"); +   derived2_class.def(boost::python::constructor<void>()); + derived_class.declare_base(base_class, boost::python::without_downcast); +
+
+ +

This approach will allow Derived2 objects to be passed where + Base2 is expected, but does not attempt to implicitly convert (downcast) + smart-pointers to Base2 into Derived2 pointers, + references, or values. + +

+ Previous: Function Overloading + Next: Special Method Names + Up: Top +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +

+ Updated: Nov 26, 2000 +

+ diff --git a/doc/overloading.html b/doc/overloading.html new file mode 100644 index 00000000..6197b230 --- /dev/null +++ b/doc/overloading.html @@ -0,0 +1,156 @@ + + + Function Overloading + +
+

+ c++boost.gif (8819 bytes)Function Overloading +

+ +

An Example

+

+ To expose overloaded functions in Python, simply def() each + one with the same Python name: +

+
+inline int f1() { return 3; }
+inline int f2(int x) { return x + 1; }
+
+class X {
+public:
+    X() : m_value(0) {}
+    X(int n) : m_value(n) {}
+    int value() const { return m_value; }
+    void value(int v) { m_value = v; }
+private:
+    int m_value;
+};
+  ...
+
+void initoverload_demo()
+{
+    try
+    {
+        boost::python::module_builder overload_demo("overload_demo");
+        // Overloaded functions at module scope
+        overload_demo.def(f1, "f");
+        overload_demo.def(f2, "f");
+
+        boost::python::class_builder<X> x_class(overload_demo, "X");
+        // Overloaded constructors
+        x_class.def(boost::python::constructor<>());
+        x_class.def(boost::python::constructor<int>());
+
+        // Overloaded member functions
+        x_class.def((int (X::*)() const)&X::value, "value");
+        x_class.def((void (X::*)(int))&X::value, "value");
+  ...
+
+
+ +

+ Now in Python: +

+
+>>> from overload_demo import *
+>>> x0 = X()
+>>> x1 = X(1)
+>>> x0.value()
+0
+>>> x1.value()
+1
+>>> x0.value(3)
+>>> x0.value()
+3
+>>> X('hello')
+TypeError: No overloaded functions match (X, string). Candidates are:
+void (*)()
+void (*)(int)
+>>> f()
+3
+>>> f(4)
+5
+
+
+ +

Discussion

+

+ Notice that overloading in the Python module was produced three ways:

    +
  1. by combining the non-overloaded C++ functions int f1() + and int f2(int) and exposing them as f in Python. +
  2. by exposing the overloaded constructors of class X +
  3. by exposing the overloaded member functions X::value. +
+

+ Techniques 1. and 3. above are really alternatives. In case 3, you need + to form a pointer to each of the overloaded functions. The casting + syntax shown above is one way to do that in C++. Case 1 does not require + complicated-looking casts, but may not be viable if you can't change + your C++ interface. N.B. There's really nothing unsafe about casting an + overloaded (member) function address this way: the compiler won't let + you write it at all unless you get it right. + +

An Alternative to Casting

+

+ This approach is not neccessarily better, but may be preferable for some + people who have trouble writing out the types of (member) function + pointers or simply prefer to avoid all casts as a matter of principle: +

+
+// Forwarding functions for X::value
+inline void set_x_value(X& self, int v) { self.value(v); }
+inline int get_x_value(X& self) { return self.value(); }
+   ...
+        // Overloaded member functions
+        x_class.def(set_x_value, "value");
+        x_class.def(get_x_value, "value");
+
+
+

Here we are taking advantage of the ability to expose C++ functions at +namespace scope as Python member functions. + +

Overload Resolution

+

+ The function overload resolution mechanism in py_cpp works as + follows: + +

+ +

+ Prev: Overridable Virtual Functions + Next: Special Method Names + Up: Top +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided “as + is” without express or implied warranty, and with no claim as to + its suitability for any purpose. +

+ Updated: Nov 26, 2000 +

+ diff --git a/doc/overriding.html b/doc/overriding.html new file mode 100644 index 00000000..f8810ca1 --- /dev/null +++ b/doc/overriding.html @@ -0,0 +1,195 @@ + + + + Overridable Virtual Functions + + c++boost.gif (8819 bytes) + +

Overridable Virtual Functions

+ +

+ In the previous example we exposed a simple + C++ class in Python and showed that we could write a subclass. We even + redefined one of the functions in our derived class. Now we will learn + how to make the function behave virtually when called from C++. + + +

Example

+ +

In this example, it is assumed that world::get() is a virtual +member function: + +

+class world
+{
+ public:
+    world(int);
+    virtual ~world();
+    virtual std::string get() const { return "hi, world"; }
+};
+
+ +

+ We'll need a derived class* to help us + dispatch the call to Python. In our derived class, we need the following + elements: + +

    + +
  1. A PyObject* data member that holds a + reference to the corresponding Python object. + +
  2. A constructor for each exposed constructor of the + base class which stores an additional initial PyObject* argument + in the data member described above. + +
  3. An implementation of each virtual function you may + wish to override in Python which uses + boost::python::callback<return-type>::call_method() to call + the Python override. + +
  4. For each non-pure virtual function meant to be + overridable from Python, a static member function (or a free function) taking + a reference or pointer to the base type as the first parameter and which + forwards any additional parameters neccessary to the default + implementation of the virtual function. See also this + note if the base class virtual function is private. + +
+ +
+struct world_callback : world
+{
+    world_callback(PyObject* self, int x) // 2
+        : world(x),
+          m_self(self) {}
+
+    std::string get() const // 3
+        { return boost::python::callback<std::string>::call_method(m_self, "get"); }
+
+    static std::string default_get(const hello::world& self) const // 4
+        { return self.world::get(); }
+ private:
+    PyObject* m_self; // 1
+};
+
+ +

+ Finally, we add world_callback to the + class_builder<> declaration in our module initialization + function, and when we define the function, we must tell py_cpp about the default + implementation: + +

+// Create the Python type object for our extension class
+boost::python::class_builder<hello::world,world_callback> world_class(hello, "world");
+// Add a virtual member function
+world_class.def(&world::get, "get", &world_callback::default_get);
+
+ +

+ Now our subclass of hello.world behaves as expected: + +

+>>> class my_subclass(hello.world):
+...     def get(self):
+...         return 'hello, world'
+...
+>>> hello.length(my_subclass())
+12
+
+ +

+ *You may ask, "Why do we need this derived + class? This could have been designed so that everything gets done right + inside of hello::world." One of the goals of py_cpp is to be + minimally intrusive on an existing C++ design. In principle, it should be + possible to expose the interface for a 3rd party library without changing + it. To unintrusively hook into the virtual functions so that a Python + override may be called, we must use a derived class. + +

Pure Virtual Functions

+ +

+ A pure virtual function with no implementation is actually a lot easier to + deal with than a virtual function with a default implementation. First of + all, you obviously don't need to supply + a default implementation. Secondly, you don't need to call + def() on the extension_class<> instance + for the virtual function. In fact, you wouldn't want to: if the + corresponding attribute on the Python class stays undefined, you'll get an + AttributeError in Python when you try to call the function, + indicating that it should have been implemented. For example: +

+
+struct baz {
+    virtual int pure(int) = 0;
+};
+
+struct baz_callback {
+    int pure(int x) { boost::python::callback<int>::call_method(m_self, "pure", x); }
+};
+
+extern "C"
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+initfoobar()
+{
+    try
+    {
+       boost::python::module_builder foobar("foobar");
+       boost::python::class_builder<baz,baz_callback> baz_class("baz");
+       baz_class.def(&baz::pure, "pure");
+    }
+    catch(...)
+    {
+       boost::python::handle_exception();    // Deal with the exception for Python
+    }
+}
+
+
+

+ Now in Python: +

+
+>>> from foobar import baz
+>>> x = baz()
+>>> x.pure(1)
+Traceback (innermost last):
+  File "<stdin>", line 1, in ?
+AttributeError: pure
+>>> class mumble(baz):
+...    def pure(self, x): return x + 1
+...
+>>> y = mumble()
+>>> y.pure(99)
+100
+
+ +

Private Non-Pure Virtual Functions

+ +

This is one area where some minor intrusiveness on the wrapped library is +required. Once it has been overridden, the only way to call the base class +implementation of a private virtual function is to make the derived class a +friend of the base class. You didn't hear it from me, but most C++ +implementations will allow you to change the declaration of the base class in +this limited way without breaking binary compatibility (though it will certainly +break the ODR). + +

+ Prev: A Simple Example Using py_cpp Next: Function Overloading Up: Top +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability for + any purpose. +

+ Updated: Nov 26, 2000 + diff --git a/doc/pointers.html b/doc/pointers.html new file mode 100644 index 00000000..fe30af0b --- /dev/null +++ b/doc/pointers.html @@ -0,0 +1,145 @@ + + + Pointers + +

+

+ c++boost.gif (8819 bytes)Pointers +

+ +

The Problem With Pointers

+ +

+In general, raw pointers passed to or returned from functions are problematic +for py_cpp because pointers have too many potential meanings. Is it an iterator? +A pointer to a single element? An array? When used as a return value, is the +caller expected to manage (delete) the pointed-to object or is the pointer +really just a reference? If the latter, what happens to Python references to the +referent when some C++ code deletes it? +

+There are a few cases in which pointers are converted automatically: +

+ +

Can you avoid the problem?

+ +

My first piece of advice to anyone with a case not covered above is +“find a way to avoid the problem.” For example, if you have just one +or two functions returning a pointer to a single (not an array of) const +T* for some wrapped T, you may be able to write a “thin +converting wrapper” over those two functions as follows (Since py_cpp +converts const T& values to_python by copying the T +into a new extension instance, Foo must have a public copy constructor): + +

+const Foo* f(); // original function
+const Foo& f_wrapper() { return *f(); }
+  ...
+my_module.def(f_wrapper, "f");
+
+ +

Dealing with the problem

+ +

The first step in handling the remaining cases is to figure out what the pointer +means. Several potential solutions are provided in the examples that follow: + +

Returning a pointer to a wrapped type

+ +

Returning a const pointer

+ +

If you have lots of functions returning a const T* for some +wrapped T, you may want to provide an automatic +to_python conversion function so you don't have to write lots of +thin wrappers. You can do this simply as follows: + +

+BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // this is a gcc 2.95.2 bug workaround
+  PyObject* to_python(const Foo* p) {
+     return to_python(*p); // convert const Foo* in terms of const Foo&
+  }
+BOOST_PYTHON_END_CONVERSION_NAMESPACE
+
+ +

If you can't (afford to) copy the referent, or the pointer is non-const

+ +

If the wrapped type doesn't have a public copy constructor, if copying is +extremely costly (remember, we're dealing with Python here), or if the +pointer is non-const and you really need to be able to modify the referent from +Python, you can use the following dangerous trick. Why dangerous? Because python +can not control the lifetime of the referent, so it may be destroyed by your C++ +code before the last Python reference to it disappears: + +

+BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // this is a gcc 2.95.2 bug workaround
+  PyObject* to_python(Foo* p)
+  {
+      return boost::python::PyExtensionClassConverters::ptr_to_python(p);
+  }
+
+  PyObject* to_python(const Foo* p)
+  {
+      return to_python(const_cast(p));
+  }
+BOOST_PYTHON_END_CONVERSION_NAMESPACE
+
+ +This will cause the Foo* to be treated as though it were an owning smart +pointer, even though it's not. Be sure you don't use the reference for anything +from Python once the pointer becomes invalid, though. Don't worry too much about +the const_cast<> above: Const-correctness is completely lost +to Python anyway! + +

[In/]Out Parameters and Immutable Types

+ +

If you have an interface that uses non-const pointers (or references) as +in/out parameters to types which in Python are immutable (e.g. int, string), +there simply is no way to get the same interface in Python. You must +resort to transforming your interface with simple thin wrappers as shown below: +

+const void f(int* in_out_x); // original function
+const int f_wrapper(int in_x) { f(in_x); return in_x; }
+  ...
+my_module.def(f_wrapper, "f");
+
+ +

Of course, [in/]out parameters commonly occur only when there is already a +return value. You can handle this case by returning a Python tuple: +

+typedef unsigned ErrorCode;
+const char* f(int* in_out_x); // original function
+ ...
+#include 
+const boost::python::tuple f_wrapper(int in_x) { 
+  const char* s = f(in_x); 
+  return boost::python::tuple(s, in_x);
+}
+  ...
+my_module.def(f_wrapper, "f");
+
+

Now, in Python: +

+>>> str,out_x = f(3)
+
+ + + +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +

+ Updated: Nov 26, 2000 +

+ diff --git a/doc/special.html b/doc/special.html new file mode 100644 index 00000000..4c314fcf --- /dev/null +++ b/doc/special.html @@ -0,0 +1,888 @@ + + + Special Method and Operator Support + +
+

+ c++boost.gif (8819 bytes)Special Method and + Operator Support +

+

+ Overview +

+

+ Py_cpp supports all of the standard + special method names supported by real Python class instances + except __complex__ (more on the reasons below). In addition, it can quickly and easily expose + suitable C++ functions and operators as Python operators. The following + categories of special method names are supported: +

+ +

Basic Customization

+ + +

+ Python provides a number of special operators for basic customization of a + class. Only a brief description is provided below; more complete + documentation can be found here. + +

+
+ __init__(self) +
+ Initialize the class instance. For extension classes not subclassed in + Python, this is provided by the + boost::python::constructor<...>() construct and should not be explicitly defed. +
+ __del__(self) +
+ Called when the extension instance is about to be destroyed. +
+ __repr__(self) +
+ Create a string representation from which the object can be + reconstructed. +
+ __str__(self) +
+ Create a string representation which is suitable for printing. +
+ __cmp__(self, other) +
+ Three-way compare function, used to implement comparison operators + (< etc.) Should return a negative integer if self < other + , zero if self == other , a positive integer if + self > other . +
+ __hash__(self) +
+ Called for the key object for dictionary operations, and by the + built-in function hash(). Should return a 32-bit integer usable as a + hash value for dictionary operations (only allowed if __cmp__ is also + defined) +
+ __nonzero__(self) +
+ called if the object is used as a truth value (e.g. in an if + statement) +
+ __call__ (self[, args...]) +
+Called when the instance is “called” as a function; if this method +is defined, x(arg1, arg2, ...) is a shorthand for +x.__call__(arg1, arg2, ...). +
+ + If we have a suitable C++ function that supports any of these features, + we can export it like any other function, using its Python special name. + For example, suppose that class Foo provides a string + conversion function: +
+std::string to_string(Foo const& f)
+{
+    std::ostringstream s;
+    s << f;
+    return s.str();
+}
+
+ This function would be wrapped like this: +
+boost::python::class_builder<Foo> foo_class(my_module, "Foo");
+foo_class.def(&to_string, "__str__");
+
+ Note that py_cpp also supports automatic wrapping of + __str__ and __cmp__. This is explained in the next section and the Table of + Automatically Wrapped Methods. + +

Numeric Operators

+ +

+ Numeric operators can be exposed manually, by defing C++ + [member] functions that support the standard Python numeric + protocols. This is the basic same technique used to expose + to_string() as __str__() above, and is covered in detail below. Py_cpp also supports + automatic wrapping of numeric operators whenever they have already + been defined in C++. + +

Exposing C++ Operators Automatically

+ +

+Supose we wanted to expose a C++ class + BigNum which supports addition, so that we can write (in C++): +

+BigNum a, b, c;
+...
+c = a + b;
+
+

+ To enable the same functionality in Python, we first wrap the + BigNum class as usual: +

+boost::python::class_builder<BigNum> bignum_class(my_module, "BigNum");
+bignum_class.def(boost::python::constructor<>());
+...
+
+ Then we export the addition operator like this: + +
+bignum_class.def(boost::python::operators<boost::python::op_add>());
+
+ + Since BigNum also supports subtraction, multiplication, and division, we + want to export those also. This can be done in a single command by + “or”ing the operator identifiers together (a complete list of these + identifiers and the corresponding operators can be found in the Table of Automatically Wrapped Methods): +
+bignum_class.def(boost::python::operators<(boost::python::op_sub | boost::python::op_mul | boost::python::op_div)>());
+
+ [Note that the or-expression must be enclosed in parentheses.] + +

This form of operator definition can be used to wrap unary and + homogeneous binary operators (a homogeneous operator has left and + right operands of the same type). Now suppose that our C++ library also + supports addition of BigNums and plain integers: + +

+BigNum a, b;
+int i;
+...
+a = b + i;
+a = i + b;
+
+ To wrap these heterogeneous operators, we need to specify a different type for + one of the operands. This is done using the right_operand + and left_operand templates: +
+bignum_class.def(boost::python::operators<boost::python::op_add>(), boost::python::right_operand<int>());
+bignum_class.def(boost::python::operators<boost::python::op_add>(), boost::python::left_operand<int>());
+
+ Py_cpp uses overloading to register several variants of the same + operation (more on this in the context of + coercion). Again, several operators can be exported at once: +
+bignum_class.def(boost::python::operators<(boost::python::op_sub | boost::python::op_mul | boost::python::op_div)>(),
+                 boost::python::right_operand<int>());
+bignum_class.def(boost::python::operators<(boost::python::op_sub | boost::python::op_mul | boost::python::op_div)>(), 
+                 boost::python::left_operand<int>());
+
+ The type of the operand not mentioned is taken from the class being wrapped. In + our example, the class object is bignum_class, and thus the + other operand's type is “BigNum const&”. You can override + this default by explicitly specifying a type in the + operators template: +
+bignum_class.def(boost::python::operators<boost::python::op_add, BigNum>(), boost::python::right_operand<int>());
+
+

+ Note that automatic wrapping uses the expression + “left + right” and can be used uniformly + regardless of whether the C++ operators are supplied as free functions +

+BigNum operator+(BigNum, BigNum)
+
+ or as member + functions
+BigNum::operator+(BigNum).
+
+ +

+ For the Python built-in functions pow() and + abs(), there is no corresponding C++ operator. Instead, + automatic wrapping attempts to wrap C++ functions of the same name. This + only works if those functions are known in namespace + python. On some compilers (e.g. MSVC) it might be + necessary to add a using declaration prior to wrapping: + +

+namespace boost { namespace python { 
+  using my_namespace::pow;
+  using my_namespace::abs;
+}
+
+ +

Wrapping Numeric Operators Manually

+

+ In some cases, automatic wrapping of operators may be impossible or + undesirable. Suppose, for example, that the modulo operation for BigNums + is defined by a set of functions mod() (for automatic + wrapping, we would need operator%()): + +

+BigNum mod(BigNum const& left, BigNum const& right);
+BigNum mod(BigNum const& left, int right);
+BigNum mod(int left, BigNum const& right);
+
+ +

+ In order to create the Python operator "__mod__" from these functions, we + have to wrap them manually: + +

+bignum_class.def((BigNum (*)(BigNum const&, BigNum const&))&mod, "__mod__");
+bignum_class.def((BigNum (*)(BigNum const&, int))&mod, "__mod__");
+
+ +

+ The third form (with int as left operand) cannot be wrapped + this way. We must first create a function rmod() with the + operands reversed: + +

+BigNum rmod(BigNum const& right, int left)
+{
+    return mod(left, right);
+}
+
+ + This function must be wrapped under the name "__rmod__": + +
+bignum_class.def(&rmod,  "__rmod__");
+
+ + Many of the possible operator names can be found in the Table of Automatically Wrapped Methods. Special treatment is + necessary to export the ternary pow operator. + +

+ Automatic and manual wrapping can be mixed arbitrarily. Note that you + cannot overload the same operator for a given extension class on both + “int” and “float”, because Python implicitly + converts these types into each other. Thus, the overloaded variant + found first (be it “int“ or “float”) will be + used for either of the two types. + +

Coercion

+ + + Plain Python can only execute operators with identical types on the left + and right hand side. If it encounters an expression where the types of + the left and right operand differ, it tries to coerce these type to a + common type before invoking the actual operator. Implementing good + coercion functions can be difficult if many type combinations must be + supported. +

+ Py_cpp solves this problem the same way that C++ does: with overloading. This technique drastically + simplifies the code neccessary to support operators: you just register + operators for all desired type combinations, and py_cpp automatically + ensures that the correct function is called in each case; there is no + need for user-defined coercion functions. To enable operator + overloading, py_cpp provides a standard coercion which is implicitly + registered whenever automatic operator wrapping is used. +

+ If you wrap all operator functions manually, but still want to use + operator overloading, you have to register the standard coercion + function explicitly: + +

+// this is not necessary if automatic operator wrapping is used
+bignum_class.def_standard_coerce();
+
+ + If you encounter a situation where you absolutely need a customized + coercion, you can overload the "__coerce__" operator itself. The signature + of a coercion function should look like one of the following (the first is + the safest): + +
+boost::python::tuple custom_coerce(boost::python::reference left, boost::python::reference right);
+boost::python::tuple custom_coerce(PyObject* left, PyObject* right);
+PyObject* custom_coerce(PyObject* left, PyObject* right);
+
+ + The resulting tuple must contain two elements which + represent the values of left and right + converted to the same type. Such a function is wrapped as usual: + +
+some_class.def(&custom_coerce, "__coerce__");
+
+ + Note that the later use of automatic operator wrapping on a + class_builder or a call to + “some_class.def_standard_coerce()” will cause any + custom coercion function to be replaced by the standard one. + +

The Ternary pow() Operator

+ +

+ In addition to the usual binary pow(x, y) operator (meaning + xy), Python also provides a ternary variant that implements + xy mod z, presumably using a more efficient algorithm than + concatenation of power and modulo operators. Automatic operator wrapping + can only be used with the binary variant. Ternary pow() must + always be wrapped manually. For a homgeneous ternary pow(), + this is done as usual: + +

+BigNum power(BigNum const& first, BigNum const& second, BigNum const& module);
+typedef BigNum (ternary_function1)(const BigNum&, const BigNum&, const BigNum&);
+...
+bignum_class.def((ternary_function1)&power,  "__pow__");
+
+ + If you want to support this function with non-uniform argument + types, wrapping is a little more involved. Suppose you have to wrap: + +
+BigNum power(BigNum const& first, int second, int modulus);
+BigNum power(int first, BigNum const& second, int modulus);
+BigNum power(int first, int second, BigNum const& modulus);
+
+ + The first variant can be wrapped as usual: + +
+typedef BigNum (ternary_function2)(const BigNum&, int, int);
+bignum_class.def((ternary_function2)&power,  "__pow__");
+
+ + In the second variant, however, BigNum appears only as second + argument, and in the last one it is the third argument. These functions + must be presented to py_cpp such that that the BigNum + argument appears in first position: + +
+BigNum rpower(BigNum const& second, int first, int modulus)
+{
+    return power(first, second, third);
+}
+
+BigNum rrpower(BigNum const& third, int first, int second)
+{
+    return power(first, second, third);
+}
+
+ +

These functions must be wrapped under the names "__rpow__" and "__rrpow__" + respectively: + +

+bignum_class.def((ternary_function2)&rpower,  "__rpow__");
+bignum_class.def((ternary_function2)&rrpower,  "__rrpow__");
+
+ +Note that "__rrpow__" is an extension not present in plain Python. + +

Table of Automatically Wrapped Methods

+

+ Py_cpp can automatically wrap the following + special methods: + +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ Python Operator Name + + Python Expression + + C++ Operator Id + + C++ Expression Used For Automatic Wrapping
+ with cpp_left = from_python(left, + type<Left>()),
+ cpp_right = from_python(right, + type<Right>()),
+ and cpp_oper = from_python(oper, type<Oper>()) +
+ __add__, __radd__ + + left + right + + op_add + + cpp_left + cpp_right +
+ __sub__, __rsub__ + + left - right + + op_sub + + cpp_left - cpp_right +
+ __mul__, __rmul__ + + left * right + + op_mul + + cpp_left * cpp_right +
+ __div__, __rdiv__ + + left / right + + op_div + + cpp_left / cpp_right +
+ __mod__, __rmod__ + + left % right + + op_mod + + cpp_left % cpp_right +
+ __divmod__, __rdivmod__ + + (quotient, remainder)
+ = divmod(left, right)
+
+ op_divmod + + cpp_left / cpp_right +
cpp_left % cpp_right +
+ __pow__, __rpow__ + + pow(left, right)
+ (binary power) +
+ op_pow + + pow(cpp_left, cpp_right) +
+ __rrpow__ + + pow(left, right, modulo)
+ (ternary power modulo) +
+ no automatic wrapping, special treatment + required +
+ __lshift__, __rlshift__ + + left << right + + op_lshift + + cpp_left << cpp_right +
+ __rshift__, __rrshift__ + + left >> right + + op_rshift + + cpp_left >> cpp_right +
+ __and__, __rand__ + + left & right + + op_and + + cpp_left & cpp_right +
+ __xor__, __rxor__ + + left ^ right + + op_xor + + cpp_left ^ cpp_right +
+ __or__, __ror__ + + left | right + + op_or + + cpp_left | cpp_right + +
+ __cmp__, __rcmp__ + + cmp(left, right)
+ left < right
+ left <= right
+ left > right
+ left >= right
+ left == right
+ left != right +
+ op_cmp + + cpp_left < cpp_right  +
cpp_right < cpp_left + +
+ __neg__ + + -oper  (unary negation) + + op_neg + + -cpp_oper +
+ __pos__ + + +oper  (identity) + + op_pos + + +cpp_oper +
+ __abs__ + + abs(oper)  (absolute value) + + op_abs + + abs(cpp_oper) +
+ __invert__ + + ~oper  (bitwise inversion) + + op_invert + + ~cpp_oper +
+ __int__ + + int(oper)  (integer conversion) + + op_int + + long(cpp_oper) +
+ __long__ + + long(oper) 
+ (infinite precision integer conversion) +
+ op_long + + PyLong_FromLong(cpp_oper) +
+ __float__ + + float(oper)  (float conversion) + + op_float + + double(cpp_oper) +
+ __str__ + + str(oper)  (string conversion) + + op_str + + std::ostringstream s; s << oper; +
+ __coerce__ + + coerce(left, right) + + usually defined automatically, otherwise + special treatment required +
+ +

Sequence and Mapping Operators

+ +

+ Sequence and mapping operators let wrapped objects behave in accordance + to Python's iteration and access protocols. These protocols differ + considerably from the ones found in C++. For example, Python's typical + iteration idiom looks like +

+for i in S:
+
+ +while in C++ one writes + +
+for (iterator i = S.begin(), end = S.end(); i != end)
+
+ +

One could try to wrap C++ iterators in order to carry the C++ idiom into + Python. However, this does not work very well because + +

    +
  1. It leads to + non-uniform Python code (wrapped sequences support a usage different from + Python built-in sequences) and + +
  2. Iterators (e.g. std::vector::iterator) are often implemented as plain C++ + pointers which are problematic for any automatic + wrapping system. +
+ +

+ It is a better idea to support the standard Python + sequence and mapping protocols for your wrapped containers. These + operators have to be wrapped manually because there are no corresponding + C++ operators that could be used for automatic wrapping. The Python + documentation lists the relevant + container operators. In particular, expose __getitem__, __setitem__ + and remember to raise the appropriate Python exceptions + (PyExc_IndexError for sequences, + PyExc_KeyError for mappings) when the requested item is not + present. + +

+ In the following example, we expose std::map<std::size_t,std::string>: +

+
+typedef std::map<std::size_t, std::string> StringMap;
+
+// A helper function for dealing with errors. Throw a Python exception
+// if p == m.end().
+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, boost::python::converters::to_python(key));
+        throw boost::python::error_already_set();
+    }
+}
+
+// Define some simple wrapper functions which match the Python  protocol
+// for __getitem__, __setitem__, and __delitem__.  Just as in Python, a
+// free function with a “self” first parameter makes a fine class method.
+
+const std::string& get_item(const StringMap& self, std::size_t key)
+{
+    const StringMap::const_iterator p = self.find(key);
+    throw_key_error_if_end(self, p, key);
+    return p->second;
+}
+
+// Sets the item corresponding to key in the map.
+void StringMapPythonClass::set_item(StringMap& self, std::size_t key, const std::string& value)
+{
+    self[key] = value;
+}
+
+// Deletes the item corresponding to key from the map.
+void StringMapPythonClass::del_item(StringMap& self, std::size_t key)
+{
+    const StringMap::iterator p = self.find(key);
+    throw_key_error_if_end(self, p, key);
+    self.erase(p);
+}
+
+class_builder<StringMap> string_map(my_module, "StringMap");
+string_map.def(boost::python::constructor<>());
+string_map.def(&StringMap::size, "__len__");
+string_map.def(get_item, "__getitem__");
+string_map.def(set_item, "__setitem__");
+string_map.def(del_item, "__delitem__");
+
+
+

+ Then in Python: +

+
+>>> m = StringMap()
+>>> m[1]
+Traceback (innermost last):
+  File "<stdin>", line 1, in ?
+KeyError: 1
+>>> m[1] = 'hello'
+>>> m[1]
+'hello'
+>>> del m[1]
+>>> m[1]            # prove that it's gone
+Traceback (innermost last):
+  File "<stdin>", line 1, in ?
+KeyError: 1
+>>> del m[2]
+Traceback (innermost last):
+  File "<stdin>", line 1, in ?
+KeyError: 2
+>>> len(m)
+0
+>>> m[0] = 'zero'
+>>> m[1] = 'one'
+>>> m[2] = 'two'
+>>> m[3] = 'three'
+>>> len(m)
+4
+
+
+ +

Customized Attribute Access

+ +

+ Just like built-in Python classes, py_cpp extension classes support special + the usual attribute access methods __getattr__, + __setattr__, and __delattr__. + Because writing these functions can + be tedious in the common case where the attributes being accessed are + known statically, py_cpp checks the special names + +

+ + to provide functional access to the attribute <name>. This + facility can be used from C++ or entirely from Python. For example, the + following shows how we can implement a “computed attribute” in Python: +
+
+>>> class Range(AnyPy_cppExtensionClass):
+...    def __init__(self, start, end):
+...        self.start = start
+...        self.end = end
+...    def __getattr__length__(self):
+...        return self.end - self.start
+...
+>>> x = Range(3, 9)
+>>> x.length
+6
+
+
+

+ Direct Access to Data Members +

+

+ Py_cpp uses the special + __xxxattr__<name>__ functionality described above + to allow direct access to data members through the following special + functions on class_builder<> and + extension_class<>: +

+

+ Note that the first two functions, used alone, may produce surprising + behavior. For example, when def_getter() is used, the + default functionality for setattr() and + delattr() remains in effect, operating on items in the extension + instance's name-space (i.e., its __dict__). For that + reason, you'll usually want to stick with def_readonly and + def_read_write. +

+ For example, to expose a std::pair<int,long> we + might write: +

+
+typedef std::pair<int,long> Pil;
+int first(const Pil& x) { return x.first; }
+long second(const Pil& x) { return x.second; }
+   ...
+my_module.def(first, "first");
+my_module.def(second, "second");
+
+class_builder<Pil> pair_int_long(my_module, "Pair");
+pair_int_long.def(boost::python::constructor<>());
+pair_int_long.def(boost::python::constructor<int,long>());
+pair_int_long.def_read_write(&Pil::first, "first");
+pair_int_long.def_read_write(&Pil::second, "second");
+
+
+

+ Now your Python class has attributes first and + second which, when accessed, actually modify or reflect the + values of corresponding data members of the underlying C++ object. Now + in Python: +

+
+>>> x = Pair(3,5)
+>>> x.first
+3
+>>> x.second
+5
+>>> x.second = 8
+>>> x.second
+8
+>>> second(x) # Prove that we're not just changing the instance __dict__
+8
+
+
+

+ And what about __complex__? +

+

+ That, dear reader, is one problem we don't know how to solve. The + Python source contains the following fragment, indicating the + special-case code really is hardwired: +

+
+/* XXX Hack to support classes with __complex__ method */
+if (PyInstance_Check(r)) { ...
+
+
+

+ Previous: Inheritance Next: A Peek Under the Hood Up: Top +

+ © Copyright David Abrahams and Ullrich Köthe 2000. + Permission to copy, use, modify, sell and distribute this document is + granted provided this copyright notice appears in all copies. This + document is provided “as is” without express or implied + warranty, and with no claim as to its suitability for any purpose. +

+ Updated: Nov 26, 2000 +

+ diff --git a/doc/template.html b/doc/template.html new file mode 100644 index 00000000..1d0cd7a4 --- /dev/null +++ b/doc/template.html @@ -0,0 +1,26 @@ + + + The Title Of This Page + +
+

+ c++boost.gif (8819 bytes)The Title Of This + Page +

+

+

+ Prev: Previous + Next: Next + Up: Top +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +

+ Updated: Nov 26, 2000 +

+ diff --git a/doc/under-the-hood.html b/doc/under-the-hood.html new file mode 100644 index 00000000..288021cf --- /dev/null +++ b/doc/under-the-hood.html @@ -0,0 +1,62 @@ + + + + A Peek Under the Hood + +

+ c++boost.gif (8819 bytes) +

+

+ A Peek Under the Hood +

+

+ Declaring a class_builder<T> causes the instantiation + of an extension_class<T> to which it forwards all + member function calls and which is doing most of the real work. + extension_class<T> is a subclass of + PyTypeObject, the struct which Python's 'C' API uses + to describe a type. An instance of the + extension_class<> becomes the Python type object + corresponding to hello::world. When we add it to the module it goes into the + module's dictionary to be looked up under the name "world". +

+ Py_cpp uses C++'s template argument deduction mechanism to determine the + types of arguments to functions (except constructors, for which we must + provide an argument list + because they can't be named in C++). Then, it calls the appropriate + overloaded functions PyObject* + to_python(S) and + S'from_python(PyObject*, + type<S>) which convert between any C++ + type S and a PyObject*, the type which represents a + reference to any Python object in its 'C' API. The extension_class<T> + template defines a whole raft of these conversions (for T, T*, + T&, std::auto_ptr<T>, etc.), using the same inline + friend function technique employed by the boost operators + library. +

+ Because the to_python and from_python functions + for a user-defined class are defined by + extension_class<T>, it is important that an instantiation of + extension_class<T> is visible to any code which wraps + a C++ function with a T, T*, const T&, etc. parameter or + return value. In particular, you may want to create all of the classes at + the top of your module's init function, then def the member + functions later to avoid problems with inter-class dependencies. +

+ Previous: Function Overloading + Next: Building a Module with Py_cpp + Up: Top +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability for + any purpose. +

+ Updated: Nov 26, 2000 + diff --git a/example/example1.cpp b/example/example1.cpp new file mode 100644 index 00000000..467ac0dc --- /dev/null +++ b/example/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 + boost::python::module_builder hello("hello"); + + // Create the Python type object for our extension class + boost::python::class_builder world_class(hello, "world"); + + // Add the __init__ function + world_class.def(boost::python::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(...) + { + boost::python::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/example/example1.py b/example/example1.py new file mode 100644 index 00000000..0e3a9a18 --- /dev/null +++ b/example/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/include/boost/python/callback.hpp b/include/boost/python/callback.hpp new file mode 100644 index 00000000..7240b5b7 --- /dev/null +++ b/include/boost/python/callback.hpp @@ -0,0 +1,829 @@ +// (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 10-argument python callbacks by gen_callback.python + +#ifndef CALLBACK_DWA_052100_H_ +# define CALLBACK_DWA_052100_H_ + +# include +# include + +namespace boost { namespace python { + +namespace detail { + template + inline void callback_adjust_refcount(PyObject*, type) {} + + inline void callback_adjust_refcount(PyObject* p, type) + { Py_INCREF(p); } +} + +// Calling Python from C++ +template +struct callback +{ + static R call_method(PyObject* self, const char* name) + { + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("()"))); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + static R call(PyObject* self) + { + ref result(PyEval_CallFunction(self, const_cast("()"))); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call_method(PyObject* self, const char* name, const A1& a1) + { + ref p1(to_python(a1)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(O)"), + p1.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call(PyObject* self, const A1& a1) + { + ref p1(to_python(a1)); + ref result(PyEval_CallFunction(self, const_cast("(O)"), + p1.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call_method(PyObject* self, const char* name, const A1& a1, const A2& a2) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OO)"), + p1.get(), + p2.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call(PyObject* self, const A1& a1, const A2& a2) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref result(PyEval_CallFunction(self, const_cast("(OO)"), + p1.get(), + p2.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call_method(PyObject* self, const char* name, const A1& a1, const A2& a2, const A3& a3) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOO)"), + p1.get(), + p2.get(), + p3.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call(PyObject* self, const A1& a1, const A2& a2, const A3& a3) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref result(PyEval_CallFunction(self, const_cast("(OOO)"), + p1.get(), + p2.get(), + p3.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call_method(PyObject* self, const char* name, const A1& a1, const A2& a2, const A3& a3, const A4& a4) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref result(PyEval_CallFunction(self, const_cast("(OOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), 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) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), 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, const A6& a6) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), 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, const A6& a6, const A7& a7) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), 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, const A6& a6, const A7& a7, const A8& a8) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), 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, const A6& a6, const A7& a7, const A8& a8, const A9& a9) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref p9(to_python(a9)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get(), + p9.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref p9(to_python(a9)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get(), + p9.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), 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, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref p9(to_python(a9)); + ref p10(to_python(a10)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get(), + p9.get(), + p10.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + + template + static R call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref p9(to_python(a9)); + ref p10(to_python(a10)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get(), + p9.get(), + p10.get())); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), 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) + { + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("()"))); + } + + static void call(PyObject* self) + { + ref result(PyEval_CallFunction(self, const_cast("()"))); + } + + template + static void call_method(PyObject* self, const char* name, const A1& a1) + { + ref p1(to_python(a1)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(O)"), + p1.get())); + } + + template + static void call(PyObject* self, const A1& a1) + { + ref p1(to_python(a1)); + ref result(PyEval_CallFunction(self, const_cast("(O)"), + p1.get())); + } + + template + static void call_method(PyObject* self, const char* name, const A1& a1, const A2& a2) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OO)"), + p1.get(), + p2.get())); + } + + template + static void call(PyObject* self, const A1& a1, const A2& a2) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref result(PyEval_CallFunction(self, const_cast("(OO)"), + p1.get(), + p2.get())); + } + + template + static void call_method(PyObject* self, const char* name, const A1& a1, const A2& a2, const A3& a3) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOO)"), + p1.get(), + p2.get(), + p3.get())); + } + + template + static void call(PyObject* self, const A1& a1, const A2& a2, const A3& a3) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref result(PyEval_CallFunction(self, const_cast("(OOO)"), + p1.get(), + p2.get(), + p3.get())); + } + + template + static void call_method(PyObject* self, const char* name, const A1& a1, const A2& a2, const A3& a3, const A4& a4) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get())); + } + + template + static void call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref result(PyEval_CallFunction(self, const_cast("(OOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get())); + } + + 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) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get())); + } + + template + static void call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get())); + } + + 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, const A6& a6) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get())); + } + + template + static void call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get())); + } + + 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, const A6& a6, const A7& a7) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get())); + } + + template + static void call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get())); + } + + 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, const A6& a6, const A7& a7, const A8& a8) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get())); + } + + template + static void call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get())); + } + + 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, const A6& a6, const A7& a7, const A8& a8, const A9& a9) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref p9(to_python(a9)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get(), + p9.get())); + } + + template + static void call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref p9(to_python(a9)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get(), + p9.get())); + } + + 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, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref p9(to_python(a9)); + ref p10(to_python(a10)); + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(OOOOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get(), + p9.get(), + p10.get())); + } + + template + static void call(PyObject* self, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10) + { + ref p1(to_python(a1)); + ref p2(to_python(a2)); + ref p3(to_python(a3)); + ref p4(to_python(a4)); + ref p5(to_python(a5)); + ref p6(to_python(a6)); + ref p7(to_python(a7)); + ref p8(to_python(a8)); + ref p9(to_python(a9)); + ref p10(to_python(a10)); + ref result(PyEval_CallFunction(self, const_cast("(OOOOOOOOOO)"), + p1.get(), + p2.get(), + p3.get(), + p4.get(), + p5.get(), + p6.get(), + p7.get(), + p8.get(), + p9.get(), + p10.get())); + } +}; + +// Make it a compile-time error to try to return a const char* from a virtual +// function. The standard conversion +// +// from_python(PyObject* string, boost::python::type) +// +// returns a pointer to the character array which is internal to string. The +// problem with trying to do this in a standard callback function is that the +// Python string would likely be destroyed upon return from the calling function +// (boost::python::callback::call[_method]) when its reference count is +// decremented. If you absolutely need to do this and you're sure it's safe (it +// usually isn't), you can use +// +// boost::python::string result(boost::python::callback::call[_method](...args...)); +// ...result.c_str()... // access the char* array +template <> +struct callback +{ + // Try hard to generate a readable error message + typedef struct unsafe_since_python_string_may_be_destroyed {} call, call_method; +}; + +}} // namespace boost::python + +#endif // CALLBACK_DWA_052100_H_ diff --git a/include/boost/python/caller.hpp b/include/boost/python/caller.hpp new file mode 100644 index 00000000..35b2d618 --- /dev/null +++ b/include/boost/python/caller.hpp @@ -0,0 +1,1279 @@ +// (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 10-argument member functions and 11-argument free +// functions by gen_caller.python + +#ifndef CALLER_DWA05090_H_ +# define CALLER_DWA05090_H_ + +# include +# include +# include +# include +# include + +namespace boost { namespace python { + +// 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)(A1, A2, A3, A4, A5, A6), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6)) + 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()), + from_python(a6, type()))); + } + + template + static PyObject* call(R (T::*pmf)(A1, A2, A3, A4, A5, A6, A7), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7)) + 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()), + from_python(a6, type()), + from_python(a7, type()))); + } + + template + static PyObject* call(R (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()))); + } + + template + static PyObject* call(R (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8, A9), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type()))); + } + + template + static PyObject* call(R (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + PyObject* a10; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type()), + from_python(a10, 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()))); + } + + template + static PyObject* call(R (T::*pmf)(A1, A2, A3, A4, A5, A6) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6)) + 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()), + from_python(a6, type()))); + } + + template + static PyObject* call(R (T::*pmf)(A1, A2, A3, A4, A5, A6, A7) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7)) + 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()), + from_python(a6, type()), + from_python(a7, type()))); + } + + template + static PyObject* call(R (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()))); + } + + template + static PyObject* call(R (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type()))); + } + + template + static PyObject* call(R (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + PyObject* a10; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type()), + from_python(a10, 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 + static PyObject* call(R (*f)(A1, A2, A3, A4, A5, A6), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + if (!PyArg_ParseTuple(args, const_cast("OOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6)) + 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()), + from_python(a6, type()))); + } + + template + static PyObject* call(R (*f)(A1, A2, A3, A4, A5, A6, A7), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7)) + 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()), + from_python(a6, type()), + from_python(a7, type()))); + } + + template + static PyObject* call(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()))); + } + + template + static PyObject* call(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type()))); + } + + template + static PyObject* call(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + PyObject* a10; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type()), + from_python(a10, type()))); + } + + template + static PyObject* call(R (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + PyObject* a10; + PyObject* a11; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type()), + from_python(a10, type()), + from_python(a11, 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 detail::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 detail::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 detail::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 detail::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 detail::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 detail::none(); + } + + template + static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5, A6), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6)) + 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()), + from_python(a6, type())); + return detail::none(); + } + + template + static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5, A6, A7), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7)) + 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()), + from_python(a6, type()), + from_python(a7, type())); + return detail::none(); + } + + template + static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type())); + return detail::none(); + } + + template + static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8, A9), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type())); + return detail::none(); + } + + template + static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10), PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + PyObject* a10; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type()), + from_python(a10, type())); + return detail::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 detail::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 detail::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 detail::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 detail::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 detail::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 detail::none(); + } + + template + static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5, A6) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6)) + 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()), + from_python(a6, type())); + return detail::none(); + } + + template + static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5, A6, A7) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7)) + 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()), + from_python(a6, type()), + from_python(a7, type())); + return detail::none(); + } + + template + static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type())); + return detail::none(); + } + + template + static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type())); + return detail::none(); + } + + template + static PyObject* call(void (T::*pmf)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const, PyObject* args, PyObject* /* keywords */ ) { + PyObject* self; + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + PyObject* a10; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOOO"), &self, &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10)) + 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()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type()), + from_python(a10, type())); + return detail::none(); + } + + + // Free functions + static PyObject* call(void (*f)(), PyObject* args, PyObject* /* keywords */ ) { + if (!PyArg_ParseTuple(args, const_cast(""))) + return 0; + f(); + return detail::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 detail::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 detail::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 detail::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 detail::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 detail::none(); + } + + template + static PyObject* call(void (*f)(A1, A2, A3, A4, A5, A6), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + if (!PyArg_ParseTuple(args, const_cast("OOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6)) + return 0; + f(from_python(a1, type()), + from_python(a2, type()), + from_python(a3, type()), + from_python(a4, type()), + from_python(a5, type()), + from_python(a6, type())); + return detail::none(); + } + + template + static PyObject* call(void (*f)(A1, A2, A3, A4, A5, A6, A7), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7)) + return 0; + f(from_python(a1, type()), + from_python(a2, type()), + from_python(a3, type()), + from_python(a4, type()), + from_python(a5, type()), + from_python(a6, type()), + from_python(a7, type())); + return detail::none(); + } + + template + static PyObject* call(void (*f)(A1, A2, A3, A4, A5, A6, A7, A8), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8)) + return 0; + f(from_python(a1, type()), + from_python(a2, type()), + from_python(a3, type()), + from_python(a4, type()), + from_python(a5, type()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type())); + return detail::none(); + } + + template + static PyObject* call(void (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9)) + return 0; + f(from_python(a1, type()), + from_python(a2, type()), + from_python(a3, type()), + from_python(a4, type()), + from_python(a5, type()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type())); + return detail::none(); + } + + template + static PyObject* call(void (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + PyObject* a10; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10)) + return 0; + f(from_python(a1, type()), + from_python(a2, type()), + from_python(a3, type()), + from_python(a4, type()), + from_python(a5, type()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type()), + from_python(a10, type())); + return detail::none(); + } + + template + static PyObject* call(void (*f)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11), PyObject* args, PyObject* /* keywords */ ) { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + PyObject* a10; + PyObject* a11; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11)) + return 0; + f(from_python(a1, type()), + from_python(a2, type()), + from_python(a3, type()), + from_python(a4, type()), + from_python(a5, type()), + from_python(a6, type()), + from_python(a7, type()), + from_python(a8, type()), + from_python(a9, type()), + from_python(a10, type()), + from_python(a11, type())); + return detail::none(); + } + +}; + +}} // namespace boost::python + +#endif diff --git a/include/boost/python/class_builder.hpp b/include/boost/python/class_builder.hpp new file mode 100644 index 00000000..9e88f049 --- /dev/null +++ b/include/boost/python/class_builder.hpp @@ -0,0 +1,156 @@ +#ifndef CLASS_WRAPPER_DWA101000_H_ +# define CLASS_WRAPPER_DWA101000_H_ + +#include +#include +#include +#include +#include +#include + +namespace boost { namespace python { + +// Syntactic sugar to make wrapping classes more convenient +template > +class class_builder + : python_extension_class_converters // Works around MSVC6.x/GCC2.95.2 bug described below +{ + public: + class_builder(module_builder& module, const char* name) + : m_class(new detail::extension_class(name)) + { + module.add(ref(as_object(m_class.get()), ref::increment_count), name); + } + + ~class_builder() + {} + + // define constructors + template + void def(const signature& signature) + { m_class->def(signature); } + + // export heterogeneous reverse-argument operators + // (type of lhs: 'left', of rhs: 'right') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub), Foo>(), + // boost::python::left_operand()); + template + void def(operators o1, left_operand o2) + { m_class->def(o1, o2); } + + // export heterogeneous operators (type of lhs: 'left', of rhs: 'right') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub), Foo>(), + // boost::python::right_operand()); + template + void def(operators o1, right_operand o2) + { m_class->def(o1, o2); } + + // define a function that passes Python arguments and keywords + // to C++ verbatim (as a 'tuple const &' and 'dictionary const &' + // respectively). This is useful for manual argument passing. + // It's also the only possibility to pass keyword arguments to C++. + // Fn must have a signatur that is compatible to + // PyObject * (*)(PyObject * aTuple, PyObject * aDictionary) + template + void def_raw(Fn fn, const char* name) + { m_class->def_raw(fn, name); } + + // 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 or reference), 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 obj + 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 obj + template + void def_setter(MemberType T::*pm, const char* name) + { m_class->def_getter(pm, name); } + + // Expose the given member (pm) of the T obj 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 obj as a read/write attribute + template + void def_read_write(MemberType T::*pm, const char* name) + { m_class->def_read_write(pm, name); } + + // define the standard coercion needed for operator overloading + void def_standard_coerce() + { m_class->def_standard_coerce(); } + + // declare the given class a base class of this one and register + // conversion functions + template + void declare_base(class_builder const & base) + { + m_class->declare_base(base.get_extension_class()); + } + + // declare the given class a base class of this one and register + // upcast conversion function + template + void declare_base(class_builder const & base, without_downcast_t) + { + m_class->declare_base(base.get_extension_class(), without_downcast); + } + + // get the embedded ExtensioClass object + detail::extension_class * get_extension_class() const + { + return m_class.get(); + } + + // set an arbitrary attribute. Useful for non-function class data members, + // e.g. enums + void add(PyObject* x, const char* name) + { m_class->set_attribute(name, x); } + void add(ref x, const char* name) + { m_class->set_attribute(name, x); } + private: + // declare the given class a base class of this one and register + // conversion functions + template + void declare_base(detail::extension_class * base) + { + m_class->declare_base(base); + } + + // declare the given class a base class of this one and register + // upcast conversion function + template + void declare_base(detail::extension_class * base, without_downcast_t) + { + m_class->declare_base(base, without_downcast); + } + + reference > m_class; +}; + +// 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. + +}} // namespace boost::python + +#endif // CLASS_WRAPPER_DWA101000_H_ diff --git a/include/boost/python/classes.hpp b/include/boost/python/classes.hpp new file mode 100644 index 00000000..d38fbba5 --- /dev/null +++ b/include/boost/python/classes.hpp @@ -0,0 +1,527 @@ +// (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 +# include +# include +# include +# include +# include +# include + +namespace boost { namespace python { + +// A simple type which acts something like a built-in Python class obj. +class instance + : public boost::python::detail::python_object +{ + public: + instance(PyTypeObject* class_); + ~instance(); + + // 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); + + // Number methods + PyObject* add(PyObject* other); + PyObject* subtract(PyObject* other); + PyObject* multiply(PyObject* other); + PyObject* divide(PyObject* other); + PyObject* remainder(PyObject* other); + PyObject* divmod(PyObject* other); + PyObject* power(PyObject*, PyObject*); + PyObject* negative(); + PyObject* positive(); + PyObject* absolute(); + int nonzero(); + PyObject* invert(); + PyObject* lshift(PyObject* other); + PyObject* rshift(PyObject* other); + PyObject* do_and(PyObject* other); + PyObject* do_xor(PyObject* other); + PyObject* do_or(PyObject* other); + int coerce(PyObject**, PyObject**); + PyObject* as_int(); + PyObject* as_long(); + PyObject* as_float(); + PyObject* oct(); + PyObject* hex(); + + private: // noncopyable, without the size bloat + instance(const instance&); + void operator=(const instance&); + + private: // helper functions + int setattr_dict(PyObject* value); + + private: + dictionary m_name_space; +}; + +template class meta_class; + +namespace detail { + class class_base : public type_object_base + { + public: + class_base(PyTypeObject* meta_class_obj, string name, tuple bases, const dictionary& name_space); + tuple bases() const; + string name() const; + dictionary& dict(); + + // Standard Python functions. + PyObject* getattr(const char* name); + int setattr(const char* name, PyObject* value); + PyObject* repr() const; + void add_base(ref base); + + protected: + bool initialize_instance(instance* obj, PyObject* args, PyObject* keywords); + + private: // virtual functions + // Subclasses should override this to delete the particular obj type + virtual void delete_instance(PyObject*) const = 0; + + private: // boost::python::type_object_base required interface implementation + void instance_dealloc(PyObject*) const; // subclasses should not override this + + private: + string m_name; + tuple m_bases; + dictionary m_name_space; + }; + + void enable_named_method(class_base* type_obj, const char* name); +} + +// A type which acts a lot like a built-in Python class. T is the obj type, +// so class_t is a very simple "class-alike". +template +class class_t + : public boost::python::detail::class_base +{ + public: + class_t(meta_class* meta_class_obj, string name, tuple bases, const dictionary& name_space); + + // Standard Python functions. + PyObject* call(PyObject* args, PyObject* keywords); + + 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* obj, int n) const; + int instance_sequence_ass_item(PyObject* obj, 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: // Implement number methods on instances + PyObject* instance_number_add(PyObject*, PyObject*) const; + PyObject* instance_number_subtract(PyObject*, PyObject*) const; + PyObject* instance_number_multiply(PyObject*, PyObject*) const; + PyObject* instance_number_divide(PyObject*, PyObject*) const; + PyObject* instance_number_remainder(PyObject*, PyObject*) const; + PyObject* instance_number_divmod(PyObject*, PyObject*) const; + PyObject* instance_number_power(PyObject*, PyObject*, PyObject*) const; + PyObject* instance_number_negative(PyObject*) const; + PyObject* instance_number_positive(PyObject*) const; + PyObject* instance_number_absolute(PyObject*) const; + int instance_number_nonzero(PyObject*) const; + PyObject* instance_number_invert(PyObject*) const; + PyObject* instance_number_lshift(PyObject*, PyObject*) const; + PyObject* instance_number_rshift(PyObject*, PyObject*) const; + PyObject* instance_number_and(PyObject*, PyObject*) const; + PyObject* instance_number_xor(PyObject*, PyObject*) const; + PyObject* instance_number_or(PyObject*, PyObject*) const; + int instance_number_coerce(PyObject*, PyObject**, PyObject**) const; + PyObject* instance_number_int(PyObject*) const; + PyObject* instance_number_long(PyObject*) const; + PyObject* instance_number_float(PyObject*) const; + PyObject* instance_number_oct(PyObject*) const; + PyObject* instance_number_hex(PyObject*) const; + + private: // Miscellaneous "special" methods + PyObject* instance_call(PyObject* obj, PyObject* args, PyObject* keywords) const; + PyObject* instance_getattr(PyObject* obj, const char* name) const; + int instance_setattr(PyObject* obj, const char* name, PyObject* value) const; + + private: // Implementation of boost::python::detail::class_base required interface + void delete_instance(PyObject*) const; + + private: // noncopyable, without the size bloat + class_t(const class_t&); + void operator=(const class_t&); +}; + +// The type of a class_t object. +template +class meta_class + : public boost::python::detail::reprable< + boost::python::detail::callable< + boost::python::detail::getattrable< + boost::python::detail::setattrable< + boost::python::detail::type_object > > > > >, + boost::noncopyable +{ + public: + meta_class(); + + // Standard Python functions. + PyObject* call(PyObject* args, PyObject* keywords); + + struct type_object + : boost::python::detail::singleton > > + { + type_object() : singleton_base(&PyType_Type) {} + }; +}; + +// +// Member function implementations. +// +template +meta_class::meta_class() + : properties(type_object::instance()) +{ +} + +template +class_t::class_t(meta_class* meta_class_obj, string name, tuple bases, const dictionary& name_space) + : boost::python::detail::class_base(meta_class_obj, name, bases, name_space) +{ +} + +template +void class_t::delete_instance(PyObject* obj) const +{ + delete downcast(obj); +} + +template +PyObject* class_t::call(PyObject* args, PyObject* keywords) +{ + reference result(new T(this)); + if (!this->initialize_instance(result.get(), args, keywords)) + return 0; + else + return result.release(); +} + +template +PyObject* class_t::instance_repr(PyObject* obj) const +{ + return downcast(obj)->repr(); +} + +template +int class_t::instance_compare(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->compare(other); +} + +template +PyObject* class_t::instance_str(PyObject* obj) const +{ + return downcast(obj)->str(); +} + +template +long class_t::instance_hash(PyObject* obj) const +{ + return downcast(obj)->hash(); +} + +template +int class_t::instance_mapping_length(PyObject* obj) const +{ + return downcast(obj)->length(); +} + +template +int class_t::instance_sequence_length(PyObject* obj) const +{ + return downcast(obj)->length(); +} + +template +PyObject* class_t::instance_mapping_subscript(PyObject* obj, PyObject* key) const +{ + return downcast(obj)->get_subscript(key); +} + +template +PyObject* class_t::instance_sequence_item(PyObject* obj, int n) const +{ + ref key(to_python(n)); + return downcast(obj)->get_subscript(key.get()); +} + +template +int class_t::instance_sequence_ass_item(PyObject* obj, int n, PyObject* value) const +{ + ref key(to_python(n)); + downcast(obj)->set_subscript(key.get(), value); + return 0; +} + +template +int class_t::instance_mapping_ass_subscript(PyObject* obj, PyObject* key, PyObject* value) const +{ + downcast(obj)->set_subscript(key, value); + return 0; +} + +void adjust_slice_indices(PyObject* obj, int& start, int& finish); + +template +PyObject* class_t::instance_sequence_slice(PyObject* obj, int start, int finish) const +{ + adjust_slice_indices(obj, start, finish); + return downcast(obj)->get_slice(start, finish); +} + +template +int class_t::instance_sequence_ass_slice(PyObject* obj, int start, int finish, PyObject* value) const +{ + adjust_slice_indices(obj, start, finish); + downcast(obj)->set_slice(start, finish, value); + return 0; +} + +template +PyObject* class_t::instance_call(PyObject* obj, PyObject* args, PyObject* keywords) const +{ + return downcast(obj)->call(args, keywords); +} + +template +PyObject* class_t::instance_getattr(PyObject* obj, const char* name) const +{ + return downcast(obj)->getattr(name); +} + + +template +int class_t::instance_setattr(PyObject* obj, const char* name, PyObject* value) const +{ + return downcast(obj)->setattr(name, value); +} + +template +PyObject* class_t::instance_number_add(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->add(other); +} + +template +PyObject* class_t::instance_number_subtract(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->subtract(other); +} + +template +PyObject* class_t::instance_number_multiply(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->multiply(other); +} + +template +PyObject* class_t::instance_number_divide(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->divide(other); +} + +template +PyObject* class_t::instance_number_remainder(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->remainder(other); +} + +template +PyObject* class_t::instance_number_divmod(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->divmod(other); +} + +template +PyObject* class_t::instance_number_power(PyObject* obj, PyObject* exponent, PyObject* modulus) const +{ + return downcast(obj)->power(exponent, modulus); +} + +template +PyObject* class_t::instance_number_negative(PyObject* obj) const +{ + return downcast(obj)->negative(); +} + +template +PyObject* class_t::instance_number_positive(PyObject* obj) const +{ + return downcast(obj)->positive(); +} + +template +PyObject* class_t::instance_number_absolute(PyObject* obj) const +{ + return downcast(obj)->absolute(); +} + +template +int class_t::instance_number_nonzero(PyObject* obj) const +{ + return downcast(obj)->nonzero(); +} + +template +PyObject* class_t::instance_number_invert(PyObject* obj) const +{ + return downcast(obj)->invert(); +} + +template +PyObject* class_t::instance_number_lshift(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->lshift(other); +} + +template +PyObject* class_t::instance_number_rshift(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->rshift(other); +} + +template +PyObject* class_t::instance_number_and(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->do_and(other); +} + +template +PyObject* class_t::instance_number_xor(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->do_xor(other); +} + +template +PyObject* class_t::instance_number_or(PyObject* obj, PyObject* other) const +{ + return downcast(obj)->do_or(other); +} + +template +int class_t::instance_number_coerce(PyObject* obj, PyObject** x, PyObject** y) const +{ + return downcast(obj)->coerce(x, y); +} + +template +PyObject* class_t::instance_number_int(PyObject* obj) const +{ + return downcast(obj)->as_int(); +} + +template +PyObject* class_t::instance_number_long(PyObject* obj) const +{ + return downcast(obj)->as_long(); +} + +template +PyObject* class_t::instance_number_float(PyObject* obj) const +{ + return downcast(obj)->as_float(); +} + +template +PyObject* class_t::instance_number_oct(PyObject* obj) const +{ + return downcast(obj)->oct(); +} + +template +PyObject* class_t::instance_number_hex(PyObject* obj) const +{ + return downcast(obj)->hex(); +} + +namespace detail { + inline dictionary& class_base::dict() + { + return m_name_space; + } + + inline tuple class_base::bases() const + { + return m_bases; + } +} + +template +PyObject* meta_class::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_t(this, string(ref(name, ref::increment_count)), + tuple(ref(bases, ref::increment_count)), + dictionary(ref(name_space, ref::increment_count))) + ); +} + +namespace detail { + const string& setattr_string(); + const string& getattr_string(); + const string& delattr_string(); + + inline string class_base::name() const + { + return m_name; + } +} + + +}} // namespace boost::python +#endif diff --git a/include/boost/python/conversions.hpp b/include/boost/python/conversions.hpp new file mode 100644 index 00000000..d11f7f14 --- /dev/null +++ b/include/boost/python/conversions.hpp @@ -0,0 +1,325 @@ +// (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 +# include +# include +# include +# include +# include +# include + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // this is a gcc 2.95.2 bug workaround + +// This can be instantiated on an enum to provide the to_python/from_python +// conversions, provided the values can fit in a long. +template +class py_enum_as_int_converters +{ + friend EnumType from_python(PyObject* x, boost::python::type) + { + return static_cast( + from_python(x, boost::python::type())); + } + + friend EnumType from_python(PyObject* x, boost::python::type) + { + return static_cast( + from_python(x, boost::python::type())); + } + + friend PyObject* to_python(EnumType x) + { + return to_python(static_cast(x)); + } +}; +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +namespace boost { namespace python { +template class enum_as_int_converters + : public BOOST_PYTHON_CONVERSION::py_enum_as_int_converters {}; + +template class wrapped_pointer; + +//#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)); +} + +}} // namespace boost::python + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE +// +// Converters +// +PyObject* to_python(long); +long from_python(PyObject* p, boost::python::type); +long from_python(PyObject* p, boost::python::type); + +PyObject* to_python(unsigned long); +unsigned long from_python(PyObject* p, boost::python::type); +unsigned long from_python(PyObject* p, boost::python::type); + +PyObject* to_python(int); +int from_python(PyObject*, boost::python::type); +int from_python(PyObject*, boost::python::type); + +PyObject* to_python(unsigned int); +unsigned int from_python(PyObject*, boost::python::type); +unsigned int from_python(PyObject*, boost::python::type); + +PyObject* to_python(short); +short from_python(PyObject*, boost::python::type); +short from_python(PyObject*, boost::python::type); + +PyObject* to_python(unsigned short); +unsigned short from_python(PyObject*, boost::python::type); +unsigned short from_python(PyObject*, boost::python::type); + +PyObject* to_python(signed char); +signed char from_python(PyObject*, boost::python::type); +signed char from_python(PyObject*, boost::python::type); + +PyObject* to_python(unsigned char); +unsigned char from_python(PyObject*, boost::python::type); +unsigned char from_python(PyObject*, boost::python::type); + +PyObject* to_python(float); +float from_python(PyObject*, boost::python::type); +float from_python(PyObject*, boost::python::type); + +PyObject* to_python(double); +double from_python(PyObject*, boost::python::type); +double from_python(PyObject*, boost::python::type); + +PyObject* to_python(bool); +bool from_python(PyObject*, boost::python::type); +bool from_python(PyObject*, boost::python::type); + +PyObject* to_python(void); +void from_python(PyObject*, boost::python::type); + +PyObject* to_python(const char* s); +const char* from_python(PyObject*, boost::python::type); + +PyObject* to_python(const std::string& s); +std::string from_python(PyObject*, boost::python::type); +std::string from_python(PyObject*, boost::python::type); + +// For when your C++ function really wants to pass/return a PyObject* +PyObject* to_python(PyObject*); +PyObject* from_python(PyObject*, boost::python::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 +// wrapped_pointer 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 wrapped_type. +// + +// 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 boost { namespace python { +// #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 +// }} // namespace boost::python + +#if !defined(BOOST_MSVC6_OR_EARLIER) +template +boost::shared_ptr from_python(PyObject*p, boost::python::type >) +{ + return smart_ptr_from_python(p, boost::python::type >(), boost::python::type()); +} +#endif + +#if 0 +template +PyObject* to_python(std::auto_ptr p) +{ + return new boost::python::wrapped_pointer, T>(p); +} + +template +PyObject* to_python(boost::shared_ptr p) +{ + return new boost::python::wrapped_pointer, T>(p); +} +#endif + +// +// inline implementations +// + +#ifndef BOOST_MSVC6_OR_EARLIER +inline PyObject* to_python(double d) +{ + return PyFloat_FromDouble(d); +} + +inline PyObject* to_python(float f) +{ + return PyFloat_FromDouble(f); +} +#endif // BOOST_MSVC6_OR_EARLIER + +inline PyObject* to_python(long l) +{ + return PyInt_FromLong(l); +} + +inline PyObject* to_python(int x) +{ + return PyInt_FromLong(x); +} + +inline PyObject* to_python(short x) +{ + return PyInt_FromLong(x); +} + +inline PyObject* to_python(bool b) +{ + return PyInt_FromLong(b); +} + +inline PyObject* to_python(void) +{ + return boost::python::detail::none(); +} + +inline PyObject* to_python(const char* s) +{ + return PyString_FromString(s); +} + +inline std::string from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline PyObject* to_python(PyObject* p) +{ + Py_INCREF(p); + return p; +} + +inline PyObject* from_python(PyObject* p, boost::python::type) +{ + return p; +} + +inline const char* from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline double from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline float from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline int from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline short from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline long from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline bool from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline unsigned int from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline unsigned short from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline signed char from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline unsigned char from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +inline unsigned long from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +#endif // METHOD_DWA122899_H_ diff --git a/include/boost/python/detail/base_object.hpp b/include/boost/python/detail/base_object.hpp new file mode 100644 index 00000000..f8ed665b --- /dev/null +++ b/include/boost/python/detail/base_object.hpp @@ -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 +# include // really just for type<> +# include +# include + +namespace boost { namespace python { namespace detail { + +// base_object - adds a constructor and non-virtual destructor to a +// base Python type (e.g. PyObject, PyTypeObject). +template +struct base_object : python_type +{ + typedef python_type base_python_type; + + // Initializes type and reference count. All other fields of base_python_type are 0 + base_object(PyTypeObject* type_obj); + + // Decrements reference count on the type + ~base_object(); +}; + +// Easy typedefs for common usage +typedef base_object python_object; +typedef base_object python_type; + + +// +// class_t template member function implementations +// +template +base_object::base_object(PyTypeObject* type_obj) +{ + base_python_type* bp = this; +#if !defined(_MSC_VER) || defined(__STLPORT) + std:: +#endif + memset(bp, 0, sizeof(base_python_type)); + ob_refcnt = 1; + ob_type = type_obj; + Py_INCREF(type_obj); +} + +template +inline base_object::~base_object() +{ + Py_DECREF(ob_type); +} + +}}} // namespace boost::python::detail + +#endif // BASE_OBJECT_DWA051600_H_ diff --git a/include/boost/python/detail/cast.hpp b/include/boost/python/detail/cast.hpp new file mode 100644 index 00000000..a6f2f046 --- /dev/null +++ b/include/boost/python/detail/cast.hpp @@ -0,0 +1,81 @@ +// (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 +# include + +namespace boost { namespace python { + +namespace detail { + // The default way of converting a PyObject* or PyTypeObject* to a T* + template + struct downcast_traits + { + 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; + } +} // namespace detail + +// 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(detail::downcast_traits::cast(detail::as_base_object((T*)0, p))) + {} + + downcast(const PyObject* p) + : m_p(detail::downcast_traits::cast(detail::as_base_object((const T*)0, p))) + {} + + downcast(PyTypeObject* p) + : m_p(detail::downcast_traits::cast(p)) + {} + + downcast(const PyTypeObject* p) + : m_p(detail::downcast_traits::cast(p)) + {} + + operator T*() const { return m_p; } + T* get() const { return m_p; } + T& operator*() const { return *m_p; } + private: + T* m_p; +}; + +}} // namespace boost::python + +#endif // CAST_DWA052500_H_ diff --git a/include/boost/python/detail/config.hpp b/include/boost/python/detail/config.hpp new file mode 100644 index 00000000..2488bd8e --- /dev/null +++ b/include/boost/python/detail/config.hpp @@ -0,0 +1,56 @@ +// (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 + // A gcc bug forces some symbols into the global namespace +# define BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE +# define BOOST_PYTHON_END_CONVERSION_NAMESPACE +# define BOOST_PYTHON_CONVERSION +# define BOOST_PYTHON_IMPORT_CONVERSION(x) using ::x +# else +# define BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE namespace boost { namespace python { +# define BOOST_PYTHON_END_CONVERSION_NAMESPACE }} // namespace boost::python +# define BOOST_PYTHON_CONVERSION python +# define BOOST_PYTHON_IMPORT_CONVERSION(x) void never_defined() // so we can follow the macro with a ';' +# endif + +# if defined(BOOST_MSVC) +# if _MSC_VER <= 1200 +# define BOOST_MSVC6_OR_EARLIER 1 +# endif + +# pragma warning (disable : 4786) + +# endif + +// Work around the broken library implementation/strict ansi checking on some +// EDG-based compilers (e.g. alpha), which incorrectly warn that the result of +// offsetof() is not an integer constant expression. +# if defined(__DECCXX_VER) && __DECCXX_VER <= 60290024 +# define BOOST_OFFSETOF(s_name, s_member) \ + ((size_t)__INTADDR__(&(((s_name *)0)->s_member))) +# else +# define BOOST_OFFSETOF(s_name, s_member) \ + offsetof(s_name, s_member) +# endif + +// The STLport puts all of the standard 'C' library names in std (as far as the +// user is concerned), but without it you need a fix if you're using MSVC. +# if defined(BOOST_MSVC6_OR_EARLIER) && !defined(__STLPORT) +# define BOOST_CSTD_ +# else +# define BOOST_CSTD_ std +# endif + +#endif // CONFIG_DWA052200_H_ diff --git a/include/boost/python/detail/extension_class.hpp b/include/boost/python/detail/extension_class.hpp new file mode 100644 index 00000000..20d48796 --- /dev/null +++ b/include/boost/python/detail/extension_class.hpp @@ -0,0 +1,834 @@ +// (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.python + +#ifndef EXTENSION_CLASS_DWA052000_H_ +# define EXTENSION_CLASS_DWA052000_H_ + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +namespace boost { namespace python { + +// forward declarations +template struct operators; +template struct left_operand; +template struct right_operand; + +enum without_downcast_t { without_downcast }; + +namespace detail { + +// forward declarations +class extension_instance; +class extension_class_base; +template class instance_holder; +template class instance_value_holder; +template class instance_ptr_holder; +template struct operand_select; + template struct choose_op; + template struct choose_rop; + template struct choose_unary_op; + template struct define_operator; + +meta_class* extension_meta_class(); +extension_instance* get_extension_instance(PyObject* p); +void report_missing_instance_data(extension_instance*, class_t*, const std::type_info&); +void report_missing_ptr_data(extension_instance*, class_t*, const std::type_info&); +void report_missing_class_object(const std::type_info&); +void report_released_smart_pointer(const std::type_info&); + +template +T* check_non_null(T* p) +{ + if (p == 0) + report_released_smart_pointer(typeid(T)); + return p; +} + +template class held_instance; + +typedef void* (*conversion_function_ptr)(void*); + +struct base_class_info +{ + base_class_info(extension_class_base* t, conversion_function_ptr f) + :class_object(t), convert(f) + {} + + extension_class_base* class_object; + conversion_function_ptr convert; +}; + +typedef base_class_info derived_class_info; + +struct add_operator_base; + +class extension_class_base : public class_t +{ + public: + extension_class_base(const char* name); + + public: + // the purpose of try_class_conversions() and its related functions + // is explained in extclass.cpp + void* try_class_conversions(instance_holder_base*) const; + void* try_base_class_conversions(instance_holder_base*) const; + void* try_derived_class_conversions(instance_holder_base*) const; + + void set_attribute(const char* name, PyObject* x); + void set_attribute(const char* name, ref x); + + private: + virtual void* extract_object_from_holder(instance_holder_base* v) const = 0; + virtual std::vector const& base_classes() const = 0; + virtual std::vector const& derived_classes() const = 0; + + protected: + friend struct add_operator_base; + void add_method(reference method, const char* name); + void add_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 class_registry +{ + public: + static extension_class_base* class_object() + { return static_class_object; } + + // Register/unregister the Python class object corresponding to T + static void register_class(extension_class_base*); + static void unregister_class(extension_class_base*); + + // Establish C++ inheritance relationships + static void register_base_class(base_class_info const&); + static void register_derived_class(derived_class_info const&); + + // Query the C++ inheritance relationships + static std::vector const& base_classes(); + static std::vector const& derived_classes(); + private: + static extension_class_base* static_class_object; + static std::vector static_base_class_info; + static std::vector static_derived_class_info; +}; + +}}} // namespace boost::python::detail + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + +// 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 = held_instance. +template > +class python_extension_class_converters +{ + public: + // Get an object which can be used to convert T to/from python. This is used + // as a kind of concept check by the global template + // + // PyObject* to_python(const T& x) + // + // below this class, to prevent the confusing messages that would otherwise + // pop up. Now, if T hasn't been wrapped as an extension class, the user + // will see an error message about the lack of an eligible + // py_extension_class_converters() function. + friend python_extension_class_converters py_extension_class_converters(boost::python::type) + { + return python_extension_class_converters(); + } + + // This is a member function because in a conforming implementation, friend + // funcitons defined inline in the class body are all instantiated as soon + // as the enclosing class is instantiated. If T is not copyable, that causes + // a compiler error. Instead, we access this function through the global + // template + // + // PyObject* to_python(const T& x) + // + // defined below this class. Since template functions are instantiated only + // on demand, errors will be avoided unless T is noncopyable and the user + // writes code which causes us to try to copy a T. + PyObject* to_python(const T& x) const + { + boost::python::reference result(create_instance()); + result->add_implementation( + std::auto_ptr( + new boost::python::detail::instance_value_holder(result.get(), x))); + return result.release(); + } + + // Convert to T* + friend T* from_python(PyObject* obj, boost::python::type) + { + // downcast to an extension_instance, then find the actual T + boost::python::detail::extension_instance* self = boost::python::detail::get_extension_instance(obj); + typedef std::vector::const_iterator iterator; + for (iterator p = self->wrapped_objects().begin(); + p != self->wrapped_objects().end(); ++p) + { + boost::python::detail::instance_holder* held = dynamic_cast*>(*p); + if (held != 0) + return held->target(); + + // see extclass.cpp for an explanation of try_class_conversions() + void* target = boost::python::detail::class_registry::class_object()->try_class_conversions(*p); + if(target) + return static_cast(target); + } + boost::python::detail::report_missing_instance_data(self, boost::python::detail::class_registry::class_object(), typeid(T)); + throw boost::python::argument_error(); + } + + // Convert to PtrType, where PtrType can be dereferenced to obtain a T. + template + static PtrType& ptr_from_python(PyObject* obj, boost::python::type) + { + // downcast to an extension_instance, then find the actual T + boost::python::detail::extension_instance* self = boost::python::detail::get_extension_instance(obj); + typedef std::vector::const_iterator iterator; + for (iterator p = self->wrapped_objects().begin(); + p != self->wrapped_objects().end(); ++p) + { + boost::python::detail::instance_ptr_holder* held = + dynamic_cast*>(*p); + if (held != 0) + return held->ptr(); + } + boost::python::detail::report_missing_ptr_data(self, boost::python::detail::class_registry::class_object(), typeid(T)); + throw boost::python::argument_error(); + } + + template + static PyObject* ptr_to_python(PtrType x) + { + boost::python::reference result(create_instance()); + result->add_implementation( + std::auto_ptr( + new boost::python::detail::instance_ptr_holder(x))); + return result.release(); + } + + static boost::python::reference create_instance() + { + PyTypeObject* class_object = boost::python::detail::class_registry::class_object(); + if (class_object == 0) + boost::python::detail::report_missing_class_object(typeid(T)); + + return boost::python::reference( + new boost::python::detail::extension_instance(class_object)); + } + + // Convert to const T* + friend const T* from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to const T* const& + friend const T* from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to T* const& + friend T* from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to T& + friend T& from_python(PyObject* p, boost::python::type) + { return *boost::python::detail::check_non_null(from_python(p, boost::python::type())); } + + // Convert to const T& + friend const T& from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to T + friend const T& from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + friend std::auto_ptr& from_python(PyObject* p, boost::python::type&>) + { return ptr_from_python(p, boost::python::type >()); } + + friend std::auto_ptr& from_python(PyObject* p, boost::python::type >) + { return ptr_from_python(p, boost::python::type >()); } + + friend const std::auto_ptr& from_python(PyObject* p, boost::python::type&>) + { return ptr_from_python(p, boost::python::type >()); } + + friend PyObject* to_python(std::auto_ptr x) + { return ptr_to_python(x); } + + friend boost::shared_ptr& from_python(PyObject* p, boost::python::type&>) + { return ptr_from_python(p, boost::python::type >()); } + + friend boost::shared_ptr& from_python(PyObject* p, boost::python::type >) + { return ptr_from_python(p, boost::python::type >()); } + + friend const boost::shared_ptr& from_python(PyObject* p, boost::python::type&>) + { return ptr_from_python(p, boost::python::type >()); } + + friend PyObject* to_python(boost::shared_ptr x) + { return ptr_to_python(x); } +}; + +// Convert T to_python, instantiated on demand and only if there isn't a +// non-template overload for this function. This version is the one invoked when +// T is a wrapped class. See the first 2 functions declared in +// python_extension_class_converters above for more info. +template +PyObject* to_python(const T& x) +{ + return py_extension_class_converters(boost::python::type()).to_python(x); +} + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +namespace boost { namespace python { + +BOOST_PYTHON_IMPORT_CONVERSION(python_extension_class_converters); + +namespace detail { + +template class instance_holder; + +class read_only_setattr_function : public function +{ + public: + read_only_setattr_function(const char* name); + PyObject* do_call(PyObject* args, PyObject* keywords) const; + const char* description() const; + private: + string m_name; +}; + + template + struct define_conversion + { + static void* upcast_ptr(void* v) + { + return static_cast(static_cast(v)); + } + + static void* downcast_ptr(void* v) + { + return dynamic_cast(static_cast(v)); + } + }; + +// An easy way to make an extension base class which wraps T. Note that Python +// subclasses of this class will simply be class_t 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 extension_class + : public python_extension_class_converters, // This generates the to_python/from_python functions + public extension_class_base +{ + public: + typedef T wrapped_type; + typedef U callback_type; + + // Construct with a name that comes from typeid(T).name(). The name only + // affects the objects of this class are represented through repr() + extension_class(); + + // Construct with the given name. The name only affects the objects of this + // class are represented through repr() + extension_class(const char* name); + + ~extension_class(); + + // define constructors + template + inline void def(constructor) + // The following incantation builds a signature1, signature2,... object. It + // should _all_ get optimized away. + { add_constructor( + prepend(type::id(), + prepend(type::id(), + prepend(type::id(), + prepend(type::id(), + prepend(type::id(), + signature0())))))); + } + + + // export homogeneous operators (type of both lhs and rhs is 'operator') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub), Foo>()); + + // export homogeneous operators (type of both lhs and rhs is 'T const&') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>()); + template + inline void def(operators) + { + typedef typename operand_select::template wrapped::type true_operand; + def_operators(operators()); + } + + // export heterogeneous operators (type of lhs: 'left', of rhs: 'right') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub), Foo>(), + // boost::python::right_operand()); + + // export heterogeneous operators (type of lhs: 'T const&', of rhs: 'right') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>(), + // boost::python::right_operand()); + template + inline void def(operators, right_operand r) + { + typedef typename operand_select::template wrapped::type true_left; + def_operators(operators(), r); + } + + // export heterogeneous reverse-argument operators + // (type of lhs: 'left', of rhs: 'right') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub), Foo>(), + // boost::python::left_operand()); + + // export heterogeneous reverse-argument operators + // (type of lhs: 'left', of rhs: 'T const&') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>(), + // boost::python::left_operand()); + template + inline void def(operators, left_operand l) + { + typedef typename operand_select::template wrapped::type true_right; + def_operators(operators(), l); + } + + // define a function that passes Python arguments and keywords + // to C++ verbatim (as a 'tuple const&' and 'dictionary const&' + // respectively). This is useful for manual argument passing. + // It's also the only possibility to pass keyword arguments to C++. + // Fn must have a signatur that is compatible to + // PyObject* (*)(PyObject* aTuple, PyObject* aDictionary) + template + inline void def_raw(Fn fn, const char* name) + { + this->add_method(new_raw_arguments_function(fn), name); + } + + // 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 + inline 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 + inline void def(Fn fn, const char* name, DefaultFn default_fn) + { + this->add_method(new_virtual_function(type(), fn, default_fn), name); + } + + // Provide a function which implements x., reading from the given + // member (pm) of the T obj + template + inline void def_getter(MemberType T::*pm, const char* name) + { + this->add_getter_method(new getter_function(pm), name); + } + + // Provide a function which implements assignment to x., writing to + // the given member (pm) of the T obj + template + inline void def_setter(MemberType T::*pm, const char* name) + { + this->add_setter_method(new setter_function(pm), name); + } + + // Expose the given member (pm) of the T obj as a read-only attribute + template + inline void def_readonly(MemberType T::*pm, const char* name) + { + this->add_setter_method(new read_only_setattr_function(name), name); + this->def_getter(pm, name); + } + + // Expose the given member (pm) of the T obj as a read/write attribute + template + inline void def_read_write(MemberType T::*pm, const char* name) + { + this->def_getter(pm, name); + this->def_setter(pm, name); + } + + // define the standard coercion needed for operator overloading + void def_standard_coerce(); + + // declare the given class a base class of this one and register + // up and down conversion functions + template + void declare_base(extension_class* base) + { + // see extclass.cpp for an explanation of why we need to register + // conversion functions + base_class_info baseInfo(base, + &define_conversion::downcast_ptr); + class_registry::register_base_class(baseInfo); + add_base(ref(as_object(base), ref::increment_count)); + + derived_class_info derivedInfo(this, + &define_conversion::upcast_ptr); + class_registry::register_derived_class(derivedInfo); + } + + // declare the given class a base class of this one and register + // only up conversion function + template + void declare_base(extension_class* base, without_downcast_t) + { + // see extclass.cpp for an explanation of why we need to register + // conversion functions + base_class_info baseInfo(base, 0); + class_registry::register_base_class(baseInfo); + add_base(ref(as_object(base), ref::increment_count)); + + derived_class_info derivedInfo(this, + &define_conversion::upcast_ptr); + class_registry::register_derived_class(derivedInfo); + } + + private: // types + typedef instance_value_holder holder; + + private: // extension_class_base virtual function implementations + std::vector const& base_classes() const; + std::vector const& derived_classes() const; + void* extract_object_from_holder(instance_holder_base* v) const; + + private: // Utility functions + template + inline void def_operators(operators) + { + def_standard_coerce(); + + // for some strange reason, this prevents MSVC from having an + // "unrecoverable block scoping error"! + typedef choose_op<(which & op_add)> choose_add; + + choose_op<(which & op_add)>::template args::add(this); + choose_op<(which & op_sub)>::template args::add(this); + choose_op<(which & op_mul)>::template args::add(this); + choose_op<(which & op_div)>::template args::add(this); + choose_op<(which & op_mod)>::template args::add(this); + choose_op<(which & op_divmod)>::template args::add(this); + choose_op<(which & op_pow)>::template args::add(this); + choose_op<(which & op_lshift)>::template args::add(this); + choose_op<(which & op_rshift)>::template args::add(this); + choose_op<(which & op_and)>::template args::add(this); + choose_op<(which & op_xor)>::template args::add(this); + choose_op<(which & op_or)>::template args::add(this); + choose_unary_op<(which & op_neg)>::template args::add(this); + choose_unary_op<(which & op_pos)>::template args::add(this); + choose_unary_op<(which & op_abs)>::template args::add(this); + choose_unary_op<(which & op_invert)>::template args::add(this); + choose_unary_op<(which & op_int)>::template args::add(this); + choose_unary_op<(which & op_long)>::template args::add(this); + choose_unary_op<(which & op_float)>::template args::add(this); + choose_op<(which & op_cmp)>::template args::add(this); + choose_unary_op<(which & op_str)>::template args::add(this); + } + + template + inline void def_operators(operators, right_operand) + { + def_standard_coerce(); + + choose_op<(which & op_add)>::template args::add(this); + choose_op<(which & op_sub)>::template args::add(this); + choose_op<(which & op_mul)>::template args::add(this); + choose_op<(which & op_div)>::template args::add(this); + choose_op<(which & op_mod)>::template args::add(this); + choose_op<(which & op_divmod)>::template args::add(this); + choose_op<(which & op_pow)>::template args::add(this); + choose_op<(which & op_lshift)>::template args::add(this); + choose_op<(which & op_rshift)>::template args::add(this); + choose_op<(which & op_and)>::template args::add(this); + choose_op<(which & op_xor)>::template args::add(this); + choose_op<(which & op_or)>::template args::add(this); + choose_op<(which & op_cmp)>::template args::add(this); + } + + template + inline void def_operators(operators, left_operand) + { + def_standard_coerce(); + + choose_rop<(which & op_add)>::template args::add(this); + choose_rop<(which & op_sub)>::template args::add(this); + choose_rop<(which & op_mul)>::template args::add(this); + choose_rop<(which & op_div)>::template args::add(this); + choose_rop<(which & op_mod)>::template args::add(this); + choose_rop<(which & op_divmod)>::template args::add(this); + choose_rop<(which & op_pow)>::template args::add(this); + choose_rop<(which & op_lshift)>::template args::add(this); + choose_rop<(which & op_rshift)>::template args::add(this); + choose_rop<(which & op_and)>::template args::add(this); + choose_rop<(which & op_xor)>::template args::add(this); + choose_rop<(which & op_or)>::template args::add(this); + choose_rop<(which & op_cmp)>::template args::add(this); + } + + template + void add_constructor(signature sig) + { + this->add_constructor_object(init_function::create(sig)); + } +}; + +// A simple wrapper over a T which allows us to use extension_class with a +// single template parameter only. See extension_class, above. +template +class held_instance : public T +{ + // There are no member functions: we want to avoid inadvertently overriding + // any virtual functions in T. +public: + held_instance(PyObject*) : T() {} + template + held_instance(PyObject*, A1 a1) : T(a1) {} + template + held_instance(PyObject*, A1 a1, A2 a2) : T(a1, a2) {} + template + held_instance(PyObject*, A1 a1, A2 a2, A3 a3) : T(a1, a2, a3) {} + template + held_instance(PyObject*, A1 a1, A2 a2, A3 a3, A4 a4) : T(a1, a2, a3, a4) {} + template + held_instance(PyObject*, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) : T(a1, a2, a3, a4, a5) {} +}; + +// Abstract base class for all obj holders. Base for template class +// instance_holder<>, below. +class instance_holder_base +{ +public: + virtual ~instance_holder_base() {} + virtual bool held_by_value() = 0; +}; + +// Abstract base class which holds a Held, somehow. Provides a uniform way to +// get a pointer to the held object +template +class instance_holder : public instance_holder_base +{ +public: + virtual Held*target() = 0; +}; + +// Concrete class which holds a Held by way of a wrapper class Wrapper. If Held +// can be constructed with arguments (A1...An), Wrapper must have a +// corresponding constructor for arguments (PyObject*, A1...An). Wrapper is +// neccessary to implement virtual function callbacks (there must be a +// back-pointer to the actual Python object so that we can call any +// overrides). held_instance (above) is used as a default Wrapper class when +// there are no virtual functions. +template +class instance_value_holder : public instance_holder +{ +public: + Held* target() { return &m_held; } + Wrapper* value_target() { return &m_held; } + + instance_value_holder(extension_instance* p) : + m_held(p) {} + template + instance_value_holder(extension_instance* p, A1 a1) : + m_held(p, a1) {} + template + instance_value_holder(extension_instance* p, A1 a1, A2 a2) : + m_held(p, a1, a2) {} + template + instance_value_holder(extension_instance* p, A1 a1, A2 a2, A3 a3) : + m_held(p, a1, a2, a3) {} + template + instance_value_holder(extension_instance* p, A1 a1, A2 a2, A3 a3, A4 a4) : + m_held(p, a1, a2, a3, a4) {} + template + instance_value_holder(extension_instance* p, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) : + m_held(p, a1, a2, a3, a4, a5) {} + + public: // implementation of instance_holder_base required interface + bool held_by_value() { return true; } + + private: + Wrapper m_held; +}; + +// Concrete class which holds a HeldType by way of a (possibly smart) pointer +// PtrType. By default, these are only generated for PtrType == +// std::auto_ptr and PtrType == boost::shared_ptr. +template +class instance_ptr_holder : public instance_holder +{ + public: + HeldType* target() { return &*m_ptr; } + PtrType& ptr() { return m_ptr; } + + instance_ptr_holder(PtrType ptr) : m_ptr(ptr) {} + + public: // implementation of instance_holder_base required interface + bool held_by_value() { return false; } + private: + PtrType m_ptr; +}; + +class extension_instance : public instance +{ + public: + extension_instance(PyTypeObject* class_); + ~extension_instance(); + + void add_implementation(std::auto_ptr holder); + + typedef std::vector held_objects; + const held_objects& wrapped_objects() const + { return m_wrapped_objects; } + private: + held_objects m_wrapped_objects; +}; + +// +// Template function implementations +// + +tuple extension_class_coerce(ref l, ref r); + +template +extension_class::extension_class() + : extension_class_base(typeid(T).name()) +{ + class_registry::register_class(this); +} + +template +extension_class::extension_class(const char* name) + : extension_class_base(name) +{ + class_registry::register_class(this); +} + +template +void extension_class::def_standard_coerce() +{ + ref coerce_fct = dict().get_item(string("__coerce__")); + + if(coerce_fct.get() == 0) // not yet defined + this->def(&extension_class_coerce, "__coerce__"); +} + +template +inline +std::vector const& +extension_class::base_classes() const +{ + return class_registry::base_classes(); +} + +template +inline +std::vector const& +extension_class::derived_classes() const +{ + return class_registry::derived_classes(); +} + +template +void* extension_class::extract_object_from_holder(instance_holder_base* v) const +{ + instance_holder* held = dynamic_cast*>(v); + if(held) + return held->target(); + return 0; +} + +template +extension_class::~extension_class() +{ + class_registry::unregister_class(this); +} + +template +inline void class_registry::register_class(extension_class_base* p) +{ + // You're not expected to create more than one of these! + assert(static_class_object == 0); + static_class_object = p; +} + +template +inline void class_registry::unregister_class(extension_class_base* 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; +} + +template +void class_registry::register_base_class(base_class_info const& i) +{ + static_base_class_info.push_back(i); +} + +template +void class_registry::register_derived_class(derived_class_info const& i) +{ + static_derived_class_info.push_back(i); +} + +template +std::vector const& class_registry::base_classes() +{ + return static_base_class_info; +} + +template +std::vector const& class_registry::derived_classes() +{ + return static_derived_class_info; +} + +// +// Static data member declaration. +// +template +extension_class_base* class_registry::static_class_object; +template +std::vector class_registry::static_base_class_info; +template +std::vector class_registry::static_derived_class_info; + +}}} // namespace boost::python::detail + +#endif // EXTENSION_CLASS_DWA052000_H_ + diff --git a/include/boost/python/detail/functions.hpp b/include/boost/python/detail/functions.hpp new file mode 100644 index 00000000..055255e7 --- /dev/null +++ b/include/boost/python/detail/functions.hpp @@ -0,0 +1,306 @@ +// (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 +# include +# include +# include +# include +# include +# include +# include +# include +# include + +namespace boost { namespace python { namespace detail { + +// forward declaration +class extension_instance; + + +// function -- +// the common base class for all overloadable function and method objects +// supplied by the library. +class function : public python_object +{ + 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(reference 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 type_object; + private: + reference m_overloads; +}; + +// wrapped_function_pointer<> -- +// A single function or member function pointer wrapped and presented to +// Python as a callable object. +// +// Template parameters: +// R - the return type of the function pointer +// F - the complete type of the wrapped function pointer +template +struct wrapped_function_pointer : function +{ + typedef F ptr_fun; // pointer-to--function or pointer-to-member-function + + wrapped_function_pointer(ptr_fun 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 ptr_fun m_pf; +}; + +// raw_arguments_function +// A function that passes the Python argument tuple and keyword dictionary +// verbatim to C++ (useful for customized argument parsing and variable +// argument lists) +template +struct raw_arguments_function : function +{ + typedef Ret (*ptr_fun)(Args, Keywords); + + raw_arguments_function(ptr_fun pf) + : m_pf(pf) {} + + private: + PyObject* do_call(PyObject* args, PyObject* keywords) const + { + ref dict(keywords ? + ref(keywords, ref::increment_count) : + ref(PyDict_New())); + + return to_python( + (*m_pf)(from_python(args, boost::python::type()), + from_python(dict.get(), boost::python::type()))); + } + + const char* description() const + { return typeid(ptr_fun).name(); } + + private: + const ptr_fun m_pf; +}; + +// virtual_function<> -- +// A virtual function with a default implementation wrapped and presented +// to Python as a callable object. +// +// Template parameters: +// T - the type of the target class +// R - the return type of the function pointer +// V - the virtual function pointer being wrapped +// (should be of the form R(T::*)(), or R (*)(T, )) +// D - a function which takes a T&, const T&, T*, or const T* first +// parameter and calls T::f on it /non-virtually/, where V +// approximates &T::f. +template +class virtual_function : public function +{ + public: + virtual_function(V virtual_function_ptr, D default_implementation) + : m_virtual_function_ptr(virtual_function_ptr), + m_default_implementation(default_implementation) + {} + + private: + PyObject* do_call(PyObject* args, PyObject* keywords) const; + + const char* description() const + { return typeid(V).name(); } + + private: + const V m_virtual_function_ptr; + const D m_default_implementation; +}; + +// 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 return_type; + return new wrapped_function_pointer(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); +} + +template +function* new_raw_arguments_function(R (*pmf)(Args, keywords)) +{ + return new raw_arguments_function(pmf); +} + + +// A helper function for new_virtual_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 V. +template +inline function* new_virtual_function_aux( + type, R, V virtual_function_ptr, D default_implementation + ) +{ + // We can't just use "typename R::Type" below because MSVC (incorrectly) pukes. + typedef typename R::type return_type; + return new virtual_function( + virtual_function_ptr, default_implementation); +} + +// Create and return a new virtual_function object wrapping the given +// virtual_function_ptr and default_implementation +template +inline function* new_virtual_function( + type, V virtual_function_ptr, D default_implementation + ) +{ + // Deduce the return type and pass it off to the helper function above + return new_virtual_function_aux( + type(), return_value(virtual_function_ptr), + virtual_function_ptr, default_implementation); +} + +// A function with a bundled "bound target" object. This is what is produced by +// the expression a.b where a is an instance or extension_instance object and b +// is a callable object not found in the obj namespace but on its class or +// a base class. +class bound_function : public python_object +{ + public: + static bound_function* create(const ref& target, const ref& fn); + + bound_function(const ref& target, const ref& fn); + PyObject* call(PyObject*args, PyObject* keywords) const; + PyObject* getattr(const char* name) const; + + private: + struct type_object; + friend struct type_object; + + ref m_target; + ref m_unbound_function; + + private: // data members for allocation/deallocation optimization + bound_function* m_free_list_link; + + static bound_function* free_list; +}; + +// Special functions designed to access data members of a wrapped C++ object. +template +class getter_function : public function +{ + public: + typedef MemberType ClassType::* pointer_to_member; + + getter_function(pointer_to_member pm) + : m_pm(pm) {} + + private: + PyObject* do_call(PyObject* args, PyObject* keywords) const; + + const char* description() const + { return typeid(MemberType (*)(const ClassType&)).name(); } + private: + pointer_to_member m_pm; +}; + +template +class setter_function : public function +{ + public: + typedef MemberType ClassType::* pointer_to_member; + + setter_function(pointer_to_member 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: + pointer_to_member m_pm; +}; + +template +PyObject* getter_function::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* setter_function::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 extract_type; + from_python(self, type())->*m_pm + = from_python(value, type()); + + return none(); +} + +template +PyObject* virtual_function::do_call(PyObject* args, PyObject* keywords) const +{ + // If the target object is held by pointer, we must call through the virtual + // function pointer to the most-derived override. + PyObject* target = PyTuple_GetItem(args, 0); + if (target != 0) + { + extension_instance* self = get_extension_instance(target); + if (self->wrapped_objects().size() == 1 + && !self->wrapped_objects()[0]->held_by_value()) + { + return caller::call(m_virtual_function_ptr, args, keywords); + } + } + return caller::call(m_default_implementation, args, keywords); +} + +}}} // namespace boost::python::detail + +#endif // FUNCTIONS_DWA051400_H_ diff --git a/include/boost/python/detail/init_function.hpp b/include/boost/python/detail/init_function.hpp new file mode 100644 index 00000000..e53f8aa9 --- /dev/null +++ b/include/boost/python/detail/init_function.hpp @@ -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 was generated for %d-argument constructors by gen_init_function.python + +#ifndef INIT_FUNCTION_DWA052000_H_ +# define INIT_FUNCTION_DWA052000_H_ + +# include +# include +# include +# include + +namespace boost { namespace python { + +namespace detail { + + // parameter_traits - so far, this is a way to pass a const T& when we can be + // sure T is not a reference type, and a raw T otherwise. This should be + // rolled into boost::call_traits. Ordinarily, parameter_traits would be + // written: + // + // template struct parameter_traits + // { + // typedef const T& const_reference; + // }; + // + // template struct parameter_traits + // { + // typedef T& const_reference; + // }; + // + // template <> struct parameter_traits + // { + // typedef void const_reference; + // }; + // + // ...but since we can't partially specialize on reference types, we need this + // long-winded but equivalent incantation. + + // const_ref_selector -- an implementation detail of parameter_traits (below). This uses + // the usual "poor man's partial specialization" hack for MSVC. + template + struct const_ref_selector + { + template + struct const_ref + { + typedef const T& type; + }; + }; + + template <> + struct const_ref_selector + { + template + struct const_ref + { + typedef T type; + }; + }; + +# ifdef BOOST_MSVC +# pragma warning(push) +# pragma warning(disable: 4181) +# endif // BOOST_MSVC + template + struct parameter_traits + { + private: + typedef const_ref_selector::value> selector; + public: + typedef typename selector::template const_ref::type const_reference; + }; +# ifdef BOOST_MSVC +# pragma warning(pop) +# endif // BOOST_MSVC + + // Full spcialization for void + template <> + struct parameter_traits + { + typedef void const_reference; + }; + + template + class reference_parameter + { + typedef typename parameter_traits::const_reference const_reference; + public: + reference_parameter(const_reference value) + : value(value) {} + operator const_reference() { return value; } + private: + const_reference value; + }; + +class extension_instance; +class instance_holder_base; + +class init; +template struct init0; +template struct init1; +template struct init2; +template struct init3; +template struct init4; +template struct init5; +template struct Init6; +template struct Init7; +template struct Init8; +template struct Init9; +template struct Init10; + +template +struct init_function +{ + static init* create(signature0) { + return new init0; + } + + template + static init* create(signature1) { + return new init1::const_reference>; + } + + template + static init* create(signature2) { + return new init2::const_reference, + detail::parameter_traits::const_reference>; + } + + template + static init* create(signature3) { + return new init3::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference>; + } + + template + static init* create(signature4) { + return new init4::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference>; + } + + template + static init* create(signature5) { + return new init5::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference>; + } + + template + static init* create(signature6) { + return new Init6::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference>; + } + + template + static init* create(signature7) { + return new Init7::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference>; + } + + template + static init* create(signature8) { + return new Init8::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference>; + } + + template + static init* create(signature9) { + return new Init9::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference>; + } + + template + static init* create(signature10) { + return new Init10::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference, + detail::parameter_traits::const_reference>; + } +}; + +class init : public function +{ +private: // override function hook + PyObject* do_call(PyObject* args, PyObject* keywords) const; +private: + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* tail_args, PyObject* keywords) const = 0; +}; + + +template +struct init0 : init +{ + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const + { + if (!PyArg_ParseTuple(args, const_cast(""))) + throw argument_error(); + return new T(self + ); + } + const char* description() const + { return typeid(void (*)(T&)).name(); } +}; + +template +struct init1 : init +{ + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const + { + PyObject* a1; + if (!PyArg_ParseTuple(args, const_cast("O"), &a1)) + throw argument_error(); + return new T(self, + boost::python::detail::reference_parameter(from_python(a1, type())) + ); + } + const char* description() const + { return typeid(void (*)(T&, A1)).name(); } +}; + +template +struct init2 : init +{ + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const + { + PyObject* a1; + PyObject* a2; + if (!PyArg_ParseTuple(args, const_cast("OO"), &a1, &a2)) + throw argument_error(); + return new T(self, + boost::python::detail::reference_parameter(from_python(a1, type())), + boost::python::detail::reference_parameter(from_python(a2, type())) + ); + } + const char* description() const + { return typeid(void (*)(T&, A1, A2)).name(); } +}; + +template +struct init3 : init +{ + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const + { + PyObject* a1; + PyObject* a2; + PyObject* a3; + if (!PyArg_ParseTuple(args, const_cast("OOO"), &a1, &a2, &a3)) + throw argument_error(); + return new T(self, + boost::python::detail::reference_parameter(from_python(a1, type())), + boost::python::detail::reference_parameter(from_python(a2, type())), + boost::python::detail::reference_parameter(from_python(a3, type())) + ); + } + const char* description() const + { return typeid(void (*)(T&, A1, A2, A3)).name(); } +}; + +template +struct init4 : init +{ + virtual instance_holder_base* create_holder(extension_instance* 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 argument_error(); + return new T(self, + boost::python::detail::reference_parameter(from_python(a1, type())), + boost::python::detail::reference_parameter(from_python(a2, type())), + boost::python::detail::reference_parameter(from_python(a3, type())), + boost::python::detail::reference_parameter(from_python(a4, type())) + ); + } + const char* description() const + { return typeid(void (*)(T&, A1, A2, A3, A4)).name(); } +}; + +template +struct init5 : init +{ + virtual instance_holder_base* create_holder(extension_instance* 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 argument_error(); + return new T(self, + boost::python::detail::reference_parameter(from_python(a1, type())), + boost::python::detail::reference_parameter(from_python(a2, type())), + boost::python::detail::reference_parameter(from_python(a3, type())), + boost::python::detail::reference_parameter(from_python(a4, type())), + boost::python::detail::reference_parameter(from_python(a5, type())) + ); + } + const char* description() const + { return typeid(void (*)(T&, A1, A2, A3, A4, A5)).name(); } +}; + +template +struct Init6 : init +{ + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const + { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + if (!PyArg_ParseTuple(args, const_cast("OOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6)) + throw argument_error(); + return new T(self, + boost::python::detail::reference_parameter(from_python(a1, type())), + boost::python::detail::reference_parameter(from_python(a2, type())), + boost::python::detail::reference_parameter(from_python(a3, type())), + boost::python::detail::reference_parameter(from_python(a4, type())), + boost::python::detail::reference_parameter(from_python(a5, type())), + boost::python::detail::reference_parameter(from_python(a6, type())) + ); + } + const char* description() const + { return typeid(void (*)(T&, A1, A2, A3, A4, A5, A6)).name(); } +}; + +template +struct Init7 : init +{ + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const + { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7)) + throw argument_error(); + return new T(self, + boost::python::detail::reference_parameter(from_python(a1, type())), + boost::python::detail::reference_parameter(from_python(a2, type())), + boost::python::detail::reference_parameter(from_python(a3, type())), + boost::python::detail::reference_parameter(from_python(a4, type())), + boost::python::detail::reference_parameter(from_python(a5, type())), + boost::python::detail::reference_parameter(from_python(a6, type())), + boost::python::detail::reference_parameter(from_python(a7, type())) + ); + } + const char* description() const + { return typeid(void (*)(T&, A1, A2, A3, A4, A5, A6, A7)).name(); } +}; + +template +struct Init8 : init +{ + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const + { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8)) + throw argument_error(); + return new T(self, + boost::python::detail::reference_parameter(from_python(a1, type())), + boost::python::detail::reference_parameter(from_python(a2, type())), + boost::python::detail::reference_parameter(from_python(a3, type())), + boost::python::detail::reference_parameter(from_python(a4, type())), + boost::python::detail::reference_parameter(from_python(a5, type())), + boost::python::detail::reference_parameter(from_python(a6, type())), + boost::python::detail::reference_parameter(from_python(a7, type())), + boost::python::detail::reference_parameter(from_python(a8, type())) + ); + } + const char* description() const + { return typeid(void (*)(T&, A1, A2, A3, A4, A5, A6, A7, A8)).name(); } +}; + +template +struct Init9 : init +{ + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const + { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9)) + throw argument_error(); + return new T(self, + boost::python::detail::reference_parameter(from_python(a1, type())), + boost::python::detail::reference_parameter(from_python(a2, type())), + boost::python::detail::reference_parameter(from_python(a3, type())), + boost::python::detail::reference_parameter(from_python(a4, type())), + boost::python::detail::reference_parameter(from_python(a5, type())), + boost::python::detail::reference_parameter(from_python(a6, type())), + boost::python::detail::reference_parameter(from_python(a7, type())), + boost::python::detail::reference_parameter(from_python(a8, type())), + boost::python::detail::reference_parameter(from_python(a9, type())) + ); + } + const char* description() const + { return typeid(void (*)(T&, A1, A2, A3, A4, A5, A6, A7, A8, A9)).name(); } +}; + +template +struct Init10 : init +{ + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const + { + PyObject* a1; + PyObject* a2; + PyObject* a3; + PyObject* a4; + PyObject* a5; + PyObject* a6; + PyObject* a7; + PyObject* a8; + PyObject* a9; + PyObject* a10; + if (!PyArg_ParseTuple(args, const_cast("OOOOOOOOOO"), &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10)) + throw argument_error(); + return new T(self, + boost::python::detail::reference_parameter(from_python(a1, type())), + boost::python::detail::reference_parameter(from_python(a2, type())), + boost::python::detail::reference_parameter(from_python(a3, type())), + boost::python::detail::reference_parameter(from_python(a4, type())), + boost::python::detail::reference_parameter(from_python(a5, type())), + boost::python::detail::reference_parameter(from_python(a6, type())), + boost::python::detail::reference_parameter(from_python(a7, type())), + boost::python::detail::reference_parameter(from_python(a8, type())), + boost::python::detail::reference_parameter(from_python(a9, type())), + boost::python::detail::reference_parameter(from_python(a10, type())) + ); + } + const char* description() const + { return typeid(void (*)(T&, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)).name(); } +}; + +}}} // namespace boost::python::detail + +#endif // INIT_FUNCTION_DWA052000_H_ diff --git a/include/boost/python/detail/none.hpp b/include/boost/python/detail/none.hpp new file mode 100644 index 00000000..8cb21004 --- /dev/null +++ b/include/boost/python/detail/none.hpp @@ -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 +# include + +namespace boost { namespace python { namespace detail { + +inline PyObject* none() { Py_INCREF(Py_None); return Py_None; } + +}}} // namespace boost::python::detail + +#endif // NONE_DWA_052000_H_ diff --git a/include/boost/python/detail/signatures.hpp b/include/boost/python/detail/signatures.hpp new file mode 100644 index 00000000..2be75b52 --- /dev/null +++ b/include/boost/python/detail/signatures.hpp @@ -0,0 +1,251 @@ +// (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.python for 10 arguments. +#ifndef SIGNATURES_DWA050900_H_ +# define SIGNATURES_DWA050900_H_ + +# include + +namespace boost { namespace python { + +namespace detail { +// 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_t {}; +} + +// 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_t. +template +struct type +{ + typedef type id; +}; + +template <> +struct type +{ + typedef boost::python::detail::void_t id; +}; + +namespace detail { +// 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 signature10 {}; + +template +struct signature9 {}; + +template +inline signature10 prepend(type, signature9) + { return signature10(); } + +template +struct signature8 {}; + +template +inline signature9 prepend(type, signature8) + { return signature9(); } + +template +struct signature7 {}; + +template +inline signature8 prepend(type, signature7) + { return signature8(); } + +template +struct signature6 {}; + +template +inline signature7 prepend(type, signature6) + { return signature7(); } + +template +struct signature5 {}; + +template +inline signature6 prepend(type, signature5) + { return signature6(); } + +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_t to the head of a void_t +// signature results in a void_t signature again. +inline signature0 prepend(void_t, signature0) { return signature0(); } + +} // namespace detail + +template +struct constructor +{ +}; + +namespace detail { +// 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 return_value_select { typedef T type; }; + +// free functions +template +return_value_select return_value(R (*)()) { return return_value_select(); } + +template +return_value_select return_value(R (*)(A1)) { return return_value_select(); } + +template +return_value_select return_value(R (*)(A1, A2)) { return return_value_select(); } + +template +return_value_select return_value(R (*)(A1, A2, A3)) { return return_value_select(); } + +template +return_value_select return_value(R (*)(A1, A2, A3, A4)) { return return_value_select(); } + +template +return_value_select return_value(R (*)(A1, A2, A3, A4, A5)) { return return_value_select(); } + +template +return_value_select return_value(R (*)(A1, A2, A3, A4, A5, A6)) { return return_value_select(); } + +template +return_value_select return_value(R (*)(A1, A2, A3, A4, A5, A6, A7)) { return return_value_select(); } + +template +return_value_select return_value(R (*)(A1, A2, A3, A4, A5, A6, A7, A8)) { return return_value_select(); } + +template +return_value_select return_value(R (*)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return return_value_select(); } + +template +return_value_select return_value(R (*)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return return_value_select(); } + +// TODO(?): handle 'const void' + +// member functions +template +return_value_select return_value(R (T::*)()) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1)) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2)) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3)) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4)) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5)) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5, A6)) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5, A6, A7)) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5, A6, A7, A8)) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5, A6, A7, A8, A9)) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)() const) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1) const) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2) const) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3) const) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4) const) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5) const) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5, A6) const) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5, A6, A7) const) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5, A6, A7, A8) const) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5, A6, A7, A8, A9) const) { return return_value_select(); } + +template +return_value_select return_value(R (T::*)(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) const) { return return_value_select(); } + +}}} // namespace boost::python::detail + +#endif diff --git a/include/boost/python/detail/singleton.hpp b/include/boost/python/detail/singleton.hpp new file mode 100644 index 00000000..07d794ac --- /dev/null +++ b/include/boost/python/detail/singleton.hpp @@ -0,0 +1,68 @@ +// (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 + +namespace boost { namespace python { namespace detail { + +struct empty {}; +template +struct singleton : Base +{ + typedef singleton singleton_base; // Convenience type for derived class constructors + + static Derived* instance(); + + // 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 + singleton(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6) : Base(a1, a2, a3, a4, a5, a6) {} + + template + singleton(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7) : Base(a1, a2, a3, a4, a5, a6, a7) {} + + template + singleton(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8) : Base(a1, a2, a3, a4, a5, a6, a7, a8) {} + + template + singleton(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9) : Base(a1, a2, a3, a4, a5, a6, a7, a8, a9) {} + + template + singleton(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10) : Base(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {} + +}; + +template +Derived* singleton::instance() +{ + static Derived x; + return &x; +} + +}}} // namespace boost::python::detail + +#endif diff --git a/include/boost/python/detail/types.hpp b/include/boost/python/detail/types.hpp new file mode 100644 index 00000000..ee33ce04 --- /dev/null +++ b/include/boost/python/detail/types.hpp @@ -0,0 +1,389 @@ +// (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 +// boost::python::callable< +// boost::python::getattrable < +// boost::python::setattrable > > +// { +// public: +// ref call(args, kw); +// ref getattr(args, kw); +// ref setattr(args, kw); +// }; + +# include +# include // really just for type<> +# include +# include +# include +# include +# include + +namespace boost { namespace python { + +class string; + +namespace detail { + +class instance_holder_base; + +class type_object_base : public python_type +{ + public: + explicit type_object_base(PyTypeObject* type_type); + virtual ~type_object_base(); + + 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, + + number_add, number_subtract, number_multiply, number_divide, + number_remainder, number_divmod, number_power, number_negative, + number_positive, number_absolute, number_nonzero, number_invert, + number_lshift, number_rshift, number_and, number_xor, number_or, + number_coerce, number_int, number_long, number_float, number_oct, + number_hex + }; + + void enable(capability); + + // + // 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* obj, PyObject* args, PyObject* kw) const; + virtual PyObject* instance_getattr(PyObject* obj, const char* name) const; + virtual int instance_setattr(PyObject* obj, 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* obj) const; + virtual PyObject* instance_sequence_concat(PyObject* obj, PyObject* other) const; + virtual PyObject* instance_sequence_repeat(PyObject* obj, int n) const; + virtual PyObject* instance_sequence_item(PyObject* obj, int n) const; + virtual PyObject* instance_sequence_slice(PyObject* obj, int start, int finish) const; + virtual int instance_sequence_ass_item(PyObject* obj, int n, PyObject* value) const; + virtual int instance_sequence_ass_slice(PyObject* obj, int start, int finish, PyObject* value) const; + + public: // Callbacks for number methods + virtual PyObject* instance_number_add(PyObject*, PyObject*) const; + virtual PyObject* instance_number_subtract(PyObject*, PyObject*) const; + virtual PyObject* instance_number_multiply(PyObject*, PyObject*) const; + virtual PyObject* instance_number_divide(PyObject*, PyObject*) const; + virtual PyObject* instance_number_remainder(PyObject*, PyObject*) const; + virtual PyObject* instance_number_divmod(PyObject*, PyObject*) const; + virtual PyObject* instance_number_power(PyObject*, PyObject*, PyObject*) const; + virtual PyObject* instance_number_negative(PyObject*) const; + virtual PyObject* instance_number_positive(PyObject*) const; + virtual PyObject* instance_number_absolute(PyObject*) const; + virtual int instance_number_nonzero(PyObject*) const; + virtual PyObject* instance_number_invert(PyObject*) const; + virtual PyObject* instance_number_lshift(PyObject*, PyObject*) const; + virtual PyObject* instance_number_rshift(PyObject*, PyObject*) const; + virtual PyObject* instance_number_and(PyObject*, PyObject*) const; + virtual PyObject* instance_number_xor(PyObject*, PyObject*) const; + virtual PyObject* instance_number_or(PyObject*, PyObject*) const; + virtual int instance_number_coerce(PyObject*, PyObject**, PyObject**) const; + virtual PyObject* instance_number_int(PyObject*) const; + virtual PyObject* instance_number_long(PyObject*) const; + virtual PyObject* instance_number_float(PyObject*) const; + virtual PyObject* instance_number_oct(PyObject*) const; + virtual PyObject* instance_number_hex(PyObject*) const; +}; + +template +class type_object : public type_object_base +{ + public: + typedef T instance; + + type_object(PyTypeObject* type_type, const char* name) + : type_object_base(type_type) + { + assert(name != 0); + this->tp_name = const_cast(name); + } + + type_object(PyTypeObject* type_type) + : type_object_base(type_type) + { + this->tp_name = const_cast(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 type_object_base 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); + callable(PyTypeObject* type_type); + private: + PyObject* instance_call(PyObject* obj, 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); + getattrable(PyTypeObject* type_type); + private: + PyObject* instance_getattr(PyObject* obj, 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); + setattrable(PyTypeObject* type_type); + private: + int instance_setattr(PyObject* obj, const char* name, PyObject* value) const; +}; + +template +class reprable : public Base +{ + public: + typedef reprable properties; // Convenience for derived class construction + typedef typename Base::instance instance; + reprable(PyTypeObject* type_type, const char* name); + reprable(PyTypeObject* type_type); + private: + PyObject* instance_repr(PyObject* obj) const; +}; + +// +// Member function definitions +// + +// type_object<> +template +void type_object::instance_dealloc(PyObject* obj) const +{ + this->dealloc(downcast(obj).get()); +} + +template +void type_object::dealloc(T* obj) const +{ + delete obj; +} + +// callable +template +callable::callable(PyTypeObject* type_type, const char* name) + : Base(type_type, name) +{ + this->enable(call); +} + +template +callable::callable(PyTypeObject* type_type) + : Base(type_type) +{ + this->enable(call); +} + +template +PyObject* callable::instance_call(PyObject* obj, PyObject* args, PyObject* kw) const +{ + return downcast(obj)->call(args, kw); +} + +// getattrable +template +getattrable::getattrable(PyTypeObject* type_type, const char* name) + : Base(type_type, name) +{ + this->enable(getattr); +} + +template +getattrable::getattrable(PyTypeObject* type_type) + : Base(type_type) +{ + this->enable(getattr); +} + +template +PyObject* getattrable::instance_getattr(PyObject* obj, const char* name) const +{ + return downcast(obj)->getattr(name); +} + +// setattrable +template +setattrable::setattrable(PyTypeObject* type_type, const char* name) + : Base(type_type, name) +{ + this->enable(setattr); +} + +template +setattrable::setattrable(PyTypeObject* type_type) + : Base(type_type) +{ + this->enable(setattr); +} + +template +int setattrable::instance_setattr(PyObject* obj, const char* name, PyObject* value) const +{ + return downcast(obj)->setattr(name, value); +} + +// reprable +template +reprable::reprable(PyTypeObject* type_type, const char* name) + : Base(type_type, name) +{ + this->enable(repr); +} + +template +reprable::reprable(PyTypeObject* type_type) + : Base(type_type) +{ + this->enable(repr); +} + +template +PyObject* reprable::instance_repr(PyObject* obj) const +{ + return downcast(obj)->repr(); +} + + // Helper class for optimized allocation of PODs: If two PODs + // happen to contain identical byte patterns, they may share their + // memory. Reference counting is used to free unused memory. + // This is useful because method tables of related extension classes tend + // to be identical, so less memory is needed for them. + class shared_pod_manager + { + typedef std::pair holder; + typedef std::vector storage; + + public: + static shared_pod_manager& obj(); + ~shared_pod_manager(); + + // Allocate memory for POD T and fill it with zeros. + // This memory is initially not shared. + template + static void create(T*& t) + { + t = reinterpret_cast(obj().create(sizeof(T))); + } + + // Decrement the refcount for the memory t points to. If the count + // goes to zero, the memory is freed. + template + static void dispose(T* t) + { + obj().dec_ref(t, sizeof(T)); + } + + // Attempt to share the memory t points to. If memory with the same + // contents already exists, t is replaced by a pointer to this memory, + // and t's old memory is disposed. Otherwise, t will be registered for + // potential future sharing. + template + static void replace_if_equal(T*& t) + { + t = reinterpret_cast(obj().replace_if_equal(t, sizeof(T))); + } + + // Create a copy of t's memory that is guaranteed to be private to t. + // Afterwards t points to the new memory, unless it was already private, in + // which case there is no change (except that t's memory will no longer + // be considered for future sharing - see raplade_if_equal()) + // This function *must* be called before the contents of (*t) can + // be overwritten. Otherwise, inconsistencies and crashes may result. + template + static void make_unique_copy(T*& t) + { + t = reinterpret_cast(obj().make_unique_copy(t, sizeof(T))); + } + + private: + void* replace_if_equal(void* pod, std::size_t size); + void* make_unique_copy(void* pod, std::size_t size); + void* create(std::size_t size); + void dec_ref(void* pod, std::size_t size); + void erase_from_list(void* pod); + + struct compare; + struct identical; + + private: + shared_pod_manager() {} // instance + +#ifdef TYPE_OBJECT_BASE_STANDALONE_TEST + public: +#endif + storage m_storage; + }; + + + void add_capability(type_object_base::capability capability, + PyTypeObject* dest); + +// This macro gets the length of an array as a compile-time constant, and will +// fail to compile if the parameter is a pointer. +# define PY_ARRAY_LENGTH(a) \ + (sizeof(::boost::python::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); + +}}} // namespace boost::python::detail + +#endif // TYPES_DWA051800_H_ diff --git a/include/boost/python/detail/wrap_python.hpp b/include/boost/python/detail/wrap_python.hpp new file mode 100644 index 00000000..7c6dd5be --- /dev/null +++ b/include/boost/python/detail/wrap_python.hpp @@ -0,0 +1,61 @@ +#ifdef _DEBUG +# ifndef DEBUG_PYTHON +# undef _DEBUG // Don't let Python force the debug library just because we're debugging. +# define DEBUG_UNDEFINED_FROM_WRAP_PYTHON_H +# endif +#endif + +// +// Some things we need in order to get Python.h to work with compilers other +// than MSVC on Win32 +// +#if defined(_WIN32) +# ifdef __GNUC__ + +typedef int pid_t; +# define WORD_BIT 32 +# define hypot _hypot +# include +# 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 + diff --git a/include/boost/python/errors.hpp b/include/boost/python/errors.hpp new file mode 100644 index 00000000..a1cdbbb0 --- /dev/null +++ b/include/boost/python/errors.hpp @@ -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 boost { namespace python { + +struct error_already_set {}; +struct argument_error : error_already_set {}; + +// Handles exceptions caught just before returning to Python code. +void handle_exception(); + +template +T* expect_non_null(T* x) +{ + if (x == 0) + throw error_already_set(); + return x; +} + +}} // namespace boost::python + +#endif // ERRORS_DWA052500_H_ diff --git a/include/boost/python/module_builder.hpp b/include/boost/python/module_builder.hpp new file mode 100644 index 00000000..57507e59 --- /dev/null +++ b/include/boost/python/module_builder.hpp @@ -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 MODULE_DWA051000_H_ +# define MODULE_DWA051000_H_ + +# include +# include +# include +# include + +namespace boost { namespace python { + +class module_builder +{ + typedef PyObject * (*raw_function_ptr)(boost::python::tuple const &, boost::python::dictionary const &); + + public: + // Create a module. REQUIRES: only one module_builder is created per module. + module_builder(const char* name); + + // Add elements to the module + void add(detail::function* x, const char* name); + void add(PyTypeObject* x, const char* name = 0); + void add(ref x, const char*name); + + template + void def_raw(Fn fn, const char* name) + { + add(detail::new_raw_arguments_function(fn), name); + } + + template + void def(Fn fn, const char* name) + { + add(detail::new_wrapped_function(fn), name); + } + + static string name(); + + private: + PyObject* m_module; + static PyMethodDef initial_methods[1]; +}; + +}} // namespace boost::python + +#endif diff --git a/include/boost/python/objects.hpp b/include/boost/python/objects.hpp new file mode 100644 index 00000000..34052e1f --- /dev/null +++ b/include/boost/python/objects.hpp @@ -0,0 +1,334 @@ +// (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 +# include +# include +# include "boost/operators.hpp" +# include + +namespace boost { namespace python { + +class object +{ + public: + explicit object(ref p); + + // Return a reference to the held object + ref reference() const; + + // Return a raw pointer to the held object + PyObject* get() const; + + private: + ref m_p; +}; + +class tuple : public object +{ + public: + explicit tuple(std::size_t n = 0); + explicit tuple(ref p); + + template + tuple(const std::pair& x) + : object(ref(PyTuple_New(2))) + { + set_item(0, x.first); + set_item(1, x.second); + } + + template + tuple(const First& first, const Second& second) + : object(ref(PyTuple_New(2))) + { + set_item(0, first); + set_item(1, second); + } + + template + tuple(const First& first, const Second& second, const Third& third) + : object(ref(PyTuple_New(3))) + { + set_item(0, first); + set_item(1, second); + set_item(2, third); + } + + template + tuple(const First& first, const Second& second, const Third& third, const Fourth& fourth) + : object(ref(PyTuple_New(4))) + { + set_item(0, first); + set_item(1, second); + set_item(2, third); + set_item(3, fourth); + } + + static PyTypeObject* type_obj(); + static bool accepts(ref p); + std::size_t size() const; + ref operator[](std::size_t pos) const; + + template + void set_item(std::size_t pos, const T& rhs) + { + this->set_item(pos, make_ref(rhs)); + } + + void set_item(std::size_t pos, const ref& 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 slice_proxy; + public: + explicit list(ref p); + explicit list(std::size_t sz = 0); + static PyTypeObject* type_obj(); + static bool accepts(ref p); + std::size_t size(); + ref operator[](std::size_t pos) const; + proxy operator[](std::size_t pos); + ref get_item(std::size_t pos) const; + + template + void set_item(std::size_t pos, const T& x) + { this->set_item(pos, make_ref(x)); } + void set_item(std::size_t pos, const ref& ); + +// void set_item(std::size_t pos, const object& ); + + template + void insert(std::size_t index, const T& x) + { this->insert(index, make_ref(x)); } + void insert(std::size_t index, const ref& item); + + template + void push_back(const T& item) + { this->push_back(make_ref(item)); } + void push_back(const ref& item); + + template + void append(const T& item) + { this->append(make_ref(item)); } + void append(const ref& item); + + list slice(int low, int high) const; + slice_proxy slice(int low, int high); + void sort(); + void reverse(); + tuple as_tuple() const; +}; + +class string + : public object, public boost::multipliable2 +{ + public: + // Construct from an owned PyObject*. + // Precondition: p must point to a python string. + explicit string(ref p); + explicit string(const char* s); + string(const char* s, std::size_t length); + string(const string& rhs); + + enum interned_t { interned }; + string(const char* s, interned_t); + + // Get the type object for Strings + static PyTypeObject* type_obj(); + + // Return true if the given object is a python string + static bool accepts(ref 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*=(unsigned int repeat_count); + 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(); + + friend string operator%(const string& format, const tuple& args); +}; + +class dictionary : public object +{ + private: + struct proxy; + + public: + explicit dictionary(ref p); + dictionary(); + void clear(); + + static PyTypeObject* type_obj(); + static bool accepts(ref p); + + public: + template + proxy operator[](const Key& key) + { return this->operator[](make_ref(key)); } + proxy operator[](ref key); + + template + ref operator[](const Key& key) const + { return this->operator[](make_ref(key)); } + ref operator[](ref key) const; + + template + ref get_item(const Key& key) const + { return this->get_item(make_ref(key)); } + ref get_item(const ref& key) const; + + template + ref get_item(const Key& key, const Default& default_) const + { return this->get_item(make_ref(key), make_ref(default_)); } + ref get_item(const ref& key, const ref& default_) const; + + template + void set_item(const Key& key, const Value& value) + { this->set_item(make_ref(key), make_ref(value)); } + void set_item(const ref& key, const ref& value); + + template + void erase(const Key& key) + { this->erase(make_ref(key)); } + void erase(ref key); + +// proxy operator[](const object& key); +// ref operator[](const object& key) const; + +// ref get_item(const object& key, ref default_ = ref()) const; +// void set_item(const object& key, const ref& value); + +// void erase(const object& key); + + list items() const; + list keys() const; + list values() const; + + std::size_t size() const; + // TODO: iterator support +}; + +struct dictionary::proxy +{ + template + const ref& operator=(const T& rhs) + { return (*this) = make_ref(rhs); } + const ref& operator=(const ref& rhs); + + operator ref() const; + private: + friend class dictionary; + proxy(const ref& dict, const ref& key); + + // This is needed to work around the very strange MSVC error report that the + // return type of the built-in operator= differs from that of the ones + // defined above. Couldn't hurt to make these un-assignable anyway, though. + const ref& operator=(const proxy&); // Not actually implemented + private: + ref m_dict; + ref m_key; +}; + +struct list::proxy +{ + template + const ref& operator=(const T& rhs) + { return (*this) = make_ref(rhs); } + const ref& operator=(const ref& rhs); + + operator ref() const; + + private: + friend class list; + proxy(const ref& list, std::size_t index); + + // This is needed to work around the very strange MSVC error report that the + // return type of the built-in operator= differs from that of the ones + // defined above. Couldn't hurt to make these un-assignable anyway, though. + const ref& operator=(const proxy&); // Not actually implemented + private: + list m_list; + std::size_t m_index; +}; + +struct list::slice_proxy +{ + const list& operator=(const list& rhs); + operator ref() const; + operator list() const; + std::size_t size(); + ref operator[](std::size_t pos) const; + private: + friend class list; + slice_proxy(const ref& list, int low, int high); + private: + ref m_list; + int m_low, m_high; +}; + +}} // namespace boost::python + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + +PyObject* to_python(const boost::python::tuple&); +boost::python::tuple from_python(PyObject* p, boost::python::type); + +inline boost::python::tuple from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +PyObject* to_python(const boost::python::list&); +boost::python::list from_python(PyObject* p, boost::python::type); + +inline boost::python::list from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +PyObject* to_python(const boost::python::string&); +boost::python::string from_python(PyObject* p, boost::python::type); + +inline boost::python::string from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +PyObject* to_python(const boost::python::dictionary&); +boost::python::dictionary from_python(PyObject* p, boost::python::type); + +inline boost::python::dictionary from_python(PyObject* p, boost::python::type) +{ + return from_python(p, boost::python::type()); +} + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +#endif // OBJECTS_DWA051100_H_ diff --git a/include/boost/python/operators.hpp b/include/boost/python/operators.hpp new file mode 100644 index 00000000..d0c06895 --- /dev/null +++ b/include/boost/python/operators.hpp @@ -0,0 +1,504 @@ +#ifndef OPERATORS_UK112000_H_ +#define OPERATORS_UK112000_H_ + +#include +#if !defined(__GNUC__) || defined(__SGI_STL_PORT) +# include +#else +# include +#endif + +namespace boost { namespace python { + +namespace detail { + + // helper class for automatic operand type detection + // during operator wrapping. + struct auto_operand {}; +} + +// Define operator ids that can be or'ed together +// (boost::python::op_add | boost::python::op_sub | boost::python::op_mul). +// This allows to wrap several operators in one line. +enum operator_id +{ + op_add = 0x1, + op_sub = 0x2, + op_mul = 0x4, + op_div = 0x8, + op_mod = 0x10, + op_divmod =0x20, + op_pow = 0x40, + op_lshift = 0x80, + op_rshift = 0x100, + op_and = 0x200, + op_xor = 0x400, + op_or = 0x800, + op_neg = 0x1000, + op_pos = 0x2000, + op_abs = 0x4000, + op_invert = 0x8000, + op_int = 0x10000, + op_long = 0x20000, + op_float = 0x40000, + op_str = 0x80000, + op_cmp = 0x100000 +}; + +// Wrap the operators given by "which". Usage: +// foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>()); +template +struct operators {}; + +// Wrap heterogeneous operators with given left operand type. Usage: +// foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>(), +// boost::python::left_operand()); +template +struct left_operand {}; + +// Wrap heterogeneous operators with given right operand type. Usage: +// foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>(), +// boost::python::right_operand()); +template +struct right_operand {}; + +namespace detail +{ + template + struct operand_select + { + template + struct wrapped + { + typedef Specified type; + }; + }; + + template <> + struct operand_select + { + template + struct wrapped + { + typedef const wrapped_type& type; + }; + }; + + template struct define_operator; + + // Base class which grants access to extension_class_base::add_method() to its derived classes + struct add_operator_base + { + protected: + static inline void add_method(extension_class_base* target, function* method, const char* name) + { target->add_method(method, name); } + }; + +// +// choose_op, choose_unary_op, and choose_rop +// +// These templates use "poor man's partial specialization" to generate the +// appropriate add_method() call (if any) for a given operator and argument set. +// +// Usage: +// choose_op<(which & op_add)>::template args::add(ext_class); +// +// (see extension_class<>::def_operators() for more examples). +// + template + struct choose_op + { + template + struct args : add_operator_base + { + static inline void add(extension_class_base* target) + { + typedef define_operator def_op; + add_method(target, + new typename def_op::template operator_function(), + def_op::name()); + } + + }; + }; + + // specialization for 0 has no effect + template <> + struct choose_op<0> + { + template + struct args + { + static inline void add(extension_class_base*) + { + } + + }; + }; + + template + struct choose_unary_op + { + template + struct args : add_operator_base + { + static inline void add(extension_class_base* target) + { + typedef define_operator def_op; + add_method(target, + new typename def_op::template operator_function(), + def_op::name()); + } + + }; + }; + + // specialization for 0 has no effect + template <> + struct choose_unary_op<0> + { + template + struct args + { + static inline void add(extension_class_base*) + { + } + + }; + }; + + template + struct choose_rop + { + template + struct args : add_operator_base + { + static inline void add(extension_class_base* target) + { + typedef define_operator def_op; + add_method(target, + new typename def_op::template roperator_function(), + def_op::rname()); + } + + }; + }; + + // specialization for 0 has no effect + template <> + struct choose_rop<0> + { + template + struct args + { + static inline void add(extension_class_base*) + { + } + + }; + }; + + +// Fully specialize define_operator for all operators defined in operator_id above. +// Every specialization defines one function object for normal operator calls and one +// for operator calls with operands reversed ("__r*__" function variants). +// Specializations for most operators follow a standard pattern: execute the expression +// that uses the operator in question. This standard pattern is realized by the following +// macros so that the actual specialization can be done by just calling a macro. +#define PY_DEFINE_BINARY_OPERATORS(id, oper) \ + template <> \ + struct define_operator \ + { \ + template \ + struct operator_function : function \ + { \ + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const \ + { \ + tuple args(ref(arguments, ref::increment_count)); \ + \ + return BOOST_PYTHON_CONVERSION::to_python( \ + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) oper \ + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type())); \ + } \ + \ + const char* description() const \ + { return "__" #id "__"; } \ + }; \ + \ + template \ + struct roperator_function : function \ + { \ + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const \ + { \ + tuple args(ref(arguments, ref::increment_count)); \ + \ + return BOOST_PYTHON_CONVERSION::to_python( \ + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) oper \ + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type())); \ + } \ + \ + const char* description() const \ + { return "__r" #id "__"; } \ + \ + }; \ + \ + static const char * name() { return "__" #id "__"; } \ + static const char * rname() { return "__r" #id "__"; } \ + } + +#define PY_DEFINE_UNARY_OPERATORS(id, oper) \ + template <> \ + struct define_operator \ + { \ + template \ + struct operator_function : function \ + { \ + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const \ + { \ + tuple args(ref(arguments, ref::increment_count)); \ + \ + return BOOST_PYTHON_CONVERSION::to_python( \ + oper(BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); \ + } \ + \ + const char* description() const \ + { return "__" #id "__"; } \ + }; \ + \ + static const char * name() { return "__" #id "__"; } \ + } + + PY_DEFINE_BINARY_OPERATORS(add, +); + PY_DEFINE_BINARY_OPERATORS(sub, -); + PY_DEFINE_BINARY_OPERATORS(mul, *); + PY_DEFINE_BINARY_OPERATORS(div, /); + PY_DEFINE_BINARY_OPERATORS(mod, %); + PY_DEFINE_BINARY_OPERATORS(lshift, <<); + PY_DEFINE_BINARY_OPERATORS(rshift, >>); + PY_DEFINE_BINARY_OPERATORS(and, &); + PY_DEFINE_BINARY_OPERATORS(xor, ^); + PY_DEFINE_BINARY_OPERATORS(or, |); + + PY_DEFINE_UNARY_OPERATORS(neg, -); + PY_DEFINE_UNARY_OPERATORS(pos, +); + PY_DEFINE_UNARY_OPERATORS(abs, abs); + PY_DEFINE_UNARY_OPERATORS(invert, ~); + PY_DEFINE_UNARY_OPERATORS(int, long); + PY_DEFINE_UNARY_OPERATORS(long, PyLong_FromLong); + PY_DEFINE_UNARY_OPERATORS(float, double); + +#undef PY_DEFINE_BINARY_OPERATORS +#undef PY_DEFINE_UNARY_OPERATORS + +// Some operators need special treatment, e.g. because there is no corresponding +// expression in C++. These are specialized manually. + +// pow(): Manual specialization needed because an error message is required if this +// function is called with three arguments. The "power modulo" operator is not +// supported by define_operator, but can be wrapped manually (see special.html). + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + if (args.size() == 3 && args[2]->ob_type != Py_None->ob_type) + { + PyErr_SetString(PyExc_TypeError, "expected 2 arguments, got 3"); + throw argument_error(); + } + + return BOOST_PYTHON_CONVERSION::to_python( + pow(BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()), + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()))); + } + + const char* description() const + { return "__pow__"; } + + }; + + template + struct roperator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + if (args.size() == 3 && args[2]->ob_type != Py_None->ob_type) + { + PyErr_SetString(PyExc_TypeError, "bad operand type(s) for pow()"); + throw argument_error(); + } + + return BOOST_PYTHON_CONVERSION::to_python( + pow(BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()), + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); + } + + const char* description() const + { return "__rpow__"; } + + }; + + static const char * name() { return "__pow__"; } + static const char * rname() { return "__rpow__"; } + }; + +// divmod(): Manual specialization needed because we must actually call two operators and +// return a tuple containing both results + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + PyObject * res = PyTuple_New(2); + + PyTuple_SET_ITEM(res, 0, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) / + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()))); + PyTuple_SET_ITEM(res, 1, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) % + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()))); + + return res; + } + + const char* description() const + { return "__divmod__"; } + + }; + + template + struct roperator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + PyObject * res = PyTuple_New(2); + + PyTuple_SET_ITEM(res, 0, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) / + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); + PyTuple_SET_ITEM(res, 1, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) % + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); + + return res; + } + + const char* description() const + { return "__rdivmod__"; } + + }; + + static const char * name() { return "__divmod__"; } + static const char * rname() { return "__rdivmod__"; } + }; + +// cmp(): Manual specialization needed because there is no three-way compare in C++. +// It is implemented by two one-way comparisons with operators reversed in the second. + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + return BOOST_PYTHON_CONVERSION::to_python( + (BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type())) ? + - 1 : + (BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type())) ? + 1 : + 0) ; + } + + const char* description() const + { return "__cmp__"; } + + }; + + template + struct roperator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + return BOOST_PYTHON_CONVERSION::to_python( + (BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type())) ? + - 1 : + (BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type())) ? + 1 : + 0) ; + } + + const char* description() const + { return "__rcmp__"; } + + }; + + static const char * name() { return "__cmp__"; } + static const char * rname() { return "__rcmp__"; } + }; + +// str(): Manual specialization needed because the string conversion does not follow +// the standard pattern relized by the macros. + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject*) const + { + tuple args(ref(arguments, ref::increment_count)); + +#if !defined(__GNUC__) || defined(__SGI_STL_PORT) + std::ostringstream s; + s << BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()); +#else + std::ostrstream s; + s << BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) << char(); +#endif + +#if !defined(__GNUC__) || defined(__SGI_STL_PORT) + return BOOST_PYTHON_CONVERSION::to_python(s.str()); +#else + return BOOST_PYTHON_CONVERSION::to_python(const_cast(s.str())); +#endif + } + + const char* description() const + { return "__str__"; } + + }; + + static const char * name() { return "__str__"; } + }; + + +} // namespace detail + +}} // namespace boost::python + +#endif /* OPERATORS_UK112000_H_ */ diff --git a/include/boost/python/reference.hpp b/include/boost/python/reference.hpp new file mode 100644 index 00000000..c633b9d7 --- /dev/null +++ b/include/boost/python/reference.hpp @@ -0,0 +1,173 @@ +// (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 +# include +# include +# include +# include +# include +# include +# include + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + +template +struct py_ptr_conversions : Base +{ + inline friend T from_python(PyObject* x, boost::python::type) + { return T(boost::python::downcast(x).get(), T::increment_count); } + + inline friend T from_python(PyObject* x, boost::python::type) + { return T(boost::python::downcast(x).get(), T::increment_count); } + + inline friend PyObject* to_python(T x) + { return boost::python::as_object(x.release()); } + +}; + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +namespace boost { namespace python { + +BOOST_PYTHON_IMPORT_CONVERSION(py_ptr_conversions); + +template +class reference + : public py_ptr_conversions, T, + boost::dereferenceable, T*> > // supplies op-> +{ +public: + typedef T value_type; + + reference(const reference& rhs) + : m_p(rhs.m_p) + { + Py_XINCREF(object()); + } + +#if !defined(BOOST_MSVC6_OR_EARLIER) + template + reference(const reference& rhs) + : m_p(rhs.object()) + { + Py_XINCREF(object()); + } +#endif + + reference() : 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 increment_count_t { increment_count }; + + enum allow_null { null_ok }; + + template + explicit reference(T2* x) + : m_p(expect_non_null(x)) {} + + template + reference(T2* x, increment_count_t) + : m_p(expect_non_null(x)) { Py_INCREF(object()); } + + template + reference(T2* x, allow_null) + : m_p(x) {} + + template + reference(T2* x, allow_null, increment_count_t) + : m_p(x) { Py_XINCREF(object()); } + + template + reference(T2* x, increment_count_t, allow_null) + : m_p(x) { Py_XINCREF(object()); } + +#if !defined(BOOST_MSVC6_OR_EARLIER) + template + reference& operator=(const reference& rhs) + { + Py_XDECREF(object()); + m_p = rhs.m_p; + Py_XINCREF(object()); + return *this; + } +#endif + + reference& operator=(const reference& rhs) + { + Py_XINCREF(static_cast(rhs.m_p)); + Py_XDECREF(object()); + m_p = rhs.m_p; + return *this; + } + + ~reference() + { + 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, increment_count_t) + { Py_XDECREF(m_p); m_p = expect_non_null(x); Py_INCREF(object()); } + + template + void reset(T2* x, allow_null) + { Py_XDECREF(m_p); m_p = x;} + + template + void reset(T2* x, allow_null, increment_count_t) + { Py_XDECREF(m_p); m_p = x; Py_XINCREF(object()); } + + template + void reset(T2* x, increment_count_t, allow_null) + { 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 reference ref; + +template +ref make_ref(const T& x) +{ + return ref(to_python(x)); +} + +}} // namespace boost::python + +#endif // PYPTR_DWA050400_H_ diff --git a/src/classes.cpp b/src/classes.cpp new file mode 100644 index 00000000..34b42c22 --- /dev/null +++ b/src/classes.cpp @@ -0,0 +1,884 @@ +// (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 +#include +#include +#include +#include +#include +#include + +namespace boost { namespace python { + +namespace detail { + void enable_named_method(boost::python::detail::class_base* type_obj, const char* name); +} + +namespace { + // 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_t<>::class_t() below. + void add_current_module_name(dictionary&); + + bool is_prefix(const char* s1, const char* s2); + bool is_special_name(const char* name); + void enable_special_methods(boost::python::detail::class_base* derived, const tuple& bases, const dictionary& name_space); + + void report_ignored_exception(PyObject* source) + { + // This bit of code copied wholesale from classobject.c in the Python source. + PyObject *f, *t, *v, *tb; + PyErr_Fetch(&t, &v, &tb); + f = PySys_GetObject(const_cast("stderr")); + if (f != NULL) + { + PyFile_WriteString(const_cast("Exception "), f); + if (t) { + PyFile_WriteObject(t, f, Py_PRINT_RAW); + if (v && v != Py_None) { + PyFile_WriteString(const_cast(": "), f); + PyFile_WriteObject(v, f, 0); + } + } + PyFile_WriteString(const_cast(" in "), f); + PyFile_WriteObject(source, f, 0); + PyFile_WriteString(const_cast(" ignored\n"), f); + PyErr_Clear(); /* Just in case */ + } + Py_XDECREF(t); + Py_XDECREF(v); + Py_XDECREF(tb); + } + + // + // pickle support courtesy of "Ralf W. Grosse-Kunstleve" + // + PyObject* class_reduce(PyObject* klass) + { + return PyObject_GetAttrString(klass, const_cast("__name__")); + } + + ref global_class_reduce() + { + static ref result(detail::new_wrapped_function(class_reduce)); + return result; + } + + + tuple instance_reduce(PyObject* obj) + { + ref instance_class(PyObject_GetAttrString(obj, const_cast("__class__"))); + + ref getinitargs(PyObject_GetAttrString(obj, const_cast("__getinitargs__")), + ref::null_ok); + PyErr_Clear(); + ref initargs; + if (getinitargs.get() != 0) + { + initargs = ref(PyEval_CallObject(getinitargs.get(), NULL)); + initargs = ref(PySequence_Tuple(initargs.get())); + } + else + { + initargs = ref(PyTuple_New(0)); + } + + ref getstate(PyObject_GetAttrString(obj, const_cast("__getstate__")), + ref::null_ok); + PyErr_Clear(); + if (getstate.get() != 0) + { + ref state = ref(PyEval_CallObject(getstate.get(), NULL)); + return tuple(instance_class, initargs, state); + } + + ref state(PyObject_GetAttrString(obj, const_cast("__dict__")), ref::null_ok); + PyErr_Clear(); + if (state.get() != 0 && dictionary(state).size() > 0) + { + return tuple(instance_class, initargs, state); + } + + return tuple(instance_class, initargs); + } + + ref global_instance_reduce() + { + static ref result(detail::new_wrapped_function(instance_reduce)); + return result; + } +} + + +namespace detail { + + class_base::class_base(PyTypeObject* meta_class_obj, string name, tuple bases, const dictionary& name_space) + : type_object_base(meta_class_obj), + m_name(name), + m_bases(bases), + m_name_space(name_space) + { + this->tp_name = const_cast(name.c_str()); + enable(type_object_base::getattr); + enable(type_object_base::setattr); + add_current_module_name(m_name_space); + static const boost::python::string docstr("__doc__", boost::python::string::interned); + if (PyDict_GetItem(m_name_space.get(), docstr.get())== 0) + { + PyDict_SetItem(m_name_space.get(), docstr.get(), Py_None); + } + enable_special_methods(this, bases, name_space); + } + + void class_base::add_base(ref 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; + } + + PyObject* class_base::getattr(const char* name) + { + if (!BOOST_CSTD_::strcmp(name, "__dict__")) + { + PyObject* result = m_name_space.get(); + Py_INCREF(result); + return result; + } + + if (!BOOST_CSTD_::strcmp(name, "__bases__")) + { + PyObject* result = m_bases.get(); + Py_INCREF(result); + return result; + } + + if (!BOOST_CSTD_::strcmp(name, "__name__")) + { + PyObject* result = m_name.get(); + Py_INCREF(result); + return result; + } + + // pickle support courtesy of "Ralf W. Grosse-Kunstleve" + if (!BOOST_CSTD_::strcmp(name, "__safe_for_unpickling__")) + { + return PyInt_FromLong(1); + } + if (!BOOST_CSTD_::strcmp(name, "__reduce__")) + { + ref target(as_object(this), ref::increment_count); + return new bound_function(target, global_class_reduce()); + } + + ref 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) + { + // Unwind the actual underlying function from unbound Python class + // methods in case of multiple inheritance from real Python + // classes. Python stubbornly insists that the first argument to a + // method must be a true Python instance object otherwise. Do not + // unwrap bound methods; that would interfere with intended semantics. + if (PyMethod_Check(base_attribute) + && reinterpret_cast(base_attribute)->im_self == 0) + { + PyObject* function + = reinterpret_cast(base_attribute)->im_func; + Py_INCREF(function); + Py_DECREF(base_attribute); + return function; + } + else + { + return base_attribute; + } + } + } + return 0; + } + + // Mostly copied wholesale from Python's classobject.c + PyObject* class_base::repr() const + { + PyObject *mod = PyDict_GetItemString( + m_name_space.get(), const_cast("__module__")); + unsigned long address = reinterpret_cast(this); + string result = (mod == NULL || !PyString_Check(mod)) + ? string("") % tuple(m_name, address) + : string("") % tuple(ref(mod, ref::increment_count), m_name, address); + return result.reference().release(); + } + + + int class_base::setattr(const char* name, PyObject* value) + { + if (is_special_name(name) + && BOOST_CSTD_::strcmp(name, "__doc__") != 0 + && BOOST_CSTD_::strcmp(name, "__name__") != 0) + { + boost::python::string message("Special attribute names other than '__doc__' and '__name__' are read-only, in particular: "); + PyErr_SetObject(PyExc_TypeError, (message + name).get()); + throw error_already_set(); + } + + if (PyCallable_Check(value)) + detail::enable_named_method(this, name); + + return PyDict_SetItemString( + m_name_space.reference().get(), const_cast(name), value); + } + + bool class_base::initialize_instance(instance* obj, PyObject* args, PyObject* keywords) + { + // Getting the init function off the obj should result in a + // bound method. + PyObject* const init_function = obj->getattr("__init__", false); + + if (init_function == 0) + { + if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); // no __init__? That's legal. + } + else { + return false; // Something else? Keep the error + } + } + else + { + // Manage the reference to the bound function + ref init_function_holder(init_function); + + // Declare a ref to manage the result of calling __init__ (which should be None). + ref init_result( + PyEval_CallObjectWithKeywords(init_function, args, keywords)); + } + return true; + } + + void class_base::instance_dealloc(PyObject* obj) const + { + Py_INCREF(obj); // This allows a __del__ function to revive the obj + + PyObject* exc_type; + PyObject* exc_value; + PyObject* exc_traceback; + PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); + + // This scope ensures that the reference held by del_function doesn't release + // the last reference and delete the object recursively (infinitely). + { + ref del_function; + try { + instance* const target = boost::python::downcast(obj); + del_function = ref(target->getattr("__del__", false), ref::null_ok); + } + catch(...) { + } + + if (del_function.get() != 0) + { + ref result(PyEval_CallObject(del_function.get(), (PyObject *)NULL), ref::null_ok); + + if (result.get() == NULL) + report_ignored_exception(del_function.get()); + } + } + PyErr_Restore(exc_type, exc_value, exc_traceback); + + if (--obj->ob_refcnt <= 0) + delete_instance(obj); + } + + +} + +instance::instance(PyTypeObject* class_) + : boost::python::detail::base_object(class_) +{ +} + +instance::~instance() +{ +} + +PyObject* instance::getattr(const char* name, bool use_special_function) +{ + if (!BOOST_CSTD_::strcmp(name, "__dict__")) + { + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, + "instance.__dict__ not accessible in restricted mode"); + return 0; + } + Py_INCREF(m_name_space.get()); + return m_name_space.get(); + } + + if (!BOOST_CSTD_::strcmp(name, "__class__")) + { + Py_INCREF(this->ob_type); + return as_object(this->ob_type); + } + + if (!BOOST_CSTD_::strcmp(name, "__reduce__")) + { + return new detail::bound_function(ref(this, ref::increment_count), global_instance_reduce()); + } + + ref 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)); + + if (function == 0 && !use_special_function) + { + return 0; + } + + ref class_attribute; + if (function != 0) + { + // This will throw if the attribute wasn't found + class_attribute = ref(function); + } + else + { + // Clear the error while we try special methods method (if any). + PyErr_Clear(); + + // 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 (PyErr_Occurred()) + { + PyErr_SetString(PyExc_AttributeError, name); + return 0; + } + + // Take ownership of the method + ref 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 detail::bound_function::create(ref(this, ref::increment_count), class_attribute); + } +} + +// instance::setattr_dict +// +// Implements setattr() functionality for the "__dict__" attribute +// +int instance::setattr_dict(PyObject* value) +{ + if (PyEval_GetRestricted()) + { + PyErr_SetString(PyExc_RuntimeError, + "__dict__ not accessible in restricted mode"); + return -1; + } + + if (value == 0 || !PyDict_Check(value)) + { + PyErr_SetString(PyExc_TypeError, + "__dict__ must be set to a dictionary"); + return -1; + } + m_name_space = dictionary(ref(value, ref::increment_count)); + return 0; +} + +// 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) +{ + if (BOOST_CSTD_::strcmp(name, "__class__") == 0) + { + PyErr_SetString(PyExc_TypeError, "__class__ attribute is read-only"); + throw error_already_set(); + } + + if (BOOST_CSTD_::strcmp(name, "__dict__") == 0) + return setattr_dict(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 + "__"); + + ref special_method( + PyObject_GetAttr(as_object(this->ob_type), specific_method_name.get()), + ref::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()), + ref::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) + { + ref manage_result(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( + ref(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); +} + +PyObject* instance::add(PyObject* other) +{ + return callback::call_method(this, "__add__", other); +} + +PyObject* instance::subtract(PyObject* other) +{ + return callback::call_method(this, "__sub__", other); +} + +PyObject* instance::multiply(PyObject* other) +{ + return callback::call_method(this, "__mul__", other); +} + +PyObject* instance::divide(PyObject* other) +{ + return callback::call_method(this, "__div__", other); +} + +PyObject* instance::remainder(PyObject* other) +{ + return callback::call_method(this, "__mod__", other); +} + +PyObject* instance::divmod(PyObject* other) +{ + return callback::call_method(this, "__divmod__", other); +} + +PyObject* instance::power(PyObject* exponent, PyObject* modulus) +{ + if (as_object(modulus->ob_type) == Py_None) + return callback::call_method(this, "__pow__", exponent); + else + return callback::call_method(this, "__pow__", exponent, modulus); +} + +PyObject* instance::negative() +{ + return callback::call_method(this, "__neg__"); +} + +PyObject* instance::positive() +{ + return callback::call_method(this, "__pos__"); +} + +PyObject* instance::absolute() +{ + return callback::call_method(this, "__abs__"); +} + +int instance::nonzero() +{ + return callback::call_method(this, "__nonzero__"); +} + +PyObject* instance::invert() +{ + return callback::call_method(this, "__invert__"); +} + +PyObject* instance::lshift(PyObject* other) +{ + return callback::call_method(this, "__lshift__", other); +} + +PyObject* instance::rshift(PyObject* other) +{ + return callback::call_method(this, "__rshift__", other); +} + +PyObject* instance::do_and(PyObject* other) +{ + return callback::call_method(this, "__and__", other); +} + +PyObject* instance::do_xor(PyObject* other) +{ + return callback::call_method(this, "__xor__", other); +} + +PyObject* instance::do_or(PyObject* other) +{ + return callback::call_method(this, "__or__", other); +} + +int instance::coerce(PyObject** x, PyObject** y) +{ + assert(this == *x); + + // Coerce must return a tuple + tuple result(callback::call_method(this, "__coerce__", *y)); + + *x = result[0].release(); + *y = result[1].release(); + return 0; +} + +PyObject* instance::as_int() +{ + return callback::call_method(this, "__int__"); +} + +PyObject* instance::as_long() +{ + return callback::call_method(this, "__long__"); +} + +PyObject* instance::as_float() +{ + return callback::call_method(this, "__float__"); +} + +PyObject* instance::oct() +{ + return callback::call_method(this, "__oct__"); +} + +PyObject* instance::hex() +{ + return callback::call_method(this, "__hex__"); +} + +namespace { + struct named_capability + { + const char* name; + detail::type_object_base::capability capability; + }; + + const named_capability enablers[] = + { + { "__hash__", detail::type_object_base::hash }, + { "__cmp__", detail::type_object_base::compare }, + { "__repr__", detail::type_object_base::repr }, + { "__str__", detail::type_object_base::str }, + { "__call__", detail::type_object_base::call }, + { "__getattr__", detail::type_object_base::getattr }, + { "__setattr__", detail::type_object_base::setattr }, + { "__len__", detail::type_object_base::mapping_length }, + { "__len__", detail::type_object_base::sequence_length }, + { "__getitem__", detail::type_object_base::mapping_subscript }, + { "__getitem__", detail::type_object_base::sequence_item }, + { "__setitem__", detail::type_object_base::mapping_ass_subscript }, + { "__setitem__", detail::type_object_base::sequence_ass_item }, + { "__delitem__", detail::type_object_base::mapping_ass_subscript }, + { "__delitem__", detail::type_object_base::sequence_ass_item }, + { "__getslice__", detail::type_object_base::sequence_slice }, + { "__setslice__", detail::type_object_base::sequence_ass_slice }, + { "__delslice__", detail::type_object_base::sequence_ass_slice }, + { "__add__", detail::type_object_base::number_add }, + { "__sub__", detail::type_object_base::number_subtract }, + { "__mul__", detail::type_object_base::number_multiply }, + { "__div__", detail::type_object_base::number_divide }, + { "__mod__", detail::type_object_base::number_remainder }, + { "__divmod__", detail::type_object_base::number_divmod }, + { "__pow__", detail::type_object_base::number_power }, + { "__neg__", detail::type_object_base::number_negative }, + { "__pos__", detail::type_object_base::number_positive }, + { "__abs__", detail::type_object_base::number_absolute }, + { "__nonzero__", detail::type_object_base::number_nonzero }, + { "__invert__", detail::type_object_base::number_invert }, + { "__lshift__", detail::type_object_base::number_lshift }, + { "__rshift__", detail::type_object_base::number_rshift }, + { "__and__", detail::type_object_base::number_and }, + { "__xor__", detail::type_object_base::number_xor }, + { "__or__", detail::type_object_base::number_or }, + { "__coerce__", detail::type_object_base::number_coerce }, + { "__int__", detail::type_object_base::number_int }, + { "__long__", detail::type_object_base::number_long }, + { "__float__", detail::type_object_base::number_float }, + { "__oct__", detail::type_object_base::number_oct }, + { "__hex__", detail::type_object_base::number_hex } + }; + + 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 = BOOST_CSTD_::strlen(name); + return name[name_length - 1] == '_' && name[name_length - 2] == '_'; + } +} + +namespace detail { + // Enable the special handler for methods of the given name, if any. + void enable_named_method(boost::python::detail::class_base* type_obj, 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_obj->enable(enablers[i].capability); + } + } + } +} + +namespace { + // Enable any special methods which are enabled in the base class. + void enable_special_methods(boost::python::detail::class_base* derived, const tuple& bases, const dictionary& name_space) + { + for (std::size_t i = 0; i < bases.size(); ++i) + { + PyObject* base = bases[i].get(); + + for (std::size_t n = 0; n < PY_ARRAY_LENGTH(enablers); ++n) + { + ref attribute( + PyObject_GetAttrString(base, const_cast(enablers[n].name)), + ref::null_ok); + PyErr_Clear(); + if (attribute.get() != 0 && PyCallable_Check(attribute.get())) + detail::add_capability(enablers[n].capability, derived); + } + } + + list keys(name_space.keys()); + for (std::size_t j = 0, len = keys.size(); j < len; ++j) + { + string name_obj(keys.get_item(j)); + const char* name = name_obj.c_str(); + + 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); + } + } + } + } + + void add_current_module_name(dictionary& name_space) + { + static string module_key("__module__", string::interned); + name_space.set_item(module_key, module_builder::name()); + } +} + +void adjust_slice_indices(PyObject* obj, int& start, int& finish) +{ + int length = callback::call_method(obj, "__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 boost::python diff --git a/src/conversions.cpp b/src/conversions.cpp new file mode 100644 index 00000000..ded01e0e --- /dev/null +++ b/src/conversions.cpp @@ -0,0 +1,241 @@ +// (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 +#include +#include +#ifndef BOOST_NO_LIMITS +# include +#endif + +namespace boost { namespace python { + +// 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 boost::python::error_already_set&) + { + // 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"); + } +} + +}} // namespace boost::python + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + +long from_python(PyObject* p, boost::python::type) +{ + // Why am I clearing the error here before trying to convert? I know there's a reason... + long result; + { + result = PyInt_AsLong(p); + if (PyErr_Occurred()) + throw boost::python::argument_error(); + } + return result; +} + +double from_python(PyObject* p, boost::python::type) +{ + double result; + { + result = PyFloat_AsDouble(p); + if (PyErr_Occurred()) + throw boost::python::argument_error(); + } + return result; +} + +template +T integer_from_python(PyObject* p, boost::python::type) +{ + const long long_result = from_python(p, boost::python::type()); + +#ifndef BOOST_NO_LIMITS + try + { + return boost::numeric_cast(long_result); + } + catch(const boost::bad_numeric_cast&) +#else + if (static_cast(long_result) == long_result) + { + return static_cast(long_result); + } + else +#endif + { + 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 boost::python::argument_error(); + } +#if defined(__MWERKS__) && __MWERKS__ <= 0x2400 + 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; + +#ifndef BOOST_NO_LIMITS + try + { + value_as_long = boost::numeric_cast(value); + } + catch(const boost::bad_numeric_cast&) +#else + value_as_long = static_cast(value); + if (value_as_long != value) +#endif + { + const char message[] = "value out of range for Python int"; + PyErr_SetString(PyExc_ValueError, message); + throw boost::python::error_already_set(); + } + + return to_python(value_as_long); +} + +int from_python(PyObject* p, boost::python::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, boost::python::type type) +{ + return integer_from_python(p, type); +} + +short from_python(PyObject* p, boost::python::type type) +{ + return integer_from_python(p, type); +} + +float from_python(PyObject* p, boost::python::type) +{ + return static_cast(from_python(p, boost::python::type())); +} + +PyObject* to_python(unsigned short i) +{ + return integer_to_python(i); +} + +unsigned short from_python(PyObject* p, boost::python::type type) +{ + return integer_from_python(p, type); +} + +PyObject* to_python(unsigned char i) +{ + return integer_to_python(i); +} + +unsigned char from_python(PyObject* p, boost::python::type type) +{ + return integer_from_python(p, type); +} + +PyObject* to_python(signed char i) +{ + return integer_to_python(i); +} + +signed char from_python(PyObject* p, boost::python::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, boost::python::type type) +{ + return integer_from_python(p, type); +} + +void from_python(PyObject* p, boost::python::type) +{ + if (p != Py_None) { + PyErr_SetString(PyExc_TypeError, "expected argument of type None"); + throw boost::python::argument_error(); + } +} + +const char* from_python(PyObject* p, boost::python::type) +{ + const char* s = PyString_AsString(p); + if (!s) + throw boost::python::argument_error(); + return s; +} + +PyObject* to_python(const std::string& s) +{ + return PyString_FromString(s.c_str()); +} + +std::string from_python(PyObject* p, boost::python::type) +{ + return std::string(from_python(p, boost::python::type())); +} + +bool from_python(PyObject* p, boost::python::type) +{ + int value = from_python(p, boost::python::type()); + if (value == 0) + return false; + return true; +} + +#ifdef BOOST_MSVC6_OR_EARLIER +// An optimizer bug prevents these from being inlined. +PyObject* to_python(double d) +{ + return PyFloat_FromDouble(d); +} + +PyObject* to_python(float f) +{ + return PyFloat_FromDouble(f); +} +#endif // BOOST_MSVC6_OR_EARLIER + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + diff --git a/src/extension_class.cpp b/src/extension_class.cpp new file mode 100644 index 00000000..cde3d836 --- /dev/null +++ b/src/extension_class.cpp @@ -0,0 +1,683 @@ +// (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 +#include +#include + +namespace boost { namespace python { +namespace detail { + + struct operator_dispatcher + : public PyObject + { + static PyTypeObject type_obj; + static PyNumberMethods number_methods; + + static operator_dispatcher* create(const ref& o, const ref& s); + + ref m_object; + ref m_self; + + // data members for allocation/deallocation optimization + operator_dispatcher* m_free_list_link; + static operator_dispatcher* free_list; + + private: + // only accessible through create() + operator_dispatcher(const ref& o, const ref& s); + }; + + operator_dispatcher* operator_dispatcher::free_list = 0; + +}}} // namespace boost::python::detail + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + +inline PyObject* to_python(boost::python::detail::operator_dispatcher* n) { return n; } + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + + +namespace boost { namespace python { + +namespace detail { + + tuple extension_class_coerce(ref l, ref r) + { + // Introduced sequence points for exception-safety. + ref first(operator_dispatcher::create(l, l)); + ref second; + + if(r->ob_type == &operator_dispatcher::type_obj) + { + second = r; + } + else + { + second = ref(operator_dispatcher::create(r, ref())); + } + return boost::python::tuple(first, second); + } + + enum { unwrap_exception_code = -1000 }; + + int unwrap_args(PyObject* left, PyObject* right, PyObject*& self, PyObject*& other) + { + if (left->ob_type != &operator_dispatcher::type_obj || + right->ob_type != &operator_dispatcher::type_obj) + { + PyErr_SetString(PyExc_RuntimeError, "operator_dispatcher::unwrap_args(): expecting operator_dispatcher arguments only!"); + return unwrap_exception_code; + } + + typedef reference DPtr; + DPtr lwrapper(static_cast(left), DPtr::increment_count); + DPtr rwrapper(static_cast(right), DPtr::increment_count); + + if (lwrapper->m_self.get() != 0) + { + self = lwrapper->m_self.get(); + other = rwrapper->m_object.get(); + return 0; + } + else + { + self = rwrapper->m_self.get(); + other = lwrapper->m_object.get(); + return 1; + } + } + + int unwrap_pow_args(PyObject* left, PyObject* right, PyObject* m, + PyObject*& self, PyObject*& first, PyObject*& second) + { + if (left->ob_type != &operator_dispatcher::type_obj || + right->ob_type != &operator_dispatcher::type_obj || + m->ob_type != &operator_dispatcher::type_obj) + { + PyErr_SetString(PyExc_RuntimeError, "operator_dispatcher::unwrap_pow_args(): expecting operator_dispatcher arguments only!"); + return unwrap_exception_code; + } + + typedef reference DPtr; + DPtr lwrapper(static_cast(left), DPtr::increment_count); + DPtr rwrapper(static_cast(right), DPtr::increment_count); + DPtr mwrapper(static_cast(m), DPtr::increment_count); + + if (lwrapper->m_self.get() != 0) + { + self = lwrapper->m_self.get(); + first = rwrapper->m_object.get(); + second = mwrapper->m_object.get(); + return 0; + } + else if (rwrapper->m_self.get() != 0) + { + self = rwrapper->m_self.get(); + first = lwrapper->m_object.get(); + second = mwrapper->m_object.get(); + return 1; + } + else + { + self = mwrapper->m_self.get(); + first = lwrapper->m_object.get(); + second = rwrapper->m_object.get(); + return 2; + } + } + +extension_instance* get_extension_instance(PyObject* p) +{ + // The object's type will just be some class_t object, + // but if its meta-type is right, then it is an extension_instance. + if (p->ob_type->ob_type != extension_meta_class()) + { + PyErr_SetString(PyExc_TypeError, p->ob_type->tp_name); + throw boost::python::argument_error(); + } + return static_cast(p); +} + +void +extension_instance::add_implementation(std::auto_ptr holder) +{ + for (held_objects::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 error_already_set(); + } + } + m_wrapped_objects.push_back(holder.release()); +} + +extension_instance::extension_instance(PyTypeObject* class_) + : instance(class_) +{ +} + +extension_instance::~extension_instance() +{ + for (held_objects::const_iterator p = m_wrapped_objects.begin(), + finish = m_wrapped_objects.end(); + p != finish; ++p) + { + delete *p; + } +} + +meta_class* extension_meta_class() +{ + static meta_class result; + return &result; +} + +typedef class_t extension_class_t; + +bool is_subclass(const extension_class_t* 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 extension_class_t* base_class = downcast(base); + if (is_subclass(base_class, possible_base)) + return true; + } + } + return false; +} + +// Return true iff obj is an obj of target_class +bool is_instance(extension_instance* obj, + class_t* target_class) +{ + if (obj->ob_type == target_class) + return true; + else + { + return is_subclass( + downcast >(obj->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 = BOOST_CSTD_::strlen(format); + std::size_t length1 = BOOST_CSTD_::strlen(s1); + std::size_t length2 = BOOST_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 argument_error(); + else + throw error_already_set(); +} + +// This is called when an attempt has been made to convert the given obj to +// a C++ type for which it doesn't have any obj data. In that case, either +// the obj was not derived from the target_class, or the appropriate +// __init__ function wasn't called to initialize the obj data of the target class. +void report_missing_instance_data( + extension_instance* obj, // The object being converted + class_t* 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(obj, target_class)) + { + if (target_is_ptr) + { + two_string_error(PyExc_RuntimeError, + "Object of extension class '%.*s' does not wrap <%.*s>.", + obj->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 convertible into '%.*s'.", + obj->ob_type->tp_name, target_class->tp_name); + } +} + +void report_missing_instance_data( + extension_instance* obj, // The object being converted + class_t* 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(obj, target_class, target_typeid, false); +} + +void report_missing_ptr_data( + extension_instance* obj, // The object being converted + class_t* 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(obj, 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 error_already_set(); +} + +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 argument_error(); +} + +read_only_setattr_function::read_only_setattr_function(const char* name) + : m_name(name) +{ +} + +PyObject* read_only_setattr_function::do_call(PyObject* /*args*/, PyObject* /*keywords*/) const +{ + PyErr_SetObject(PyExc_AttributeError, ("'" + m_name + "' attribute is read-only").get()); + return 0; +} + +const char* read_only_setattr_function::description() const +{ + return "uncallable"; +} + +extension_class_base::extension_class_base(const char* name) + : class_t( + extension_meta_class(), string(name), tuple(), dictionary()) +{ +} + +// This function is used in from_python() to convert wrapped classes that are +// related by inheritance. The problem is this: although C++ provides all necessary +// conversion operators, source and target of a conversion must be known at compile +// time. However, in Python we want to convert classes at runtime. The solution is to +// generate conversion functions at compile time, register them within the appropriate +// class objects and call them when a particular runtime conversion is required. + +// If functions for any possible conversion have to be stored, their number will grow +// qudratically. To reduce this number, we actually store only conversion functions +// between adjacent levels in the inheritance tree. By traversing the tree recursively, +// we can build any allowed conversion as a concatenation of simple conversions. This +// traversal is done in the functions try_base_class_conversions() and +// try_derived_class_conversions(). If a particular conversion is impossible, all +// conversion functions will return a NULL pointer. + +// The function extract_object_from_holder() attempts to actually extract the pointer +// to the contained object from an instance_holder_base (a wrapper class). A conversion +// of the held object to 'T *' is allowed when the conversion +// 'dynamic_cast *>(an_instance_holder_base)' succeeds. +void* extension_class_base::try_class_conversions(instance_holder_base* object) const +{ + void* result = try_derived_class_conversions(object); + if (result) + return result; + + if (!object->held_by_value()) + return try_base_class_conversions(object); + else + return 0; +} + +void* extension_class_base::try_base_class_conversions(instance_holder_base* object) const +{ + for (std::size_t i = 0; i < base_classes().size(); ++i) + { + if (base_classes()[i].convert == 0) + continue; + void* result1 = base_classes()[i].class_object->extract_object_from_holder(object); + if (result1) + return (*base_classes()[i].convert)(result1); + + void* result2 = base_classes()[i].class_object->try_base_class_conversions(object); + if (result2) + return (*base_classes()[i].convert)(result2); + } + return 0; +} + +void* extension_class_base::try_derived_class_conversions(instance_holder_base* object) const +{ + for (std::size_t i = 0; i < derived_classes().size(); ++i) + { + void* result1 = derived_classes()[i].class_object->extract_object_from_holder(object); + if (result1) + return (*derived_classes()[i].convert)(result1); + + void* result2 = derived_classes()[i].class_object->try_derived_class_conversions(object); + if (result2) + return (*derived_classes()[i].convert)(result2); + } + return 0; +} + +void extension_class_base::add_method(function* method, const char* name) +{ + add_method(reference(method), name); +} + +void extension_class_base::add_method(reference method, const char* name) +{ + // Add the attribute to the computed target + function::add_to_namespace(method, name, this->dict().get()); + + // If it is a special member function it should be enabled both here and there. + detail::enable_named_method(this, name); +} + +void extension_class_base::add_constructor_object(function* init_fn) +{ + add_method(init_fn, "__init__"); +} + +void extension_class_base::add_setter_method(function* setter_, const char* name) +{ + reference setter(setter_); + add_method(setter, (detail::setattr_string() + name + "__").c_str()); +} + +void extension_class_base::add_getter_method(function* getter_, const char* name) +{ + reference getter(getter_); + add_method(getter, (detail::getattr_string() + name + "__").c_str()); +} + +void extension_class_base::set_attribute(const char* name, PyObject* x_) +{ + ref x(x_); + set_attribute(name, x); +} + +void extension_class_base::set_attribute(const char* name, ref x) +{ + dict().set_item(string(name), x); + if (PyCallable_Check(x.get())) + detail::enable_named_method(this, name); +} + +operator_dispatcher::operator_dispatcher(const ref& o, const ref& s) + : m_object(o), m_self(s), m_free_list_link(0) + +{ + ob_refcnt = 1; + ob_type = &type_obj; +} + +operator_dispatcher* +operator_dispatcher::create(const ref& object, const ref& self) +{ + operator_dispatcher* const result = free_list; + if (result == 0) + return new operator_dispatcher(object, self); + + free_list = result->m_free_list_link; + result->m_object = object; + result->m_self = self; + Py_INCREF(result); + return result; +} + +extern "C" +{ + +void operator_dispatcher_dealloc(PyObject* self) +{ + operator_dispatcher* obj = static_cast(self); + obj->m_free_list_link = operator_dispatcher::free_list; + operator_dispatcher::free_list = obj; + obj->m_object.reset(); + obj->m_self.reset(); +} + +int operator_dispatcher_coerce(PyObject** l, PyObject** r) +{ + Py_INCREF(*l); + try + { + *r = operator_dispatcher::create(ref(*r, ref::increment_count), ref()); + } + catch(...) + { + handle_exception(); + return -1; + } + return 0; +} + + +#define PY_DEFINE_OPERATOR(id, symbol) \ + PyObject* operator_dispatcher_call_##id(PyObject* left, PyObject* right) \ + { \ + /* unwrap the arguments from their OperatorDispatcher */ \ + PyObject* self; \ + PyObject* other; \ + int reverse = unwrap_args(left, right, self, other); \ + if (reverse == unwrap_exception_code) \ + return 0; \ + \ + /* call the function */ \ + PyObject* result = \ + PyEval_CallMethod(self, \ + const_cast(reverse ? "__r" #id "__" : "__" #id "__"), \ + const_cast("(O)"), \ + other); \ + if (result == 0 && PyErr_GivenExceptionMatches(PyErr_Occurred(), PyExc_AttributeError)) \ + { \ + PyErr_Clear(); \ + PyErr_SetString(PyExc_TypeError, "bad operand type(s) for " #symbol); \ + } \ + return result; \ + } + +PY_DEFINE_OPERATOR(add, +) +PY_DEFINE_OPERATOR(sub, -) +PY_DEFINE_OPERATOR(mul, *) +PY_DEFINE_OPERATOR(div, /) +PY_DEFINE_OPERATOR(mod, %) +PY_DEFINE_OPERATOR(divmod, divmod) +PY_DEFINE_OPERATOR(lshift, <<) +PY_DEFINE_OPERATOR(rshift, >>) +PY_DEFINE_OPERATOR(and, &) +PY_DEFINE_OPERATOR(xor, ^) +PY_DEFINE_OPERATOR(or, |) + +/* coercion rules for heterogeneous pow(): + pow(Foo, int): left, right coerced; m: None => reverse = 0 + pow(int, Foo): left, right coerced; m: None => reverse = 1 + pow(Foo, int, int): left, right, m coerced => reverse = 0 + pow(int, Foo, int): left, right, m coerced => reverse = 1 + pow(int, int, Foo): left, right, m coerced => reverse = 2 + pow(Foo, Foo, int): left, right coerced; m coerced twice => reverse = 0 + pow(Foo, int, Foo): left, right, m coerced => reverse = 0 + pow(int, Foo, Foo): left, right, m coerced => reverse = 1 +*/ +PyObject* operator_dispatcher_call_pow(PyObject* left, PyObject* right, PyObject* m) +{ + int reverse; + PyObject* self; + PyObject* first; + PyObject* second; + + if (m->ob_type == Py_None->ob_type) + { + reverse = unwrap_args(left, right, self, first); + second = m; + } + else + { + reverse = unwrap_pow_args(left, right, m, self, first, second); + } + + if (reverse == unwrap_exception_code) + return 0; + + // call the function + PyObject* result = + PyEval_CallMethod(self, + const_cast((reverse == 0) + ? "__pow__" + : (reverse == 1) + ? "__rpow__" + : "__rrpow__"), + const_cast("(OO)"), + first, second); + if (result == 0 && + (PyErr_GivenExceptionMatches(PyErr_Occurred(), PyExc_TypeError) || + PyErr_GivenExceptionMatches(PyErr_Occurred(), PyExc_AttributeError))) + { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, "bad operand type(s) for pow()"); + } + return result; +} + +int operator_dispatcher_call_cmp(PyObject* left, PyObject* right) +{ + // unwrap the arguments from their OperatorDispatcher + PyObject* self; + PyObject* other; + int reverse = unwrap_args(left, right, self, other); + if (reverse == unwrap_exception_code) + return -1; + + // call the function + PyObject* result = + PyEval_CallMethod(self, + const_cast(reverse ? "__rcmp__" : "__cmp__"), + const_cast("(O)"), + other); + if (result == 0) + { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, "bad operand type(s) for cmp() or <"); + return -1; + } + else + { + try + { + return BOOST_PYTHON_CONVERSION::from_python(result, type()); + } + catch(...) + { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, "cmp() didn't return int"); + return -1; + } + } +} + +} // extern "C" + +PyTypeObject operator_dispatcher::type_obj = +{ + PyObject_HEAD_INIT(&PyType_Type) + 0, + const_cast("operator_dispatcher"), + sizeof(operator_dispatcher), + 0, + &operator_dispatcher_dealloc, + 0, + 0, + 0, + &operator_dispatcher_call_cmp, + 0, + &operator_dispatcher::number_methods, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +PyNumberMethods operator_dispatcher::number_methods = +{ + &operator_dispatcher_call_add, + &operator_dispatcher_call_sub, + &operator_dispatcher_call_mul, + &operator_dispatcher_call_div, + &operator_dispatcher_call_mod, + &operator_dispatcher_call_divmod, + &operator_dispatcher_call_pow, + 0, + 0, + 0, + 0, + 0, + &operator_dispatcher_call_lshift, + &operator_dispatcher_call_rshift, + &operator_dispatcher_call_and, + &operator_dispatcher_call_xor, + &operator_dispatcher_call_or, + &operator_dispatcher_coerce, + 0, + 0, + 0, + 0, + 0 +}; + +} // namespace detail + +}} // namespace boost::python diff --git a/src/functions.cpp b/src/functions.cpp new file mode 100644 index 00000000..e166c91c --- /dev/null +++ b/src/functions.cpp @@ -0,0 +1,167 @@ +// (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 +#include +#include +#include +#include + +namespace boost { namespace python { namespace detail { + +struct function::type_object : + singleton > > +{ + type_object() : singleton_base(&PyType_Type) {} +}; + + +void function::add_to_namespace(reference new_function, const char* name, PyObject* dict) +{ + dictionary d(ref(dict, ref::increment_count)); + string key(name); + + ref existing_object = d.get_item(key.reference()); + if (existing_object.get() == 0) + { + d[key] = ref(new_function.get(), ref::increment_count); + } + else + { + if (existing_object->ob_type == type_object::instance()) + { + 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 error_already_set(); + } + } +} + +function::function() + : python_object(type_object::instance()) +{ +} + +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 argument_error&) + { + } + } + + if (m_overloads.get() == 0) + return 0; + + PyErr_Clear(); + string message("No overloaded functions match ("); + tuple arguments(ref(args, ref::increment_count)); + 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; +} + +bound_function* bound_function::create(const ref& target, const ref& fn) +{ + bound_function* const result = free_list; + if (result == 0) + return new bound_function(target, fn); + + free_list = result->m_free_list_link; + result->m_target = target; + result->m_unbound_function = fn; + Py_INCREF(result); + return result; +} + +// The instance class whose obj represents the type of bound_function +// objects in Python. bound_functions must be GetAttrable so the __doc__ +// attribute of built-in Python functions can be accessed when bound. +struct bound_function::type_object : + singleton > > > +{ + type_object() : singleton_base(&PyType_Type) {} + +private: // type_object hook override + void dealloc(bound_function*) const; +}; + +bound_function::bound_function(const ref& target, const ref& fn) + : python_object(type_object::instance()), + m_target(target), + m_unbound_function(fn), + m_free_list_link(0) +{ +} + +PyObject* +bound_function::call(PyObject* args, PyObject* keywords) const +{ + // Build a new tuple which prepends the target to the arguments + tuple tail_arguments(ref(args, ref::increment_count)); + ref 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); +} + +PyObject* bound_function::getattr(const char* name) const +{ + return PyObject_GetAttrString(m_unbound_function.get(), const_cast(name)); +} + +void bound_function::type_object::dealloc(bound_function* obj) const +{ + obj->m_free_list_link = free_list; + free_list = obj; + obj->m_target.reset(); + obj->m_unbound_function.reset(); +} + +bound_function* bound_function::free_list; + +}}} // namespace boost::python::detail diff --git a/src/gen_all.py b/src/gen_all.py new file mode 100644 index 00000000..fd8d78cc --- /dev/null +++ b/src/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('instance.h', 'w').write(gen_singleton(args)) + open('extclass.h', 'w').write(gen_extclass(args)) + +if __name__ == '__main__': + import sys + + if len(sys.argv) == 1: + args = 10 + else: + args = int(sys.argv[1]) + + print gen_all(args) + + diff --git a/src/gen_callback.py b/src/gen_callback.py new file mode 100644 index 00000000..f178212b --- /dev/null +++ b/src/gen_callback.py @@ -0,0 +1,124 @@ +from gen_function import * +import string + +def gen_callback(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 python callbacks by gen_callback.python + +#ifndef CALLBACK_DWA_052100_H_ +# define CALLBACK_DWA_052100_H_ + +# include +# include + +namespace boost { namespace python { + +namespace detail { + template + inline void callback_adjust_refcount(PyObject*, type) {} + + inline void callback_adjust_refcount(PyObject* p, type) + { Py_INCREF(p); } +} + +// Calling Python from C++ +template +struct callback +{""" % args + + + gen_functions(''' +%{ template <%(class A%n%:, %)> +%} static R call_method(PyObject* self, const char* name%(, const A%n& a%n%)) + {%( + ref p%n(to_python(a%n));%) + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(%(O%))")%(, + p%n.get()%))); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } + +%{ template <%(class A%n%:, %)> +%} static R call(PyObject* self%(, const A%n& a%n%)) + {%( + ref p%n(to_python(a%n));%) + ref result(PyEval_CallFunction(self, const_cast("(%(O%))")%(, + p%n.get()%))); + detail::callback_adjust_refcount(result.get(), type()); + return from_python(result.get(), type()); + } +''', args) + + +"""}; + +// 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(''' +%{ template <%(class A%n%:, %)> +%} static void call_method(PyObject* self, const char* name%(, const A%n& a%n%)) + {%( + ref p%n(to_python(a%n));%) + ref result(PyEval_CallMethod(self, const_cast(name), + const_cast("(%(O%))")%(, + p%n.get()%))); + } + +%{ template <%(class A%n%:, %)> +%} static void call(PyObject* self%(, const A%n& a%n%)) + {%( + ref p%n(to_python(a%n));%) + ref result(PyEval_CallFunction(self, const_cast("(%(O%))")%(, + p%n.get()%))); + } +''', args) + + +"""}; + +// Make it a compile-time error to try to return a const char* from a virtual +// function. The standard conversion +// +// from_python(PyObject* string, boost::python::type) +// +// returns a pointer to the character array which is internal to string. The +// problem with trying to do this in a standard callback function is that the +// Python string would likely be destroyed upon return from the calling function +// (boost::python::callback::call[_method]) when its reference count is +// decremented. If you absolutely need to do this and you're sure it's safe (it +// usually isn't), you can use +// +// boost::python::string result(boost::python::callback::call[_method](...args...)); +// ...result.c_str()... // access the char* array +template <> +struct callback +{ + // Try hard to generate a readable error message + typedef struct unsafe_since_python_string_may_be_destroyed {} call, call_method; +}; + +}} // namespace boost::python + +#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/src/gen_caller.py b/src/gen_caller.py new file mode 100644 index 00000000..26bd88c6 --- /dev/null +++ b/src/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.python +''' + +body_sections = ( +''' +#ifndef CALLER_DWA05090_H_ +# define CALLER_DWA05090_H_ + +# include +# include +# include +# include +# include + +namespace boost { namespace python { + +// Calling C++ from Python +template +struct caller +{ +''', +''' +''', +''' // Free functions +''', +'''}; + +template <> +struct caller +{ +''', +''' +''', +''' + // Free functions +''', +'''}; + +}} // namespace boost::python + +#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 + 1 + + return_none = '''; + return detail::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/src/gen_extclass.py b/src/gen_extclass.py new file mode 100644 index 00000000..5180a3de --- /dev/null +++ b/src/gen_extclass.py @@ -0,0 +1,830 @@ +from gen_function import * +import string + +def gen_extclass(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 for %d-argument constructors by +// gen_extclass.python + +#ifndef EXTENSION_CLASS_DWA052000_H_ +# define EXTENSION_CLASS_DWA052000_H_ + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +namespace boost { namespace python { + +// forward declarations +template struct operators; +template struct left_operand; +template struct right_operand; + +enum without_downcast_t { without_downcast }; + +namespace detail { + +// forward declarations +class extension_instance; +class extension_class_base; +template class instance_holder; +template class instance_value_holder; +template class instance_ptr_holder; +template struct operand_select; + template struct choose_op; + template struct choose_rop; + template struct choose_unary_op; + template struct define_operator; + +meta_class* extension_meta_class(); +extension_instance* get_extension_instance(PyObject* p); +void report_missing_instance_data(extension_instance*, class_t*, const std::type_info&); +void report_missing_ptr_data(extension_instance*, class_t*, const std::type_info&); +void report_missing_class_object(const std::type_info&); +void report_released_smart_pointer(const std::type_info&); + +template +T* check_non_null(T* p) +{ + if (p == 0) + report_released_smart_pointer(typeid(T)); + return p; +} + +template class held_instance; + +typedef void* (*conversion_function_ptr)(void*); + +struct base_class_info +{ + base_class_info(extension_class_base* t, conversion_function_ptr f) + :class_object(t), convert(f) + {} + + extension_class_base* class_object; + conversion_function_ptr convert; +}; + +typedef base_class_info derived_class_info; + +struct add_operator_base; + +class extension_class_base : public class_t +{ + public: + extension_class_base(const char* name); + + public: + // the purpose of try_class_conversions() and its related functions + // is explained in extclass.cpp + void* try_class_conversions(instance_holder_base*) const; + void* try_base_class_conversions(instance_holder_base*) const; + void* try_derived_class_conversions(instance_holder_base*) const; + + void set_attribute(const char* name, PyObject* x); + void set_attribute(const char* name, ref x); + + private: + virtual void* extract_object_from_holder(instance_holder_base* v) const = 0; + virtual std::vector const& base_classes() const = 0; + virtual std::vector const& derived_classes() const = 0; + + protected: + friend struct add_operator_base; + void add_method(reference method, const char* name); + void add_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 class_registry +{ + public: + static extension_class_base* class_object() + { return static_class_object; } + + // Register/unregister the Python class object corresponding to T + static void register_class(extension_class_base*); + static void unregister_class(extension_class_base*); + + // Establish C++ inheritance relationships + static void register_base_class(base_class_info const&); + static void register_derived_class(derived_class_info const&); + + // Query the C++ inheritance relationships + static std::vector const& base_classes(); + static std::vector const& derived_classes(); + private: + static extension_class_base* static_class_object; + static std::vector static_base_class_info; + static std::vector static_derived_class_info; +}; + +}}} // namespace boost::python::detail + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + +// 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 = held_instance. +template > +class python_extension_class_converters +{ + public: + // Get an object which can be used to convert T to/from python. This is used + // as a kind of concept check by the global template + // + // PyObject* to_python(const T& x) + // + // below this class, to prevent the confusing messages that would otherwise + // pop up. Now, if T hasn't been wrapped as an extension class, the user + // will see an error message about the lack of an eligible + // py_extension_class_converters() function. + friend python_extension_class_converters py_extension_class_converters(boost::python::type) + { + return python_extension_class_converters(); + } + + // This is a member function because in a conforming implementation, friend + // funcitons defined inline in the class body are all instantiated as soon + // as the enclosing class is instantiated. If T is not copyable, that causes + // a compiler error. Instead, we access this function through the global + // template + // + // PyObject* to_python(const T& x) + // + // defined below this class. Since template functions are instantiated only + // on demand, errors will be avoided unless T is noncopyable and the user + // writes code which causes us to try to copy a T. + PyObject* to_python(const T& x) const + { + boost::python::reference result(create_instance()); + result->add_implementation( + std::auto_ptr( + new boost::python::detail::instance_value_holder(result.get(), x))); + return result.release(); + } + + // Convert to T* + friend T* from_python(PyObject* obj, boost::python::type) + { + // downcast to an extension_instance, then find the actual T + boost::python::detail::extension_instance* self = boost::python::detail::get_extension_instance(obj); + typedef std::vector::const_iterator iterator; + for (iterator p = self->wrapped_objects().begin(); + p != self->wrapped_objects().end(); ++p) + { + boost::python::detail::instance_holder* held = dynamic_cast*>(*p); + if (held != 0) + return held->target(); + + // see extclass.cpp for an explanation of try_class_conversions() + void* target = boost::python::detail::class_registry::class_object()->try_class_conversions(*p); + if(target) + return static_cast(target); + } + boost::python::detail::report_missing_instance_data(self, boost::python::detail::class_registry::class_object(), typeid(T)); + throw boost::python::argument_error(); + } + + // Convert to PtrType, where PtrType can be dereferenced to obtain a T. + template + static PtrType& ptr_from_python(PyObject* obj, boost::python::type) + { + // downcast to an extension_instance, then find the actual T + boost::python::detail::extension_instance* self = boost::python::detail::get_extension_instance(obj); + typedef std::vector::const_iterator iterator; + for (iterator p = self->wrapped_objects().begin(); + p != self->wrapped_objects().end(); ++p) + { + boost::python::detail::instance_ptr_holder* held = + dynamic_cast*>(*p); + if (held != 0) + return held->ptr(); + } + boost::python::detail::report_missing_ptr_data(self, boost::python::detail::class_registry::class_object(), typeid(T)); + throw boost::python::argument_error(); + } + + template + static PyObject* ptr_to_python(PtrType x) + { + boost::python::reference result(create_instance()); + result->add_implementation( + std::auto_ptr( + new boost::python::detail::instance_ptr_holder(x))); + return result.release(); + } + + static boost::python::reference create_instance() + { + PyTypeObject* class_object = boost::python::detail::class_registry::class_object(); + if (class_object == 0) + boost::python::detail::report_missing_class_object(typeid(T)); + + return boost::python::reference( + new boost::python::detail::extension_instance(class_object)); + } + + // Convert to const T* + friend const T* from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to const T* const& + friend const T* from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to T* const& + friend T* from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to T& + friend T& from_python(PyObject* p, boost::python::type) + { return *boost::python::detail::check_non_null(from_python(p, boost::python::type())); } + + // Convert to const T& + friend const T& from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to T + friend const T& from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + friend std::auto_ptr& from_python(PyObject* p, boost::python::type&>) + { return ptr_from_python(p, boost::python::type >()); } + + friend std::auto_ptr& from_python(PyObject* p, boost::python::type >) + { return ptr_from_python(p, boost::python::type >()); } + + friend const std::auto_ptr& from_python(PyObject* p, boost::python::type&>) + { return ptr_from_python(p, boost::python::type >()); } + + friend PyObject* to_python(std::auto_ptr x) + { return ptr_to_python(x); } + + friend boost::shared_ptr& from_python(PyObject* p, boost::python::type&>) + { return ptr_from_python(p, boost::python::type >()); } + + friend boost::shared_ptr& from_python(PyObject* p, boost::python::type >) + { return ptr_from_python(p, boost::python::type >()); } + + friend const boost::shared_ptr& from_python(PyObject* p, boost::python::type&>) + { return ptr_from_python(p, boost::python::type >()); } + + friend PyObject* to_python(boost::shared_ptr x) + { return ptr_to_python(x); } +}; + +// Convert T to_python, instantiated on demand and only if there isn't a +// non-template overload for this function. This version is the one invoked when +// T is a wrapped class. See the first 2 functions declared in +// python_extension_class_converters above for more info. +template +PyObject* to_python(const T& x) +{ + return py_extension_class_converters(boost::python::type()).to_python(x); +} + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +namespace boost { namespace python { + +BOOST_PYTHON_IMPORT_CONVERSION(python_extension_class_converters); + +namespace detail { + +template class instance_holder; + +class read_only_setattr_function : public function +{ + public: + read_only_setattr_function(const char* name); + PyObject* do_call(PyObject* args, PyObject* keywords) const; + const char* description() const; + private: + string m_name; +}; + + template + struct define_conversion + { + static void* upcast_ptr(void* v) + { + return static_cast(static_cast(v)); + } + + static void* downcast_ptr(void* v) + { + return dynamic_cast(static_cast(v)); + } + }; + +// An easy way to make an extension base class which wraps T. Note that Python +// subclasses of this class will simply be class_t 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 extension_class + : public python_extension_class_converters, // This generates the to_python/from_python functions + public extension_class_base +{ + public: + typedef T wrapped_type; + typedef U callback_type; + + // Construct with a name that comes from typeid(T).name(). The name only + // affects the objects of this class are represented through repr() + extension_class(); + + // Construct with the given name. The name only affects the objects of this + // class are represented through repr() + extension_class(const char* name); + + ~extension_class(); + + // define constructors +""" % args + + gen_function( +""" template <%(class A%n%:, %)> + inline void def(constructor<%(A%n%:, %)>) + // The following incantation builds a signature1, signature2,... object. It + // should _all_ get optimized away. + { add_constructor( + %(prepend(type::id(), + %) signature0()%()%)); + } +""", args) + + +""" + + // export homogeneous operators (type of both lhs and rhs is 'operator') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub), Foo>()); + + // export homogeneous operators (type of both lhs and rhs is 'T const&') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>()); + template + inline void def(operators) + { + typedef typename operand_select::template wrapped::type true_operand; + def_operators(operators()); + } + + // export heterogeneous operators (type of lhs: 'left', of rhs: 'right') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub), Foo>(), + // boost::python::right_operand()); + + // export heterogeneous operators (type of lhs: 'T const&', of rhs: 'right') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>(), + // boost::python::right_operand()); + template + inline void def(operators, right_operand r) + { + typedef typename operand_select::template wrapped::type true_left; + def_operators(operators(), r); + } + + // export heterogeneous reverse-argument operators + // (type of lhs: 'left', of rhs: 'right') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub), Foo>(), + // boost::python::left_operand()); + + // export heterogeneous reverse-argument operators + // (type of lhs: 'left', of rhs: 'T const&') + // usage: foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>(), + // boost::python::left_operand()); + template + inline void def(operators, left_operand l) + { + typedef typename operand_select::template wrapped::type true_right; + def_operators(operators(), l); + } + + // define a function that passes Python arguments and keywords + // to C++ verbatim (as a 'tuple const&' and 'dictionary const&' + // respectively). This is useful for manual argument passing. + // It's also the only possibility to pass keyword arguments to C++. + // Fn must have a signatur that is compatible to + // PyObject* (*)(PyObject* aTuple, PyObject* aDictionary) + template + inline void def_raw(Fn fn, const char* name) + { + this->add_method(new_raw_arguments_function(fn), name); + } + + // 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 + inline 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 + inline void def(Fn fn, const char* name, DefaultFn default_fn) + { + this->add_method(new_virtual_function(type(), fn, default_fn), name); + } + + // Provide a function which implements x., reading from the given + // member (pm) of the T obj + template + inline void def_getter(MemberType T::*pm, const char* name) + { + this->add_getter_method(new getter_function(pm), name); + } + + // Provide a function which implements assignment to x., writing to + // the given member (pm) of the T obj + template + inline void def_setter(MemberType T::*pm, const char* name) + { + this->add_setter_method(new setter_function(pm), name); + } + + // Expose the given member (pm) of the T obj as a read-only attribute + template + inline void def_readonly(MemberType T::*pm, const char* name) + { + this->add_setter_method(new read_only_setattr_function(name), name); + this->def_getter(pm, name); + } + + // Expose the given member (pm) of the T obj as a read/write attribute + template + inline void def_read_write(MemberType T::*pm, const char* name) + { + this->def_getter(pm, name); + this->def_setter(pm, name); + } + + // define the standard coercion needed for operator overloading + void def_standard_coerce(); + + // declare the given class a base class of this one and register + // up and down conversion functions + template + void declare_base(extension_class* base) + { + // see extclass.cpp for an explanation of why we need to register + // conversion functions + base_class_info baseInfo(base, + &define_conversion::downcast_ptr); + class_registry::register_base_class(baseInfo); + add_base(ref(as_object(base), ref::increment_count)); + + derived_class_info derivedInfo(this, + &define_conversion::upcast_ptr); + class_registry::register_derived_class(derivedInfo); + } + + // declare the given class a base class of this one and register + // only up conversion function + template + void declare_base(extension_class* base, without_downcast_t) + { + // see extclass.cpp for an explanation of why we need to register + // conversion functions + base_class_info baseInfo(base, 0); + class_registry::register_base_class(baseInfo); + add_base(ref(as_object(base), ref::increment_count)); + + derived_class_info derivedInfo(this, + &define_conversion::upcast_ptr); + class_registry::register_derived_class(derivedInfo); + } + + private: // types + typedef instance_value_holder holder; + + private: // extension_class_base virtual function implementations + std::vector const& base_classes() const; + std::vector const& derived_classes() const; + void* extract_object_from_holder(instance_holder_base* v) const; + + private: // Utility functions + template + inline void def_operators(operators) + { + def_standard_coerce(); + + // for some strange reason, this prevents MSVC from having an + // "unrecoverable block scoping error"! + typedef choose_op<(which & op_add)> choose_add; + + choose_op<(which & op_add)>::template args::add(this); + choose_op<(which & op_sub)>::template args::add(this); + choose_op<(which & op_mul)>::template args::add(this); + choose_op<(which & op_div)>::template args::add(this); + choose_op<(which & op_mod)>::template args::add(this); + choose_op<(which & op_divmod)>::template args::add(this); + choose_op<(which & op_pow)>::template args::add(this); + choose_op<(which & op_lshift)>::template args::add(this); + choose_op<(which & op_rshift)>::template args::add(this); + choose_op<(which & op_and)>::template args::add(this); + choose_op<(which & op_xor)>::template args::add(this); + choose_op<(which & op_or)>::template args::add(this); + choose_unary_op<(which & op_neg)>::template args::add(this); + choose_unary_op<(which & op_pos)>::template args::add(this); + choose_unary_op<(which & op_abs)>::template args::add(this); + choose_unary_op<(which & op_invert)>::template args::add(this); + choose_unary_op<(which & op_int)>::template args::add(this); + choose_unary_op<(which & op_long)>::template args::add(this); + choose_unary_op<(which & op_float)>::template args::add(this); + choose_op<(which & op_cmp)>::template args::add(this); + choose_unary_op<(which & op_str)>::template args::add(this); + } + + template + inline void def_operators(operators, right_operand) + { + def_standard_coerce(); + + choose_op<(which & op_add)>::template args::add(this); + choose_op<(which & op_sub)>::template args::add(this); + choose_op<(which & op_mul)>::template args::add(this); + choose_op<(which & op_div)>::template args::add(this); + choose_op<(which & op_mod)>::template args::add(this); + choose_op<(which & op_divmod)>::template args::add(this); + choose_op<(which & op_pow)>::template args::add(this); + choose_op<(which & op_lshift)>::template args::add(this); + choose_op<(which & op_rshift)>::template args::add(this); + choose_op<(which & op_and)>::template args::add(this); + choose_op<(which & op_xor)>::template args::add(this); + choose_op<(which & op_or)>::template args::add(this); + choose_op<(which & op_cmp)>::template args::add(this); + } + + template + inline void def_operators(operators, left_operand) + { + def_standard_coerce(); + + choose_rop<(which & op_add)>::template args::add(this); + choose_rop<(which & op_sub)>::template args::add(this); + choose_rop<(which & op_mul)>::template args::add(this); + choose_rop<(which & op_div)>::template args::add(this); + choose_rop<(which & op_mod)>::template args::add(this); + choose_rop<(which & op_divmod)>::template args::add(this); + choose_rop<(which & op_pow)>::template args::add(this); + choose_rop<(which & op_lshift)>::template args::add(this); + choose_rop<(which & op_rshift)>::template args::add(this); + choose_rop<(which & op_and)>::template args::add(this); + choose_rop<(which & op_xor)>::template args::add(this); + choose_rop<(which & op_or)>::template args::add(this); + choose_rop<(which & op_cmp)>::template args::add(this); + } + + template + void add_constructor(signature sig) + { + this->add_constructor_object(init_function::create(sig)); + } +}; + +// A simple wrapper over a T which allows us to use extension_class with a +// single template parameter only. See extension_class, above. +template +class held_instance : public T +{ + // There are no member functions: we want to avoid inadvertently overriding + // any virtual functions in T. +public:""" + + gen_functions("""%{ + template <%(class A%n%:, %)>%} + held_instance(PyObject*%(, A%n% a%n%)) : T(%(a%n%:, %)) {}""", args) + + """ +}; + +// Abstract base class for all obj holders. Base for template class +// instance_holder<>, below. +class instance_holder_base +{ +public: + virtual ~instance_holder_base() {} + virtual bool held_by_value() = 0; +}; + +// Abstract base class which holds a Held, somehow. Provides a uniform way to +// get a pointer to the held object +template +class instance_holder : public instance_holder_base +{ +public: + virtual Held*target() = 0; +}; + +// Concrete class which holds a Held by way of a wrapper class Wrapper. If Held +// can be constructed with arguments (A1...An), Wrapper must have a +// corresponding constructor for arguments (PyObject*, A1...An). Wrapper is +// neccessary to implement virtual function callbacks (there must be a +// back-pointer to the actual Python object so that we can call any +// overrides). held_instance (above) is used as a default Wrapper class when +// there are no virtual functions. +template +class instance_value_holder : public instance_holder +{ +public: + Held* target() { return &m_held; } + Wrapper* value_target() { return &m_held; } +""" + + gen_functions("""%{ + template <%(class A%n%:, %)>%} + instance_value_holder(extension_instance* p%(, A%n a%n%)) : + m_held(p%(, a%n%)) {}""", args) + + """ + + public: // implementation of instance_holder_base required interface + bool held_by_value() { return true; } + + private: + Wrapper m_held; +}; + +// Concrete class which holds a HeldType by way of a (possibly smart) pointer +// PtrType. By default, these are only generated for PtrType == +// std::auto_ptr and PtrType == boost::shared_ptr. +template +class instance_ptr_holder : public instance_holder +{ + public: + HeldType* target() { return &*m_ptr; } + PtrType& ptr() { return m_ptr; } + + instance_ptr_holder(PtrType ptr) : m_ptr(ptr) {} + + public: // implementation of instance_holder_base required interface + bool held_by_value() { return false; } + private: + PtrType m_ptr; +}; + +class extension_instance : public instance +{ + public: + extension_instance(PyTypeObject* class_); + ~extension_instance(); + + void add_implementation(std::auto_ptr holder); + + typedef std::vector held_objects; + const held_objects& wrapped_objects() const + { return m_wrapped_objects; } + private: + held_objects m_wrapped_objects; +}; + +// +// Template function implementations +// + +tuple extension_class_coerce(ref l, ref r); + +template +extension_class::extension_class() + : extension_class_base(typeid(T).name()) +{ + class_registry::register_class(this); +} + +template +extension_class::extension_class(const char* name) + : extension_class_base(name) +{ + class_registry::register_class(this); +} + +template +void extension_class::def_standard_coerce() +{ + ref coerce_fct = dict().get_item(string("__coerce__")); + + if(coerce_fct.get() == 0) // not yet defined + this->def(&extension_class_coerce, "__coerce__"); +} + +template +inline +std::vector const& +extension_class::base_classes() const +{ + return class_registry::base_classes(); +} + +template +inline +std::vector const& +extension_class::derived_classes() const +{ + return class_registry::derived_classes(); +} + +template +void* extension_class::extract_object_from_holder(instance_holder_base* v) const +{ + instance_holder* held = dynamic_cast*>(v); + if(held) + return held->target(); + return 0; +} + +template +extension_class::~extension_class() +{ + class_registry::unregister_class(this); +} + +template +inline void class_registry::register_class(extension_class_base* p) +{ + // You're not expected to create more than one of these! + assert(static_class_object == 0); + static_class_object = p; +} + +template +inline void class_registry::unregister_class(extension_class_base* 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; +} + +template +void class_registry::register_base_class(base_class_info const& i) +{ + static_base_class_info.push_back(i); +} + +template +void class_registry::register_derived_class(derived_class_info const& i) +{ + static_derived_class_info.push_back(i); +} + +template +std::vector const& class_registry::base_classes() +{ + return static_base_class_info; +} + +template +std::vector const& class_registry::derived_classes() +{ + return static_derived_class_info; +} + +// +// Static data member declaration. +// +template +extension_class_base* class_registry::static_class_object; +template +std::vector class_registry::static_base_class_info; +template +std::vector class_registry::static_derived_class_info; + +}}} // namespace boost::python::detail + +#endif // EXTENSION_CLASS_DWA052000_H_ +""") + +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/src/gen_function.py b/src/gen_function.py new file mode 100644 index 00000000..bebbc818 --- /dev/null +++ b/src/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/src/gen_init_function.py b/src/gen_init_function.py new file mode 100644 index 00000000..69935230 --- /dev/null +++ b/src/gen_init_function.py @@ -0,0 +1,166 @@ +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.python + +#ifndef INIT_FUNCTION_DWA052000_H_ +# define INIT_FUNCTION_DWA052000_H_ + +# include +# include +# include +# include + +namespace boost { namespace python { + +namespace detail { + + // parameter_traits - so far, this is a way to pass a const T& when we can be + // sure T is not a reference type, and a raw T otherwise. This should be + // rolled into boost::call_traits. Ordinarily, parameter_traits would be + // written: + // + // template struct parameter_traits + // { + // typedef const T& const_reference; + // }; + // + // template struct parameter_traits + // { + // typedef T& const_reference; + // }; + // + // template <> struct parameter_traits + // { + // typedef void const_reference; + // }; + // + // ...but since we can't partially specialize on reference types, we need this + // long-winded but equivalent incantation. + + // const_ref_selector -- an implementation detail of parameter_traits (below). This uses + // the usual "poor man's partial specialization" hack for MSVC. + template + struct const_ref_selector + { + template + struct const_ref + { + typedef const T& type; + }; + }; + + template <> + struct const_ref_selector + { + template + struct const_ref + { + typedef T type; + }; + }; + +# ifdef BOOST_MSVC +# pragma warning(push) +# pragma warning(disable: 4181) +# endif // BOOST_MSVC + template + struct parameter_traits + { + private: + typedef const_ref_selector::value> selector; + public: + typedef typename selector::template const_ref::type const_reference; + }; +# ifdef BOOST_MSVC +# pragma warning(pop) +# endif // BOOST_MSVC + + // Full spcialization for void + template <> + struct parameter_traits + { + typedef void const_reference; + }; + + template + class reference_parameter + { + typedef typename parameter_traits::const_reference const_reference; + public: + reference_parameter(const_reference value) + : value(value) {} + operator const_reference() { return value; } + private: + const_reference value; + }; + +class extension_instance; +class instance_holder_base; + +class init; +""" + + gen_functions('template struct init%x;\n', args) + + """ +template +struct init_function +{ +""" + gen_functions("""%{ + template <%(class A%n%:, %)> +%} static init* create(signature%x%{<%(A%n%:, %)>%}) { + return new init%x::const_reference%)>; + } +""", args)+"""}; + +class init : public function +{ +private: // override function hook + PyObject* do_call(PyObject* args, PyObject* keywords) const; +private: + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* tail_args, PyObject* keywords) const = 0; +}; +""" + gen_functions(""" + +template +struct init%x : init +{ + virtual instance_holder_base* create_holder(extension_instance* self, PyObject* args, PyObject* /*keywords*/) const + { + %(PyObject* a%n; + %)if (!PyArg_ParseTuple(args, const_cast("%(O%)")%(, &a%n%))) + throw argument_error(); + return new T(self%(, + boost::python::detail::reference_parameter(from_python(a%n, type()))%) + ); + } + const char* description() const + { return typeid(void (*)(T&%(, A%n%%))).name(); } +};""", args) + """ + +}}} // namespace boost::python::detail + +#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/src/gen_signatures.py b/src/gen_signatures.py new file mode 100644 index 00000000..f5a97b17 --- /dev/null +++ b/src/gen_signatures.py @@ -0,0 +1,158 @@ +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_t to the head of a void_t +# // signature results in a void_t signature again. +# static inline signature0 prepend(void_t) { 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.python for %d arguments. +#ifndef SIGNATURES_DWA050900_H_ +# define SIGNATURES_DWA050900_H_ + +# include + +namespace boost { namespace python { + +namespace detail { +// 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_t {}; +} + +// 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_t. +template +struct type +{ + typedef type id; +}; + +template <> +struct type +{ + typedef boost::python::detail::void_t id; +}; + +namespace detail { +// 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_t to the head of a void_t +// signature results in a void_t signature again. +inline signature0 prepend(void_t, signature0) { return signature0(); } + +} // namespace detail +""" + + gen_function(""" +template <%(class A%n% = detail::void_t%:, %)> +struct constructor +{ +}; +""", args) + + """ +namespace detail { +// 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 return_value_select { typedef T type; }; + +// free functions""" + + gen_functions(""" +template +return_value_select return_value(R (*)(%(A%n%:, %))) { return return_value_select(); } +""", args) + + + +""" +// TODO(?): handle 'const void' + +// member functions""" + + gen_functions(""" +template +return_value_select return_value(R (T::*)(%(A%n%:, %))) { return return_value_select(); } +""", args) + + + gen_functions(""" +template +return_value_select return_value(R (T::*)(%(A%n%:, %)) const) { return return_value_select(); } +""", args) + + + """ +}}} // namespace boost::python::detail + +#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/src/gen_singleton.py b/src/gen_singleton.py new file mode 100644 index 00000000..3a5ca7e3 --- /dev/null +++ b/src/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 + +namespace boost { namespace python { namespace detail { + +struct empty {}; +template +struct singleton : Base +{ + typedef singleton singleton_base; // Convenience type for derived class constructors + + static Derived* instance(); + + // Pass-through constructors +""" + + gen_functions("""%{ + template <%(class A%n%:, %)> +%} singleton(%(const A%n& a%n%:, %)) : Base(%(a%n%:, %)) {} +""", args) + + """ +}; + +template +Derived* singleton::instance() +{ + static Derived x; + return &x; +} + +}}} // namespace boost::python::detail + +#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/src/init_function.cpp b/src/init_function.cpp new file mode 100644 index 00000000..1667724c --- /dev/null +++ b/src/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 +#include +#include +#include + +namespace boost { namespace python { namespace detail { + + PyObject* init::do_call(PyObject* args_, PyObject* keywords) const + { + tuple args(ref(args_, ref::increment_count)); + if (args[0]->ob_type->ob_type != extension_meta_class()) + { + PyErr_SetString(PyExc_TypeError, "argument 1 to __init__ must be an ExtensionInstance"); + return 0; + } + + extension_instance *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(); + } + +}}} // namespace boost::python::detail diff --git a/src/module_builder.cpp b/src/module_builder.cpp new file mode 100644 index 00000000..ea00b71c --- /dev/null +++ b/src/module_builder.cpp @@ -0,0 +1,52 @@ +// (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 + +namespace boost { namespace python { + +namespace { + ref name_holder; +} + +string module_builder::name() +{ + // If this fails, you haven't created a module_builder object + assert(name_holder.get() != 0); + return string(name_holder); +} + +module_builder::module_builder(const char* name) + : m_module(Py_InitModule(const_cast(name), initial_methods)) +{ + // If this fails, you've created more than 1 module_builder object in your module + assert(name_holder.get() == 0); + name_holder = ref(PyObject_GetAttrString(m_module, const_cast("__name__"))); +} + +void +module_builder::add(detail::function* x, const char* name) +{ + reference f(x); // First take possession of the object. + detail::function::add_to_namespace(f, name, PyModule_GetDict(m_module)); +} + +void module_builder::add(ref x, const char* name) +{ + PyObject* dictionary = PyModule_GetDict(m_module); + PyDict_SetItemString(dictionary, const_cast(name), x.get()); +} + +void module_builder::add(PyTypeObject* x, const char* name /*= 0*/) +{ + this->add(ref(as_object(x)), name ? name : x->tp_name); +} + +PyMethodDef module_builder::initial_methods[] = { { 0, 0, 0, 0 } }; + +}} // namespace boost::python diff --git a/src/objects.cpp b/src/objects.cpp new file mode 100644 index 00000000..cc46b2ba --- /dev/null +++ b/src/objects.cpp @@ -0,0 +1,485 @@ +// (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 +#include + +namespace boost { namespace python { + +template +T object_from_python(PyObject* p, type) +{ + ref x(p, ref::increment_count); + if (!T::accepts(x)) + { + PyErr_SetString(PyExc_TypeError, p->ob_type->tp_name); + throw error_already_set(); + } + return T(x); +} + +inline PyObject* object_to_python(const object& x) +{ + return x.reference().release(); +} + +object::object(ref p) + : m_p(p) {} + +// Return a reference to the held object +ref object::reference() const +{ + return m_p; +} + +// Return a raw pointer to the held object +PyObject* object::get() const +{ + return m_p.get(); +} + +}} // namespace boost::python + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + +PyObject* to_python(const boost::python::tuple& x) +{ + return object_to_python(x); +} + +boost::python::tuple from_python(PyObject* p, boost::python::type type) +{ + return boost::python::object_from_python(p, type); +} + +PyObject* to_python(const boost::python::list& x) +{ + return object_to_python(x); +} + +boost::python::list from_python(PyObject* p, boost::python::type type) +{ + return boost::python::object_from_python(p, type); +} + +PyObject* to_python(const boost::python::dictionary& x) +{ + return object_to_python(x); +} + +boost::python::dictionary from_python(PyObject* p, boost::python::type type) +{ + return boost::python::object_from_python(p, type); +} + +PyObject* to_python(const boost::python::string& x) +{ + return object_to_python(x); +} + +boost::python::string from_python(PyObject* p, boost::python::type type) +{ + return boost::python::object_from_python(p, type); +} + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +namespace boost { namespace python { + +tuple::tuple(std::size_t n) + : object(ref(PyTuple_New(n))) +{ + for (std::size_t i = 0; i < n; ++i) + PyTuple_SET_ITEM(get(), i, detail::none()); +} + +tuple::tuple(ref p) + : object(p) +{ + assert(accepts(p)); + if (!accepts(p)) + { + PyErr_SetString(PyExc_TypeError, p->ob_type->tp_name); + throw error_already_set(); + } +} + +PyTypeObject* tuple::type_obj() +{ + return &PyTuple_Type; +} + +bool tuple::accepts(ref p) +{ + return PyTuple_Check(p.get()); +} + +std::size_t tuple::size() const +{ + return PyTuple_Size(get()); +} + +ref tuple::operator[](std::size_t pos) const +{ + return ref(PyTuple_GetItem(get(), static_cast(pos)), + ref::increment_count); +} + +void tuple::set_item(std::size_t pos, const ref& rhs) +{ + int failed = PyTuple_SetItem( + get(), static_cast(pos), ref(rhs).release()); // A reference is stolen here. + (void)failed; + assert(failed == 0); +} + +tuple tuple::slice(int low, int high) const +{ + return tuple(ref(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(ref p) + : object(p) +{ + assert(accepts(p)); + if (!accepts(p)) + { + PyErr_SetString(PyExc_TypeError, p->ob_type->tp_name); + throw error_already_set(); + } +} + +string::string(const char* s) + : object(ref(PyString_FromString(s))) {} + +string::string(const char* s, std::size_t length) + : object(ref(PyString_FromStringAndSize(s, length))) {} + +string::string(const char* s, interned_t) + : object(ref(PyString_InternFromString(s))) {} + +#if 0 +string::string(const char* s, std::size_t length, interned_t) + : object(ref(PyString_InternFromStringAndSize(s, length))) {} +#endif + +string::string(const string& rhs) + : object(rhs.reference()) {} + +// Get the type object for Strings +PyTypeObject* string::type_obj() +{ return &PyString_Type; } + +// Return true if the given object is a python string +bool string::accepts(ref 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(ref(PyString_InternFromString(c_str()), ref::increment_count)); +} + +string& string::operator*=(unsigned int repeat_count) +{ + *this = string(ref(PySequence_Repeat(get(), repeat_count))); + return *this; +} + +dictionary::dictionary(ref p) + : object(p) +{ + assert(accepts(p)); + if (!accepts(p)) + { + PyErr_SetString(PyExc_TypeError, p->ob_type->tp_name); + throw error_already_set(); + } +} + +dictionary::dictionary() + : object(ref(PyDict_New())) {} + +PyTypeObject* dictionary::type_obj() +{ return &PyDict_Type; } + +bool dictionary::accepts(ref p) +{ return PyDict_Check(p.get()); } + +void dictionary::clear() +{ PyDict_Clear(get()); } + +const ref& dictionary::proxy::operator=(const ref& rhs) +{ + if (PyDict_SetItem(m_dict.get(), m_key.get(), rhs.get()) == -1) + throw error_already_set(); + return rhs; +} + +dictionary::proxy::operator ref() const +{ + return ref(m_dict->ob_type->tp_as_mapping->mp_subscript(m_dict.get(), m_key.get()), + ref::increment_count); +} + +dictionary::proxy::proxy(const ref& dict, const ref& key) + : m_dict(dict), m_key(key) {} + +dictionary::proxy dictionary::operator[](ref key) +{ return proxy(reference(), key); } + +ref dictionary::operator[](ref key) const { + // An odd MSVC bug causes the ".operator Ptr()" to be needed + return proxy(reference(), key).operator ref(); +} + + +ref dictionary::get_item(const ref& key) const +{ + return get_item(key, ref()); +} + +ref dictionary::get_item(const ref& key, const ref& default_) const +{ + PyObject* value_or_null = PyDict_GetItem(get(), key.get()); + if (value_or_null == 0 && !PyErr_Occurred()) + return default_; + else + return ref(value_or_null, ref::increment_count); // Will throw if there was another error +} + +void dictionary::set_item(const ref& key, const ref& value) +{ + if (PyDict_SetItem(get(), key.get(), value.get()) == -1) + throw error_already_set(); +} + +void dictionary::erase(ref key) { + if (PyDict_DelItem(get(), key.get()) == -1) + throw error_already_set(); +} + +list dictionary::items() const { return list(ref(PyDict_Items(get()))); } +list dictionary::keys() const { return list(ref(PyDict_Keys(get()))); } +list dictionary::values() const { return list(ref(PyDict_Values(get()))); } + +std::size_t dictionary::size() const { return static_cast(PyDict_Size(get())); } + +string operator+(string x, string y) +{ + PyObject* io_string = x.reference().release(); + PyString_Concat(&io_string, y.get()); + return string(ref(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(ref(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(ref p) + : object(p) +{ + assert(accepts(p)); + if (!accepts(p)) + { + PyErr_SetString(PyExc_TypeError, p->ob_type->tp_name); + throw error_already_set(); + } +} + +list::list(std::size_t sz) + : object(ref(PyList_New(sz))) +{ +} + +PyTypeObject* list::type_obj() +{ + return &PyList_Type; +} + +bool list::accepts(ref p) +{ + return PyList_Check(p.get()); +} + +std::size_t list::size() +{ + return PyList_Size(get()); +} + +ref list::operator[](std::size_t pos) const +{ + return ref(PyList_GetItem(get(), pos), ref::increment_count); +} + +list::proxy list::operator[](std::size_t pos) +{ + return proxy(reference(), pos); +} + +void list::insert(std::size_t index, const ref& item) +{ + if (PyList_Insert(get(), index, item.get()) == -1) + throw error_already_set(); +} + +void list::push_back(const ref& item) +{ + if (PyList_Append(get(), item.get()) == -1) + throw error_already_set(); +} + +void list::append(const ref& item) +{ + this->push_back(item); +} + +list list::slice(int low, int high) const +{ + return list(ref(PyList_GetSlice(get(), low, high))); +} + +list::slice_proxy list::slice(int low, int high) +{ + return slice_proxy(reference(), low, high); +} + +void list::sort() +{ + if (PyList_Sort(get()) == -1) + throw error_already_set(); +} + +void list::reverse() +{ + if (PyList_Reverse(get()) == -1) + throw error_already_set(); +} + +tuple list::as_tuple() const +{ + return tuple(ref(PyList_AsTuple(get()))); +} + +const ref& list::proxy::operator=(const ref& rhs) +{ + m_list.set_item(m_index, rhs); + return rhs; +} + +list::proxy::operator ref() const +{ + return ref(PyList_GetItem(m_list.get(), m_index), ref::increment_count); +} + +ref list::get_item(std::size_t pos) const +{ + return ref(PyList_GetItem(this->get(), pos), ref::increment_count); +} + +void list::set_item(std::size_t pos, const ref& rhs) +{ + int result = PyList_SetItem(this->get(), pos, rhs.get()); + if (result == -1) + throw error_already_set(); + Py_INCREF(rhs.get()); +} + +list::proxy::proxy(const ref& list, std::size_t index) + : m_list(list), m_index(index) +{ +} + +const list& list::slice_proxy::operator=(const list& rhs) +{ + if (PyList_SetSlice(m_list.get(), m_low, m_high, rhs.get()) == -1) + throw error_already_set(); + return rhs; +} + +list::slice_proxy::operator ref() const +{ + return ref(PyList_GetSlice(m_list.get(), m_low, m_high)); +} + +list::slice_proxy::operator list() const +{ + return list(this->operator ref()); +} + +std::size_t list::slice_proxy::size() +{ + return this->operator list().size(); +} + +ref list::slice_proxy::operator[](std::size_t pos) const +{ + return this->operator list()[pos].operator ref(); +} + +list::slice_proxy::slice_proxy(const ref& list, int low, int high) + : m_list(list), m_low(low), m_high(high) +{ +} + +}} // namespace boost::python diff --git a/src/types.cpp b/src/types.cpp new file mode 100644 index 00000000..7ba5840b --- /dev/null +++ b/src/types.cpp @@ -0,0 +1,1097 @@ +// (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 +#include // for handle_exception() +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace python { + +namespace { + + using detail::type_object_base; + + PyObject* call(PyObject* obj, PyObject* (type_object_base::*f)(PyObject*) const) + { + try + { + return (static_cast(obj->ob_type)->*f)(obj); + } + catch(...) + { + handle_exception(); + return 0; + } + } + + // Naming this differently allows us to use it for functions returning long on + // compilers without partial ordering + template + R int_call(PyObject* obj, R (type_object_base::*f)(PyObject*) const) + { + try + { + return (static_cast(obj->ob_type)->*f)(obj); + } + catch(...) + { + handle_exception(); + return -1; + } + } + + // Implemented in terms of int_call, above + int call(PyObject* obj, int (type_object_base::*f)(PyObject*) const) + { + return int_call(obj, f); + } + + template + PyObject* call(PyObject* obj, PyObject* (type_object_base::*f)(PyObject*, A1) const, A1 a1) + { + try + { + return (static_cast(obj->ob_type)->*f)(obj, a1); + } + catch(...) + { + handle_exception(); + return 0; + } + } + + template + int call(PyObject* obj, int (type_object_base::*f)(PyObject*, A1) const, A1 a1) + { + try + { + return (static_cast(obj->ob_type)->*f)(obj, a1); + } + catch(...) + { + handle_exception(); + return -1; + } + } + + template + PyObject* call(PyObject* obj, PyObject* (type_object_base::*f)(PyObject*, A1, A2) const, A1 a1, A2 a2) + { + try + { + return (static_cast(obj->ob_type)->*f)(obj, a1, a2); + } + catch(...) + { + handle_exception(); + return 0; + } + } + + template + int call(PyObject* obj, int (type_object_base::*f)(PyObject*, A1, A2) const, A1 a1, A2 a2) + { + try + { + return (static_cast(obj->ob_type)->*f)(obj, a1, a2); + } + catch(...) + { + handle_exception(); + return -1; + } + } + + template + int call(PyObject* obj, int (type_object_base::*f)(PyObject*, A1, A2, A3) const, A1 a1, A2 a2, A3 a3) + { + try + { + return (static_cast(obj->ob_type)->*f)(obj, a1, a2, a3); + } + catch(...) + { + handle_exception(); + return -1; + } + } + + int call_length_function(PyObject* obj, int (type_object_base::*f)(PyObject*) const) + { + try + { + const int outcome = + (static_cast(obj->ob_type)->*f)(obj); + + if (outcome < 0) + { + PyErr_SetString(PyExc_ValueError, "__len__() should return >= 0"); + return -1; + } + return outcome; + } + catch(...) + { + handle_exception(); + return -1; + } + } + +} // anonymous namespace + +extern "C" { + +static PyObject* do_instance_repr(PyObject* obj) +{ + return call(obj, &type_object_base::instance_repr); +} + +static int do_instance_compare(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_compare, other); +} + +static PyObject* do_instance_str(PyObject* obj) +{ + return call(obj, &type_object_base::instance_str); +} + +static long do_instance_hash(PyObject* obj) +{ + return int_call(obj, &type_object_base::instance_hash); +} + +static PyObject* do_instance_call(PyObject* obj, PyObject* args, PyObject* keywords) +{ + return call(obj, &type_object_base::instance_call, args, keywords); +} + +static void do_instance_dealloc(PyObject* obj) +{ + try + { + static_cast(obj->ob_type) + ->instance_dealloc(obj); + } + catch(...) + { + assert(!"exception during destruction!"); + handle_exception(); + } +} + +static PyObject* do_instance_getattr(PyObject* obj, char* name) +{ + const char* name_ = name; + return call(obj, &type_object_base::instance_getattr, name_); +} + +static int do_instance_setattr(PyObject* obj, char* name, PyObject* value) +{ + const char* name_ = name; + return call(obj, &type_object_base::instance_setattr, name_, value); +} + +static int do_instance_mp_length(PyObject* obj) +{ + return call_length_function(obj, &type_object_base::instance_mapping_length); +} + +static int do_instance_sq_length(PyObject* obj) +{ + return call_length_function(obj, &type_object_base::instance_sequence_length); +} + +static PyObject* do_instance_mp_subscript(PyObject* obj, PyObject* index) +{ + return call(obj, &type_object_base::instance_mapping_subscript, index); +} + +static PyObject* do_instance_sq_item(PyObject* obj, int index) +{ + try + { + const PyTypeObject* const type = obj->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(obj)) + { + PyErr_SetString(PyExc_IndexError, type->tp_name); + return 0; + } + + return static_cast(obj->ob_type) + ->instance_sequence_item(obj, index); + } + catch(...) + { + handle_exception(); + return 0; + } +} + +static int do_instance_mp_ass_subscript(PyObject* obj, PyObject* index, PyObject* value) +{ + return call(obj, &type_object_base::instance_mapping_ass_subscript, index, value); +} + +static int do_instance_sq_ass_item(PyObject* obj, int index, PyObject* value) +{ + return call(obj, &type_object_base::instance_sequence_ass_item, index, value); +} + +static PyObject* do_instance_sq_concat(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_sequence_concat, other); +} + +static PyObject* do_instance_sq_repeat(PyObject* obj, int n) +{ + return call(obj, &type_object_base::instance_sequence_repeat, n); +} + +static PyObject* do_instance_sq_slice( + PyObject* obj, int start, int finish) +{ + return call(obj, &type_object_base::instance_sequence_slice, start, finish); +} + +static int do_instance_sq_ass_slice( + PyObject* obj, int start, int finish, PyObject* value) +{ + return call(obj, &type_object_base::instance_sequence_ass_slice, start, finish, value); +} + +static PyObject* do_instance_nb_add(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_number_add, other); +} + +static PyObject* do_instance_nb_subtract(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_number_subtract, other); +} + +static PyObject* do_instance_nb_multiply(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_number_multiply, other); +} + +static PyObject* do_instance_nb_divide(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_number_divide, other); +} + +static PyObject* do_instance_nb_remainder(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_number_remainder, other); +} + +static PyObject* do_instance_nb_divmod(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_number_divmod, other); +} + +static PyObject* do_instance_nb_power(PyObject* obj, PyObject* exponent, PyObject* modulus) +{ + return call(obj, &type_object_base::instance_number_power, exponent, modulus); +} + +static PyObject* do_instance_nb_negative(PyObject* obj) +{ + return call(obj, &type_object_base::instance_number_negative); +} + +static PyObject* do_instance_nb_positive(PyObject* obj) +{ + return call(obj, &type_object_base::instance_number_positive); +} + +static PyObject* do_instance_nb_absolute(PyObject* obj) +{ + return call(obj, &type_object_base::instance_number_absolute); +} + +static int do_instance_nb_nonzero(PyObject* obj) +{ + return call(obj, &type_object_base::instance_number_nonzero); +} + +static PyObject* do_instance_nb_invert(PyObject* obj) +{ + return call(obj, &type_object_base::instance_number_invert); +} + + +static PyObject* do_instance_nb_lshift(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_number_lshift, other); +} + +static PyObject* do_instance_nb_rshift(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_number_rshift, other); +} + +static PyObject* do_instance_nb_and(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_number_and, other); +} + +static PyObject* do_instance_nb_xor(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_number_xor, other); +} + +static PyObject* do_instance_nb_or(PyObject* obj, PyObject* other) +{ + return call(obj, &type_object_base::instance_number_or, other); +} + +static int do_instance_nb_coerce(PyObject**obj, PyObject**other) +{ + return call(*obj, &type_object_base::instance_number_coerce, obj, other); +} +static PyObject* do_instance_nb_int(PyObject* obj) +{ + return call(obj, &type_object_base::instance_number_int); +} + +static PyObject* do_instance_nb_long(PyObject* obj) +{ + return call(obj, &type_object_base::instance_number_long); +} + +static PyObject* do_instance_nb_float(PyObject* obj) +{ + return call(obj, &type_object_base::instance_number_float); +} + +static PyObject* do_instance_nb_oct(PyObject* obj) +{ + return call(obj, &type_object_base::instance_number_oct); +} + +static PyObject* do_instance_nb_hex(PyObject* obj) +{ + return call(obj, &type_object_base::instance_number_hex); +} + +} // extern "C" + +namespace +{ + +#define ENABLE_GENERAL_CAPABILITY(field) \ + case type_object_base::field: \ + dest->tp_##field = &do_instance_##field; \ + return true + +bool add_capability_general(type_object_base::capability capability, PyTypeObject* dest) +{ + assert(dest != 0); + + switch(capability) + { + ENABLE_GENERAL_CAPABILITY(hash); + ENABLE_GENERAL_CAPABILITY(call); + ENABLE_GENERAL_CAPABILITY(str); + ENABLE_GENERAL_CAPABILITY(getattr); + ENABLE_GENERAL_CAPABILITY(setattr); + ENABLE_GENERAL_CAPABILITY(compare); + ENABLE_GENERAL_CAPABILITY(repr); + default: + return false; + } +} + + +template +void create_method_table_if_null(T*& table) +{ + if(table == 0) + { + detail::shared_pod_manager::create(table); + } + else + { + detail::shared_pod_manager::make_unique_copy(table); + } +} + +#define ENABLE_MAPPING_CAPABILITY(field) \ + case type_object_base::mapping_##field: \ + create_method_table_if_null(dest); \ + dest->mp_##field = &do_instance_mp_##field; \ + detail::shared_pod_manager::replace_if_equal(dest); \ + return true + +bool add_capability_mapping(type_object_base::capability capability, PyMappingMethods*& dest) +{ + switch(capability) + { + ENABLE_MAPPING_CAPABILITY(length); + ENABLE_MAPPING_CAPABILITY(subscript); + ENABLE_MAPPING_CAPABILITY(ass_subscript); + default: + return false; + } +} + +#define ENABLE_SEQUENCE_CAPABILITY(field) \ + case type_object_base::sequence_##field: \ + create_method_table_if_null(dest); \ + dest->sq_##field = &do_instance_sq_##field; \ + detail::shared_pod_manager::replace_if_equal(dest); \ + return true + +bool add_capability_sequence(type_object_base::capability capability, PySequenceMethods*& dest) +{ + switch(capability) + { + ENABLE_SEQUENCE_CAPABILITY(length); + ENABLE_SEQUENCE_CAPABILITY(item); + ENABLE_SEQUENCE_CAPABILITY(ass_item); + ENABLE_SEQUENCE_CAPABILITY(concat); + ENABLE_SEQUENCE_CAPABILITY(repeat); + ENABLE_SEQUENCE_CAPABILITY(slice); + ENABLE_SEQUENCE_CAPABILITY(ass_slice); + default: + return false; + } +} + +#define ENABLE_NUMBER_CAPABILITY(field) \ + case type_object_base::number_##field: \ + create_method_table_if_null(dest); \ + dest->nb_##field = &do_instance_nb_##field; \ + detail::shared_pod_manager::replace_if_equal(dest); \ + return true + +bool add_capability_number(type_object_base::capability capability, PyNumberMethods*& dest) +{ + switch(capability) + { + ENABLE_NUMBER_CAPABILITY(add); + ENABLE_NUMBER_CAPABILITY(subtract); + ENABLE_NUMBER_CAPABILITY(multiply); + ENABLE_NUMBER_CAPABILITY(divide); + ENABLE_NUMBER_CAPABILITY(remainder); + ENABLE_NUMBER_CAPABILITY(divmod); + ENABLE_NUMBER_CAPABILITY(power); + ENABLE_NUMBER_CAPABILITY(negative); + ENABLE_NUMBER_CAPABILITY(positive); + ENABLE_NUMBER_CAPABILITY(absolute); + ENABLE_NUMBER_CAPABILITY(nonzero); + ENABLE_NUMBER_CAPABILITY(invert); + ENABLE_NUMBER_CAPABILITY(lshift); + ENABLE_NUMBER_CAPABILITY(rshift); + ENABLE_NUMBER_CAPABILITY(and); + ENABLE_NUMBER_CAPABILITY(xor); + ENABLE_NUMBER_CAPABILITY(or); + ENABLE_NUMBER_CAPABILITY(coerce); + ENABLE_NUMBER_CAPABILITY(int); + ENABLE_NUMBER_CAPABILITY(long); + ENABLE_NUMBER_CAPABILITY(float); + ENABLE_NUMBER_CAPABILITY(oct); + ENABLE_NUMBER_CAPABILITY(hex); + default: + return false; + } +} + +#define ENABLE_BUFFER_CAPABILITY(field) \ + case type_object_base::buffer_##field: \ + create_method_table_if_null(dest); \ + dest->bf_##field = &do_instance_bf_##field; \ + detail::shared_pod_manager::replace_if_equal(dest); \ + return true + +bool add_capability_buffer(type_object_base::capability capability, PyBufferProcs*& dest) +{ + (void)dest; // suppress unused argument warning + (void)capability; // likwise +#if 0 + switch(capability) + { + // nothing defined yet + default: + return false; + } +#endif + return false; +} + +} // anonymous namespace + +namespace detail { + + void add_capability( + type_object_base::capability capability, + PyTypeObject* dest_) + { + if(add_capability_general(capability, dest_)) + return; + if(add_capability_mapping(capability, dest_->tp_as_mapping)) + return; + if(add_capability_sequence(capability, dest_->tp_as_sequence)) + return; + if(add_capability_number(capability, dest_->tp_as_number)) + return; + if(add_capability_buffer(capability, dest_->tp_as_buffer)) + return; + + // no one recognized the capability + throw std::runtime_error("py::detail::add_capability(): unknown capability"); + } +} // namespace detail + +type_object_base::~type_object_base() +{ + detail::shared_pod_manager::dispose(tp_as_mapping); + detail::shared_pod_manager::dispose(tp_as_sequence); + detail::shared_pod_manager::dispose(tp_as_number); + detail::shared_pod_manager::dispose(tp_as_buffer); +} + +void type_object_base::enable(type_object_base::capability capability) +{ + detail::add_capability(capability, this); +} + +type_object_base::type_object_base(PyTypeObject* t) + : python_type(t) +{ + this->tp_dealloc = do_instance_dealloc; +} + +namespace +{ + typedef long pod_refcount; + + inline pod_refcount pod_refcount_offset(std::size_t size) + { + const std::size_t alignment = boost::alignment_of::value; + return (size + alignment - 1) / alignment * alignment; + } + + inline pod_refcount* counted_pod_refcount(char* pod, std::size_t size) + { + if(pod == 0) + return 0; + + return reinterpret_cast(pod + pod_refcount_offset(size)); + } + + #ifdef TYPE_OBJECT_BASE_STANDALONE_TEST + int pod_instance_counter = 0; + #endif + + inline pod_refcount counted_pod_getref(char* pod, std::size_t size) + { + pod_refcount* ref_count = counted_pod_refcount(pod, size); + return ref_count == 0 ? -1 : *ref_count; + } + + inline pod_refcount counted_pod_decref(char* pod, std::size_t size) + { + pod_refcount* const ref_count = counted_pod_refcount(pod, size); + if (ref_count == 0) + return -1; + --(*ref_count); + if (*ref_count <= 0) + { + #ifdef TYPE_OBJECT_BASE_STANDALONE_TEST + --pod_instance_counter; + #endif + ::operator delete(pod); + return 0; + } + return *ref_count; + } + + pod_refcount counted_pod_incref(char* pod, std::size_t size) + { + pod_refcount* ref_count = counted_pod_refcount(pod, size); + return ref_count == 0 ? -1 : ++(*ref_count); + } + +} // anonymous namespace + +namespace detail +{ + struct shared_pod_manager::compare + { + bool operator()(const std::pair& x1, + const std::pair& x2) const + { + const std::size_t n1 = x1.second; + const std::size_t n2 = x2.second; + return n1 < n2 || n1 == n2 && BOOST_CSTD_::memcmp(x1.first, x2.first, n1) < 0; + } + }; + + struct shared_pod_manager::identical + { + identical(char* p) : pod(p) {} + + bool operator()(const std::pair& x) const + { + return pod == x.first; + } + + char* pod; + }; + + shared_pod_manager& shared_pod_manager::obj() + { + static shared_pod_manager spm; + return spm; + } + + shared_pod_manager::~shared_pod_manager() + { + } + + void* shared_pod_manager::replace_if_equal(void* pod, std::size_t size) + { + if(pod == 0) + return 0; + + const holder element(static_cast(pod), size); + + const storage::iterator found + = std::lower_bound(m_storage.begin(), m_storage.end(), element, compare()); + + if (found != m_storage.end() && pod == found->first) + { + // pod already in list => do nothing + return pod; + } + else if (found != m_storage.end() && !compare()(element, *found)) + { + // equal element in list => replace + void* replacement = found->first; + counted_pod_incref(found->first, size); + dec_ref(element.first, size); // invalidates iterator 'found' + return replacement; + } + else + { + // new element => insert + m_storage.insert(found, element); + return pod; + } + } + + void* shared_pod_manager::make_unique_copy(void* pod, std::size_t size) + { + if(pod == 0) + return 0; + if(counted_pod_getref(static_cast(pod), size) == 1) + { + erase_from_list(pod); + return pod; + } + else + { + void* copy = create(size); + memmove(copy, pod, size); + dec_ref(pod, size); + return copy; + } + } + + void* shared_pod_manager::create(std::size_t size) + { + std::size_t total_size = pod_refcount_offset(size) + sizeof(pod_refcount); + char* pod = static_cast(::operator new(total_size)); + #ifdef TYPE_OBJECT_BASE_STANDALONE_TEST + ++pod_instance_counter; + #endif + memset(pod, 0, total_size); + + *counted_pod_refcount(pod, size) = 1; + + return pod; + } + + void shared_pod_manager::dec_ref(void* pod, std::size_t size) + { + if(pod == 0) + return; + + int ref_count = counted_pod_decref(static_cast(pod), size); + + if(ref_count <= 0) + erase_from_list(pod); + } + + void shared_pod_manager::erase_from_list(void* pod) + { + if(pod == 0) + return; + + const storage::iterator found = + std::find_if(m_storage.begin(), m_storage.end(), + identical(static_cast(pod))); + + if(found != m_storage.end()) + { + m_storage.erase(found); + } + } +} // namespace detail + +namespace { + struct error_type { + operator PyObject*() const { return 0; } + operator int() const { return -1; } + }; + + error_type unimplemented(const char* name) + { + assert(!"Control should never reach here"); + string s("Unimplemented "); + s += string(name); + PyErr_SetObject(PyExc_RuntimeError, s.get()); + return error_type(); + } +} + +PyObject* type_object_base::instance_repr(PyObject*) const +{ + return unimplemented("instance_repr"); +} + +int type_object_base::instance_compare(PyObject*, PyObject*) const +{ + return unimplemented("instance_compare"); +} + +PyObject* type_object_base::instance_str(PyObject*) const +{ + return unimplemented("instance_str"); +} + +long type_object_base::instance_hash(PyObject* /* obj */) const +{ + return unimplemented("instance_hash"); +} + +PyObject* type_object_base::instance_call(PyObject* /*obj*/, PyObject* /*args*/, PyObject* /*kw*/) const +{ + return unimplemented("instance_call"); +} + +PyObject* type_object_base::instance_getattr(PyObject* /*obj*/, const char* /*name*/) const +{ + return unimplemented("instance_getattr"); +} + +int type_object_base::instance_setattr(PyObject* /*obj*/, const char* /*name*/, PyObject* /*value*/) const +{ + return unimplemented("instance_setattr"); +} + +int type_object_base::instance_mapping_length(PyObject*) const +{ + return unimplemented("instance_mapping_length"); +} + +int type_object_base::instance_sequence_length(PyObject*) const +{ + return unimplemented("instance_sequence_length"); +} + +PyObject* type_object_base::instance_mapping_subscript(PyObject*, PyObject*) const +{ + return unimplemented("instance_mapping_subscript"); +} + +PyObject* type_object_base::instance_sequence_item(PyObject*, int) const +{ + return unimplemented("instance_sequence_item"); +} + +int type_object_base::instance_mapping_ass_subscript(PyObject*, PyObject*, PyObject*) const +{ + return unimplemented("instance_mapping_ass_subscript"); +} + +int type_object_base::instance_sequence_ass_item(PyObject*, int, PyObject*) const +{ + return unimplemented("instance_sequence_ass_item"); +} + +PyObject* type_object_base::instance_sequence_concat(PyObject*, PyObject*) const +{ + return unimplemented("instance_sequence_concat"); +} + +PyObject* type_object_base::instance_sequence_repeat(PyObject*, int) const +{ + return unimplemented("instance_sequence_repeat"); +} + +PyObject* type_object_base::instance_sequence_slice(PyObject*, int, int) const +{ + return unimplemented("instance_sequence_slice"); +} + +int type_object_base::instance_sequence_ass_slice(PyObject*, int, int, PyObject*) const +{ + return unimplemented("instance_sequence_ass_slice"); +} + +PyObject* type_object_base::instance_number_add(PyObject*, PyObject*) const +{ + return unimplemented("instance_number_add"); +} + +PyObject* type_object_base::instance_number_subtract(PyObject*, PyObject*) const +{ + return unimplemented("instance_number_subtract"); +} + +PyObject* type_object_base::instance_number_multiply(PyObject*, PyObject*) const +{ + return unimplemented("instance_number_multiply"); +} + +PyObject* type_object_base::instance_number_divide(PyObject*, PyObject*) const +{ + return unimplemented("instance_number_divide"); +} + +PyObject* type_object_base::instance_number_remainder(PyObject*, PyObject*) const +{ + return unimplemented("instance_number_remainder"); +} + +PyObject* type_object_base::instance_number_divmod(PyObject*, PyObject*) const +{ + return unimplemented("instance_number_divmod"); +} + +PyObject* type_object_base::instance_number_power(PyObject*, PyObject*, PyObject*) const +{ + return unimplemented("instance_number_divmod"); +} + +PyObject* type_object_base::instance_number_negative(PyObject*) const +{ + return unimplemented("instance_number_negative"); +} + +PyObject* type_object_base::instance_number_positive(PyObject*) const +{ + return unimplemented("instance_number_positive"); +} + +PyObject* type_object_base::instance_number_absolute(PyObject*) const +{ + return unimplemented("instance_number_absolute"); +} + +int type_object_base::instance_number_nonzero(PyObject*) const +{ + return unimplemented("instance_number_nonzero"); +} + +PyObject* type_object_base::instance_number_invert(PyObject*) const +{ + return unimplemented("instance_number_invert"); +} + +PyObject* type_object_base::instance_number_lshift(PyObject*, PyObject*) const +{ + return unimplemented("instance_number_lshift"); +} + +PyObject* type_object_base::instance_number_rshift(PyObject*, PyObject*) const +{ + return unimplemented("instance_number_rshift"); +} + +PyObject* type_object_base::instance_number_and(PyObject*, PyObject*) const +{ + return unimplemented("instance_number_and"); +} + +PyObject* type_object_base::instance_number_xor(PyObject*, PyObject*) const +{ + return unimplemented("instance_number_xor"); +} + +PyObject* type_object_base::instance_number_or(PyObject*, PyObject*) const +{ + return unimplemented("instance_number_or"); +} + +int type_object_base::instance_number_coerce(PyObject*, PyObject**, PyObject**) const +{ + return unimplemented("instance_number_coerce"); +} + +PyObject* type_object_base::instance_number_int(PyObject*) const +{ + return unimplemented("instance_number_int"); +} + +PyObject* type_object_base::instance_number_long(PyObject*) const +{ + return unimplemented("instance_number_long"); +} + +PyObject* type_object_base::instance_number_float(PyObject*) const +{ + return unimplemented("instance_number_float"); +} + +PyObject* type_object_base::instance_number_oct(PyObject*) const +{ + return unimplemented("instance_number_oct"); +} + +PyObject* type_object_base::instance_number_hex(PyObject*) const +{ + return unimplemented("instance_number_hex"); +} + +}} // namespace boost::python + +#ifdef TYPE_OBJECT_BASE_STANDALONE_TEST + +struct TestTypeObject : boost::python::type_object_base +{ + TestTypeObject() + : boost::python::type_object_base(Py_None->ob_type->ob_type) + {} + + void instance_dealloc(PyObject*) const {} +}; + +struct POD1 +{ + unsigned char data; +}; + +int main() +{ + boost::python::type_object_base *o1, *o2, *o3; + +// POD1 * pod1; +// boost::python::detail::shared_pod_manager::create(pod1); + + o1 = new TestTypeObject; + o2 = new TestTypeObject; + o3 = new TestTypeObject; + + assert(boost::python::pod_instance_counter == 0); + + o1->enable(boost::python::type_object_base::number_add); + o1->enable(boost::python::type_object_base::compare); + + o2->enable(boost::python::type_object_base::number_add); + o2->enable(boost::python::type_object_base::mapping_length); + + o3->enable(boost::python::type_object_base::number_add); + o3->enable(boost::python::type_object_base::sequence_length); + + assert(boost::python::pod_instance_counter == 3); + assert(o1->tp_as_number && !o1->tp_as_mapping && !o1->tp_as_sequence); + assert(o2->tp_as_number && o2->tp_as_mapping && !o2->tp_as_sequence); + assert(o3->tp_as_number && !o3->tp_as_mapping && o3->tp_as_sequence); + assert(o1->tp_as_number == o2->tp_as_number); + assert(o1->tp_as_number == o3->tp_as_number); + assert((void*)o2->tp_as_number != o2->tp_as_mapping); + assert((void*)o2->tp_as_mapping != o3->tp_as_sequence); + + o1->enable(boost::python::type_object_base::number_subtract); + + assert(boost::python::pod_instance_counter == 4); + assert(o1->tp_as_number != o2->tp_as_number); + assert(o2->tp_as_number == o3->tp_as_number); + + o3->enable(boost::python::type_object_base::mapping_subscript); + + assert(boost::python::pod_instance_counter == 5); + assert(o3->tp_as_number && o3->tp_as_mapping && o3->tp_as_sequence); + assert(o2->tp_as_mapping != o3->tp_as_mapping); + + o2->enable(boost::python::type_object_base::mapping_subscript); + o3->enable(boost::python::type_object_base::mapping_length); + + assert(boost::python::pod_instance_counter == 4); + assert(o2->tp_as_number && o2->tp_as_mapping && !o2->tp_as_sequence); + assert(o3->tp_as_number && o3->tp_as_mapping && o3->tp_as_sequence); + assert(o2->tp_as_mapping == o3->tp_as_mapping); + + boost::python::type_object_base *o4 = new TestTypeObject; + + assert(boost::python::pod_instance_counter == 4); + + o4->enable(boost::python::type_object_base::number_add); + + assert(boost::python::pod_instance_counter == 4); + assert(o4->tp_as_number && !o4->tp_as_mapping && !o4->tp_as_sequence); + assert(o4->tp_as_number == o3->tp_as_number); + + delete o3; + + assert(boost::python::pod_instance_counter == 3); + assert(o1->tp_as_number && !o1->tp_as_mapping && !o1->tp_as_sequence); + assert(o2->tp_as_number && o2->tp_as_mapping && !o2->tp_as_sequence); + assert(o4->tp_as_number && !o4->tp_as_mapping && !o4->tp_as_sequence); + assert(o4->tp_as_number == o2->tp_as_number); + + o3 = new TestTypeObject; + + assert(boost::python::pod_instance_counter == 3); + + o3->enable(boost::python::type_object_base::number_add); + o3->enable(boost::python::type_object_base::sequence_length); + + assert(boost::python::pod_instance_counter == 4); + assert(o3->tp_as_number && !o3->tp_as_mapping && o3->tp_as_sequence); + assert(o1->tp_as_number != o3->tp_as_number); + assert(o2->tp_as_number == o3->tp_as_number); + + delete o1; + + assert(boost::python::pod_instance_counter == 3); + + delete o4; + + assert(boost::python::pod_instance_counter == 3); + + delete o3; + + assert(boost::python::pod_instance_counter == 2); + + delete o2; + + assert(boost::python::pod_instance_counter == 0); + + assert(boost::python::detail::shared_pod_manager::obj().m_storage.size() == 0); +} + +#endif + diff --git a/test/comprehensive.cpp b/test/comprehensive.cpp new file mode 100644 index 00000000..cae924e6 --- /dev/null +++ b/test/comprehensive.cpp @@ -0,0 +1,1133 @@ +// (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 "comprehensive.hpp" +#include +#include // used for portability on broken compilers +#include // for pow() +#include + +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 boost::python::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. +std::string FooCallback::pure() const +{ + return boost::python::callback::call_method(m_self, "pure"); +} + +Foo::PythonClass::PythonClass(boost::python::module_builder& m) + : boost::python::class_builder(m, "Foo") +{ + def(boost::python::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(boost::python::module_builder& m) + : boost::python::class_builder(m, "Bar") +{ + def(boost::python::constructor()); + def(&Bar::first, "first"); + def(&Bar::second, "second"); + def(&Bar::pass_baz, "pass_baz"); +} + +BazPythonClass::BazPythonClass(boost::python::module_builder& m) + : boost::python::class_builder(m, "Baz") // optional +{ + def(boost::python::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(boost::python::module_builder& m) + : boost::python::class_builder(m, "StringMap") +{ + def(boost::python::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 boost::python::error_already_set(); +} + +IntPairPythonClass::IntPairPythonClass(boost::python::module_builder& m) + : boost::python::class_builder(m, "IntPair") +{ + def(boost::python::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 boost::python::error_already_set(); + } +} + +void IntPairPythonClass::delattr(IntPair&, const char*) +{ + PyErr_SetString(PyExc_AttributeError, "Attributes can't be deleted!"); + throw boost::python::error_already_set(); +} + +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 boost::python::error_already_set(); + } +#if defined(__MWERKS__) && __MWERKS__ <= 0x2400 + 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, BOOST_PYTHON_CONVERSION::to_python(key)); + throw boost::python::error_already_set(); + } +} +}} // 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: + std::string 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; +} + +std::string 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 BOOST_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; +} + +boost::python::string range_str(const Range& r) +{ + char buf[200]; + sprintf(buf, "(%d, %d)", r.m_start, r.m_finish); + return boost::python::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; +} + +/************************************************************/ +/* */ +/* some functions to test overloading */ +/* */ +/************************************************************/ + +static std::string testVoid() +{ + return std::string("Hello world!"); +} + +static int testInt(int i) +{ + return i; +} + +static std::string testString(std::string i) +{ + return i; +} + +static int test2(int i1, int i2) +{ + return i1+i2; +} + +static int test3(int i1, int i2, int i3) +{ + return i1+i2+i3; +} + +static int test4(int i1, int i2, int i3, int i4) +{ + return i1+i2+i3+i4; +} + +static int test5(int i1, int i2, int i3, int i4, int i5) +{ + return i1+i2+i3+i4+i5; +} + +/************************************************************/ +/* */ +/* a class to test overloading */ +/* */ +/************************************************************/ + +struct OverloadTest +{ + OverloadTest(): x_(1000) {} + OverloadTest(int x): x_(x) {} + OverloadTest(int x,int y): x_(x+y) { } + OverloadTest(int x,int y,int z): x_(x+y+z) {} + OverloadTest(int x,int y,int z, int a): x_(x+y+z+a) {} + OverloadTest(int x,int y,int z, int a, int b): x_(x+y+z+a+b) {} + + int x() const { return x_; } + void setX(int x) { x_ = x; } + + int p1(int x) { return x; } + int p2(int x, int y) { return x + y; } + int p3(int x, int y, int z) { return x + y + z; } + int p4(int x, int y, int z, int a) { return x + y + z + a; } + int p5(int x, int y, int z, int a, int b) { return x + y + z + a + b; } + private: + int x_; +}; + +static int getX(OverloadTest* u) +{ + return u->x(); +} + + +/************************************************************/ +/* */ +/* classes to test base declarations and conversions */ +/* */ +/************************************************************/ + +struct Dummy +{ + virtual ~Dummy() {} + int dummy_; +}; + +struct Base +{ + virtual int x() const { return 999; }; + virtual ~Base() {} +}; + +// inherit Dummy so that the Base part of Concrete starts at an offset +// otherwise, typecast tests wouldn't be very meaningful +struct Derived1 : public Dummy, public Base +{ + Derived1(int x): x_(x) {} + virtual int x() const { return x_; } + + private: + int x_; +}; + +struct Derived2 : public Dummy, public Base +{ + Derived2(int x): x_(x) {} + virtual int x() const { return x_; } + + private: + int x_; +}; + +static int testUpcast(Base* b) +{ + return b->x(); +} + +static std::auto_ptr derived1Factory(int i) +{ + return std::auto_ptr(new Derived1(i)); +} + +static std::auto_ptr derived2Factory(int i) +{ + return std::auto_ptr(new Derived2(i)); +} + +static int testDowncast1(Derived1* d) +{ + return d->x(); +} + +static int testDowncast2(Derived2* d) +{ + return d->x(); +} + +/************************************************************/ +/* */ +/* test classes for interaction of overloading, */ +/* base declarations, and callbacks */ +/* */ +/************************************************************/ + +struct CallbackTestBase +{ + virtual int testCallback(int i) { return callback(i); } + virtual int callback(int i) = 0; + virtual ~CallbackTestBase() {} +}; + +struct CallbackTest : public CallbackTestBase +{ + virtual int callback(int i) { return i + 1; } + virtual std::string callbackString(std::string const & i) { return i + " 1"; } +}; + +struct CallbackTestCallback : public CallbackTest +{ + CallbackTestCallback(PyObject* self) + : m_self(self) + {} + + int callback(int x) + { + return boost::python::callback::call_method(m_self, "callback", x); + } + std::string callbackString(std::string const & x) + { + return boost::python::callback::call_method(m_self, "callback", x); + } + + static int default_callback(CallbackTest* self, int x) + { + return self->CallbackTest::callback(x); + } + static std::string default_callbackString(CallbackTest* self, std::string x) + { + return self->CallbackTest::callbackString(x); + } + + PyObject* m_self; +}; + +int testCallback(CallbackTestBase* b, int i) +{ + return b->testCallback(i); +} + +/************************************************************/ +/* */ +/* test classes for interaction of method lookup */ +/* in the context of inheritance */ +/* */ +/************************************************************/ + +struct A1 { + virtual ~A1() {} + virtual std::string overrideA1() const { return "A1::overrideA1"; } + virtual std::string inheritA1() const { return "A1::inheritA1"; } +}; + +struct A2 { + virtual ~A2() {} + virtual std::string inheritA2() const { return "A2::inheritA2"; } +}; + +struct B1 : A1, A2 { + std::string overrideA1() const { return "B1::overrideA1"; } + virtual std::string overrideB1() const { return "B1::overrideB1"; } +}; + +struct B2 : A1, A2 { + std::string overrideA1() const { return "B2::overrideA1"; } + virtual std::string inheritB2() const { return "B2::inheritB2"; } +}; + +struct C : B1 { + std::string overrideB1() const { return "C::overrideB1"; } +}; + +std::string call_overrideA1(const A1& a) { return a.overrideA1(); } +std::string call_overrideB1(const B1& b) { return b.overrideB1(); } +std::string call_inheritA1(const A1& a) { return a.inheritA1(); } + +std::auto_ptr factoryA1asA1() { return std::auto_ptr(new A1); } +std::auto_ptr factoryB1asA1() { return std::auto_ptr(new B1); } +std::auto_ptr factoryB2asA1() { return std::auto_ptr(new B2); } +std::auto_ptr factoryCasA1() { return std::auto_ptr(new C); } +std::auto_ptr factoryA2asA2() { return std::auto_ptr(new A2); } +std::auto_ptr factoryB1asA2() { return std::auto_ptr(new B1); } +std::auto_ptr factoryB1asB1() { return std::auto_ptr(new B1); } +std::auto_ptr factoryCasB1() { return std::auto_ptr(new C); } + +struct B_callback : B1 { + B_callback(PyObject* self) : m_self(self) {} + + std::string overrideA1() const { return boost::python::callback::call_method(m_self, "overrideA1"); } + std::string overrideB1() const { return boost::python::callback::call_method(m_self, "overrideB1"); } + + static std::string default_overrideA1(B1& x) { return x.B1::overrideA1(); } + static std::string default_overrideB1(B1& x) { return x.B1::overrideB1(); } + + PyObject* m_self; +}; + +struct A_callback : A1 { + A_callback(PyObject* self) : m_self(self) {} + + std::string overrideA1() const { return boost::python::callback::call_method(m_self, "overrideA1"); } + std::string inheritA1() const { return boost::python::callback::call_method(m_self, "inheritA1"); } + + static std::string default_overrideA1(A1& x) { return x.A1::overrideA1(); } + static std::string default_inheritA1(A1& x) { return x.A1::inheritA1(); } + + PyObject* m_self; +}; + +/************************************************************/ +/* */ +/* RawTest */ +/* (test passing of raw arguments to C++) */ +/* */ +/************************************************************/ + +struct RawTest +{ + RawTest(int i) : i_(i) {} + + int i_; +}; + +PyObject* raw(boost::python::tuple const & args, boost::python::dictionary const & keywords); + +int raw1(PyObject* args, PyObject* keywords) +{ + return PyTuple_Size(args) + PyDict_Size(keywords); +} + +int raw2(boost::python::ref args, boost::python::ref keywords) +{ + return PyTuple_Size(args.get()) + PyDict_Size(keywords.get()); +} + + + +/************************************************************/ +/* */ +/* Ratio */ +/* */ +/************************************************************/ + +typedef boost::rational Ratio; + +boost::python::string ratio_str(const Ratio& r) +{ + char buf[200]; + + if (r.denominator() == 1) + sprintf(buf, "%d", r.numerator()); + else + sprintf(buf, "%d/%d", r.numerator(), r.denominator()); + + return boost::python::string(buf); +} + +boost::python::string ratio_repr(const Ratio& r) +{ + char buf[200]; + sprintf(buf, "Rational(%d, %d)", r.numerator(), r.denominator()); + return boost::python::string(buf); +} + +boost::python::tuple ratio_coerce(const Ratio& r1, int r2) +{ + return boost::python::tuple(r1, Ratio(r2)); +} + +// The most reliable way, across compilers, to grab the particular abs function +// we're interested in. +Ratio ratio_abs(const Ratio& r) +{ + return boost::abs(r); +} + +// An experiment, to be integrated into the py_cpp library at some point. +template +struct StandardOps +{ + static T add(const T& x, const T& y) { return x + y; } + static T sub(const T& x, const T& y) { return x - y; } + static T mul(const T& x, const T& y) { return x * y; } + static T div(const T& x, const T& y) { return x / y; } + static T cmp(const T& x, const T& y) { return std::less()(x, y) ? -1 : std::less()(y, x) ? 1 : 0; } +}; + +// This helps us prove that we can now pass non-const reference parameters to constructors +struct Fubar { + Fubar(Foo&) {} + Fubar(int) {} +}; + +/************************************************************/ +/* */ +/* Int */ +/* this class tests operator export */ +/* */ +/************************************************************/ + +#ifndef NDEBUG +int total_Ints = 0; +#endif + +struct Int +{ + explicit Int(int i) : i_(i) { +#ifndef NDEBUG + ++total_Ints; +#endif + } + +#ifndef NDEBUG + ~Int() { --total_Ints; } + Int(const Int& rhs) : i_(rhs.i_) { ++total_Ints; } +#endif + + int i() const { return i_; } + + int i_; +}; + +Int operator+(Int const & l, Int const & r) { return Int(l.i_ + r.i_); } +Int operator+(Int const & l, int const & r) { return Int(l.i_ + r); } +Int operator+(int const & l, Int const & r) { return Int(l + r.i_); } + +Int operator-(Int const & l, Int const & r) { return Int(l.i_ - r.i_); } +Int operator-(Int const & l, int const & r) { return Int(l.i_ - r); } +Int operator-(int const & l, Int const & r) { return Int(l - r.i_); } +Int operator-(Int const & r) { return Int(- r.i_); } + +Int mul(Int const & l, Int const & r) { return Int(l.i_ * r.i_); } +Int imul(Int const & l, int const & r) { return Int(l.i_ * r); } +Int rmul(Int const & r, int const & l) { return Int(l * r.i_); } + +Int operator/(Int const & l, Int const & r) { return Int(l.i_ / r.i_); } + +Int operator%(Int const & l, Int const & r) { return Int(l.i_ % r.i_); } + +bool operator<(Int const & l, Int const & r) { return l.i_ < r.i_; } +bool operator<(Int const & l, int const & r) { return l.i_ < r; } +bool operator<(int const & l, Int const & r) { return l < r.i_; } + +Int pow(Int const & l, Int const & r) { return Int(static_cast(::pow(l.i_, r.i_))); } +Int powmod(Int const & l, Int const & r, Int const & m) { return Int((int)::pow(l.i_, r.i_) % m.i_); } +Int pow(Int const & l, int const & r) { return Int(static_cast(::pow(l.i_, r))); } + +std::ostream & operator<<(std::ostream & o, Int const & r) { return (o << r.i_); } + +/************************************************************/ +/* */ +/* double tests from Mark Evans() */ +/* */ +/************************************************************/ +double sizelist(boost::python::list list) { return list.size(); } +void vd_push_back(std::vector& vd, const double& x) +{ + vd.push_back(x); +} + +/************************************************************/ +/* */ +/* What if I want to return a pointer? */ +/* */ +/************************************************************/ + +// +// This example exposes the pointer by copying its referent +// +struct Record { + Record(int x) : value(x){} + int value; +}; + +const Record* get_record() +{ + static Record v(1234); + return &v; +} + +template class boost::python::class_builder; // explicitly instantiate + +} // namespace extclass_demo + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE +inline PyObject* to_python(const extclass_demo::Record* p) +{ + return to_python(*p); +} +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +/************************************************************/ +/* */ +/* Enums and non-method class attributes */ +/* */ +/************************************************************/ + +namespace extclass_demo { + +struct EnumOwner +{ + public: + enum enum_type { one = 1, two = 2, three = 3 }; + + EnumOwner(enum_type a1, const enum_type& a2) + : m_first(a1), m_second(a2) {} + + void set_first(const enum_type& x) { m_first = x; } + void set_second(const enum_type& x) { m_second = x; } + + enum_type first() { return m_first; } + enum_type second() { return m_second; } + private: + enum_type m_first, m_second; +}; + +} + +namespace boost { namespace python { + template class enum_as_int_converters; + using extclass_demo::pow; +}} // namespace boost::python + +// This is just a way of getting the converters instantiated +//struct EnumOwner_enum_type_Converters +// : boost::python::py_enum_as_int_converters +//{ +//}; + +namespace extclass_demo { + +/************************************************************/ +/* */ +/* pickling support */ +/* */ +/************************************************************/ + class world + { + private: + std::string country; + int secret_number; + public: + world(const std::string& country) : secret_number(0) { + this->country = country; + } + std::string greet() const { return "Hello from " + country + "!"; } + std::string get_country() const { return country; } + void set_secret_number(int number) { secret_number = number; } + int get_secret_number() const { return secret_number; } + }; + + // Support for pickle. + boost::python::tuple world_getinitargs(const world& w) + { + boost::python::tuple result(1); + result.set_item(0, w.get_country()); + return result; + } + + boost::python::tuple world_getstate(const world& w) + { + boost::python::tuple result(1); + result.set_item(0, w.get_secret_number()); + return result; + } + + void world_setstate(world& w, boost::python::tuple state) + { + if (state.size() != 1) { + PyErr_SetString(PyExc_ValueError, + "Unexpected argument in call to __setstate__."); + throw boost::python::error_already_set(); + } + + const int number = BOOST_PYTHON_CONVERSION::from_python(state[0].get(), boost::python::type()); + if (number != 42) + w.set_secret_number(number); + } + +/************************************************************/ +/* */ +/* init the module */ +/* */ +/************************************************************/ + +void init_module(boost::python::module_builder& m) +{ + m.def(get_record, "get_record"); + boost::python::class_builder record_class(m, "Record"); + record_class.def_readonly(&Record::value, "value"); + + m.def(sizelist, "sizelist"); + + boost::python::class_builder > vector_double(m, "vector_double"); + vector_double.def(boost::python::constructor<>()); + vector_double.def(vd_push_back, "push_back"); + + boost::python::class_builder fubar(m, "Fubar"); + fubar.def(boost::python::constructor()); + fubar.def(boost::python::constructor()); + + Foo::PythonClass foo(m); + BarPythonClass bar(m); + BazPythonClass baz(m); + StringMapPythonClass string_map(m); + IntPairPythonClass int_pair(m); + m.def(make_pair, "make_pair"); + CompareIntPairPythonClass compare_int_pair(m); + + boost::python::class_builder string_pair(m, "StringPair"); + string_pair.def(boost::python::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"); + + // This shows the wrapping of a 3rd-party numeric type. + boost::python::class_builder > rational(m, "Rational"); + rational.def(boost::python::constructor()); + rational.def(boost::python::constructor()); + rational.def(boost::python::constructor<>()); + rational.def(StandardOps::add, "__add__"); + rational.def(StandardOps::sub, "__sub__"); + rational.def(StandardOps::mul, "__mul__"); + rational.def(StandardOps::div, "__div__"); + rational.def(StandardOps::cmp, "__cmp__"); + rational.def(ratio_coerce, "__coerce__"); + rational.def(ratio_str, "__str__"); + rational.def(ratio_repr, "__repr__"); + rational.def(ratio_abs, "__abs__"); + + boost::python::class_builder range(m, "Range"); + range.def(boost::python::constructor()); + range.def(boost::python::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"); + + m.def(&testVoid, "overloaded"); + m.def(&testInt, "overloaded"); + m.def(&testString, "overloaded"); + m.def(&test2, "overloaded"); + m.def(&test3, "overloaded"); + m.def(&test4, "overloaded"); + m.def(&test5, "overloaded"); + + boost::python::class_builder over(m, "OverloadTest"); + over.def(boost::python::constructor<>()); + over.def(boost::python::constructor()); + over.def(boost::python::constructor()); + over.def(boost::python::constructor()); + over.def(boost::python::constructor()); + over.def(boost::python::constructor()); + over.def(boost::python::constructor()); + over.def(&getX, "getX"); + over.def(&OverloadTest::setX, "setX"); + over.def(&OverloadTest::x, "overloaded"); + over.def(&OverloadTest::p1, "overloaded"); + over.def(&OverloadTest::p2, "overloaded"); + over.def(&OverloadTest::p3, "overloaded"); + over.def(&OverloadTest::p4, "overloaded"); + over.def(&OverloadTest::p5, "overloaded"); + + boost::python::class_builder base(m, "Base"); + base.def(&Base::x, "x"); + + boost::python::class_builder derived1(m, "Derived1"); + // this enables conversions between Base and Derived1 + // and makes wrapped methods of Base available + derived1.declare_base(base); + derived1.def(boost::python::constructor()); + + boost::python::class_builder derived2(m, "Derived2"); + // don't enable downcast from Base to Derived2 + derived2.declare_base(base, boost::python::without_downcast); + derived2.def(boost::python::constructor()); + + m.def(&testUpcast, "testUpcast"); + m.def(&derived1Factory, "derived1Factory"); + m.def(&derived2Factory, "derived2Factory"); + m.def(&testDowncast1, "testDowncast1"); + m.def(&testDowncast2, "testDowncast2"); + + boost::python::class_builder callbackTestBase(m, "CallbackTestBase"); + callbackTestBase.def(&CallbackTestBase::testCallback, "testCallback"); + m.def(&testCallback, "testCallback"); + + boost::python::class_builder callbackTest(m, "CallbackTest"); + callbackTest.def(boost::python::constructor<>()); + callbackTest.def(&CallbackTest::callback, "callback", + &CallbackTestCallback::default_callback); + callbackTest.def(&CallbackTest::callbackString, "callback", + &CallbackTestCallback::default_callbackString); + + callbackTest.declare_base(callbackTestBase); + + boost::python::class_builder a1_class(m, "A1"); + a1_class.def(boost::python::constructor<>()); + a1_class.def(&A1::overrideA1, "overrideA1", &A_callback::default_overrideA1); + a1_class.def(&A1::inheritA1, "inheritA1", &A_callback::default_inheritA1); + + boost::python::class_builder a2_class(m, "A2"); + a2_class.def(boost::python::constructor<>()); + a2_class.def(&A2::inheritA2, "inheritA2"); + + boost::python::class_builder b1_class(m, "B1"); + b1_class.declare_base(a1_class); + b1_class.declare_base(a2_class); + + b1_class.def(boost::python::constructor<>()); + b1_class.def(&B1::overrideA1, "overrideA1", &B_callback::default_overrideA1); + b1_class.def(&B1::overrideB1, "overrideB1", &B_callback::default_overrideB1); + + boost::python::class_builder b2_class(m, "B2"); + b2_class.declare_base(a1_class); + b2_class.declare_base(a2_class); + + b2_class.def(boost::python::constructor<>()); + b2_class.def(&B2::overrideA1, "overrideA1"); + b2_class.def(&B2::inheritB2, "inheritB2"); + + m.def(call_overrideA1, "call_overrideA1"); + m.def(call_overrideB1, "call_overrideB1"); + m.def(call_inheritA1, "call_inheritA1"); + + m.def(factoryA1asA1, "factoryA1asA1"); + m.def(factoryB1asA1, "factoryB1asA1"); + m.def(factoryB2asA1, "factoryB2asA1"); + m.def(factoryCasA1, "factoryCasA1"); + m.def(factoryA2asA2, "factoryA2asA2"); + m.def(factoryB1asA2, "factoryB1asA2"); + m.def(factoryB1asB1, "factoryB1asB1"); + m.def(factoryCasB1, "factoryCasB1"); + + boost::python::class_builder rawtest_class(m, "RawTest"); + rawtest_class.def(boost::python::constructor()); + rawtest_class.def_raw(&raw, "raw"); + + m.def_raw(&raw, "raw"); + m.def_raw(&raw1, "raw1"); + m.def_raw(&raw2, "raw2"); + + boost::python::class_builder int_class(m, "Int"); + int_class.def(boost::python::constructor()); + int_class.def(&Int::i, "i"); + + // wrap homogeneous operators + int_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub | boost::python::op_neg | + boost::python::op_cmp | boost::python::op_str | boost::python::op_divmod | boost::python::op_pow )>()); + // export non-operator functions as homogeneous operators + int_class.def(&mul, "__mul__"); + int_class.def(&powmod, "__pow__"); + + // wrap heterogeneous operators (lhs: Int const &, rhs: int const &) + int_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub | boost::python::op_cmp | boost::python::op_pow)>(), + boost::python::right_operand()); + // export non-operator function as heterogeneous operator + int_class.def(&imul, "__mul__"); + + // wrap heterogeneous operators (lhs: int const &, rhs: Int const &) + int_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub | boost::python::op_cmp)>(), + boost::python::left_operand()); + // export non-operator function as heterogeneous reverse-argument operator + int_class.def(&rmul, "__rmul__"); + + + boost::python::class_builder enum_owner(m, "EnumOwner"); + enum_owner.def(boost::python::constructor()); + enum_owner.def(&EnumOwner::set_first, "__setattr__first__"); + enum_owner.def(&EnumOwner::set_second, "__setattr__second__"); + enum_owner.def(&EnumOwner::first, "__getattr__first__"); + enum_owner.def(&EnumOwner::second, "__getattr__second__"); + enum_owner.add(PyInt_FromLong(EnumOwner::one), "one"); + enum_owner.add(PyInt_FromLong(EnumOwner::two), "two"); + enum_owner.add(PyInt_FromLong(EnumOwner::three), "three"); + + // pickling support + + // Create the Python type object for our extension class. + boost::python::class_builder world_class(m, "world"); + + // Add the __init__ function. + world_class.def(boost::python::constructor()); + // Add a regular member function. + world_class.def(&world::greet, "greet"); + world_class.def(&world::get_secret_number, "get_secret_number"); + world_class.def(&world::set_secret_number, "set_secret_number"); + + // Support for pickle. + world_class.def(world_getinitargs, "__getinitargs__"); + world_class.def(world_getstate, "__getstate__"); + world_class.def(world_setstate, "__setstate__"); +} + +PyObject* raw(boost::python::tuple const& args, boost::python::dictionary const& keywords) +{ + if(args.size() != 2 || keywords.size() != 2) + { + PyErr_SetString(PyExc_TypeError, "wrong number of arguments"); + throw boost::python::argument_error(); + } + + RawTest* first = BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()); + int second = BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()); + + int third = BOOST_PYTHON_CONVERSION::from_python(keywords[boost::python::string("third")].get(), boost::python::type()); + int fourth = BOOST_PYTHON_CONVERSION::from_python(keywords[boost::python::string("fourth")].get(), boost::python::type()); + + return BOOST_PYTHON_CONVERSION::to_python(first->i_ + second + third + fourth); +} + +void init_module() +{ + boost::python::module_builder demo("demo"); + init_module(demo); + + // Just for giggles, add a raw metaclass. + demo.add(new boost::python::meta_class); +} + +extern "C" +#ifdef _WIN32 +__declspec(dllexport) +#endif +void initdemo() +{ + try { + extclass_demo::init_module(); + } + catch(...) { + boost::python::handle_exception(); + } // Need a way to report other errors here +} + +CompareIntPairPythonClass::CompareIntPairPythonClass(boost::python::module_builder& m) + : boost::python::class_builder(m, "CompareIntPair") +{ + def(boost::python::constructor<>()); + def(&CompareIntPair::operator(), "__call__"); +} + +} // namespace extclass_demo + + +#if defined(_WIN32) +# ifdef __MWERKS__ +# pragma ANSI_strict off +# endif +# include +# ifdef __MWERKS__ +# pragma ANSI_strict reset +# endif +extern "C" BOOL WINAPI DllMain ( HINSTANCE hInst, DWORD wDataSeg, LPVOID lpvReserved ); + +# ifdef BOOST_MSVC +extern "C" void structured_exception_translator(unsigned int, EXCEPTION_POINTERS*) +{ + throw; +} +# endif + +#ifndef NDEBUG +namespace boost { namespace python { namespace detail { + extern int total_Dispatchers; +}}} // namespace boost::python::detail +#endif + +BOOL WINAPI DllMain( + HINSTANCE, //hDllInst + DWORD fdwReason, + LPVOID // lpvReserved + ) +{ +# ifdef BOOST_MSVC + _set_se_translator(structured_exception_translator); +#endif + (void)fdwReason; // warning suppression. + +#ifndef NDEBUG + switch(fdwReason) + { + case DLL_PROCESS_DETACH: + assert(extclass_demo::total_Ints == 0); + } +#endif + + return 1; +} +#endif // _WIN32 diff --git a/test/comprehensive.hpp b/test/comprehensive.hpp new file mode 100644 index 00000000..d8a8e8ea --- /dev/null +++ b/test/comprehensive.hpp @@ -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 +# include +# 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 + std::string 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 std::string 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 extension_class (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. + std::string pure() const; + + private: // Required boilerplate if functions will be overridden + PyObject* m_self; // No, we don't want a boost::python::ref here, or we'd get an ownership cycle. +}; + +// Define the Python base class +struct Foo::PythonClass : boost::python::class_builder { PythonClass(boost::python::module_builder&); }; + +// 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_t 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 boost::python::class_builder<> that +// causes to_python/from_python conversion functions to be generated. +struct BarPythonClass : boost::python::class_builder { BarPythonClass(boost::python::module_builder&); }; +struct BazPythonClass : boost::python::class_builder { BazPythonClass(boost::python::module_builder&); }; + +struct StringMapPythonClass + : boost::python::class_builder +{ + StringMapPythonClass(boost::python::module_builder&); + + // 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 + : boost::python::class_builder +{ + IntPairPythonClass(boost::python::module_builder&); + + // 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 + : boost::python::class_builder +{ + CompareIntPairPythonClass(boost::python::module_builder&); +}; + +} // namespace extclass_demo + +#endif // EXTCLASS_DEMO_DWA052200_H_ diff --git a/test/comprehensive.py b/test/comprehensive.py new file mode 100644 index 00000000..578197f1 --- /dev/null +++ b/test/comprehensive.py @@ -0,0 +1,1087 @@ +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 + + >>> try: ext = Foo('foo') + ... except TypeError, err: + ... assert re.match( + ... '(illegal argument type for built-in operation)|(an integer is required)', str(err)) + ... else: print 'no exception' + + >>> ext = Foo(1) + +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' + +We can even mulitply inherit from built-in Python classes, even if they are +first in the list of bases + + >>> class RealPythonClass: + ... def real_python_method(self): + ... print 'RealPythonClass.real_python_method()' + ... def other_first(self, other): + ... return other.first() + + >>> class MISubclass2(RealPythonClass, Bar): + ... def new_method(self): + ... print 'MISubclass2.new_method()' + ... bound_function = RealPythonClass().other_first + ... + >>> mi2 = MISubclass2(7, 8) + >>> mi2.first() # we can call inherited member functions from Bar + 7 + >>> mi2.real_python_method() # we can call inherited member functions from RealPythonClass + RealPythonClass.real_python_method() + + >>> mi2.new_method() # we can call methods on the common derived class + MISubclass2.new_method() + + We can call unbound methods from the base class accessed through the derived class + >>> MISubclass2.real_python_method(mi2) + RealPythonClass.real_python_method() + + We have not interfered with ordinary python bound methods + >>> MISubclass2.bound_function(mi2) + 7 + >>> mi2.bound_function() + 7 + +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 convertible into 'Bar'. + +The clone function on Baz returns a smart pointer; we wrap it into an +extension_instance and make it look just like any other Baz obj. + + >>> 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)) + ... else: + ... print 'no exception' + +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)) + ... else: + ... print 'no exeption' + +Polymorphism also works: + + >>> polymorphic_foo = baz.create_foo() + >>> polymorphic_foo.call_pure() + 'this was never pure!' + >>> baz.get_foo_value(polymorphic_foo) + 1000 + +Pickling tests: + + >>> world.__module__ + 'demo' + >>> world.__safe_for_unpickling__ + 1 + >>> world.__reduce__() + 'world' + >>> reduced = world('Hello').__reduce__() + >>> reduced[0] == world + 1 + >>> reduced[1:] + (('Hello',), (0,)) + >>> import StringIO + >>> import cPickle + >>> pickle = cPickle + >>> for number in (24, 42): + ... wd = world('California') + ... wd.set_secret_number(number) + ... # Dump it out and read it back in. + ... f = StringIO.StringIO() + ... pickle.dump(wd, f) + ... f = StringIO.StringIO(f.getvalue()) + ... wl = pickle.load(f) + ... # + ... print wd.greet(), wd.get_secret_number() + ... print wl.greet(), wl.get_secret_number() + ... + Hello from California! 24 + Hello from California! 24 + Hello from California! 42 + Hello from California! 0 + +Special member attributes. Tests courtesy of Barry Scott + + >>> class DerivedFromFoo(Foo): + ... def __init__(self): + ... Foo.__init__( self, 1 ) + ... def fred(self): + ... 'Docs for DerivedFromFoo.fred' + ... print 'Barry.fred' + ... def __del__(self): + ... print 'Deleting DerivedFromFoo' + + >>> class Base: + ... i_am_base = 'yes' + ... def fred(self): + ... 'Docs for Base.fred' + ... pass + + + >>> class DerivedFromBase(Base): + ... i_am_derived_from_base = 'yes' + ... def fred(self): + ... 'Docs for DerivedFromBase.fred' + ... pass + + >>> df = DerivedFromFoo() + >>> dir(df) + [] + >>> dir(DerivedFromFoo) + ['__del__', '__doc__', '__init__', '__module__', 'fred'] + >>> df.__dict__ + {} + + >>> df.fred.__doc__ + 'Docs for DerivedFromFoo.fred' + >>> db = DerivedFromBase() + >>> dir(db) + [] + >>> dir(DerivedFromBase) + ['__doc__', '__module__', 'fred', 'i_am_derived_from_base'] + >>> db.__dict__ + {} + >>> db.fred.__doc__ + 'Docs for DerivedFromBase.fred' + +Special member functions in action + >>> del df + Deleting DerivedFromFoo + + # force method table sharing + >>> class DerivedFromStringMap(StringMap): pass + ... + + >>> 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 + +Check for the ability to pass a non-const reference as a constructor parameter + >>> x = Fubar(Foo(1)) + +Some simple 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)) + ... else: print 'no exception' + +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] + +Numeric tests: + >>> x = Rational(2,3) + >>> y = Rational(1,4) + >>> print x + y + 11/12 + >>> print x - y + 5/12 + >>> print x * y + 1/6 + >>> print x / y + 8/3 + >>> print x + 1 # testing coercion + 5/3 + >>> print 1 + x # coercion the other way + 5/3 + +delete non-existent attribute: + del m.foobar + Traceback (innermost last): + File "", line 1, in ? + AttributeError: delete non-existing obj 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 + +Testing overloaded free functions + >>> overloaded() + 'Hello world!' + >>> overloaded(1) + 1 + >>> overloaded('foo') + 'foo' + >>> overloaded(1,2) + 3 + >>> overloaded(1,2,3) + 6 + >>> overloaded(1,2,3,4) + 10 + >>> overloaded(1,2,3,4,5) + 15 + >>> try: overloaded(1, 'foo') + ... except TypeError, err: + ... assert re.match("No overloaded functions match \(int, string\)\. Candidates are:", + ... str(err)) + ... else: + ... print 'no exception' + +Testing overloaded constructors + + >>> over = OverloadTest() + >>> over.getX() + 1000 + >>> over = OverloadTest(1) + >>> over.getX() + 1 + >>> over = OverloadTest(1,1) + >>> over.getX() + 2 + >>> over = OverloadTest(1,1,1) + >>> over.getX() + 3 + >>> over = OverloadTest(1,1,1,1) + >>> over.getX() + 4 + >>> over = OverloadTest(1,1,1,1,1) + >>> over.getX() + 5 + >>> over = OverloadTest(over) + >>> over.getX() + 5 + >>> try: over = OverloadTest(1, 'foo') + ... except TypeError, err: + ... assert re.match("No overloaded functions match \(OverloadTest, int, string\)\. Candidates are:", + ... str(err)) + ... else: + ... print 'no exception' + +Testing overloaded methods + + >>> over.setX(3) + >>> over.overloaded() + 3 + >>> over.overloaded(1) + 1 + >>> over.overloaded(1,1) + 2 + >>> over.overloaded(1,1,1) + 3 + >>> over.overloaded(1,1,1,1) + 4 + >>> over.overloaded(1,1,1,1,1) + 5 + >>> try: over.overloaded(1,'foo') + ... except TypeError, err: + ... assert re.match("No overloaded functions match \(OverloadTest, int, string\)\. Candidates are:", + ... str(err)) + ... else: + ... print 'no exception' + +Testing base class conversions + + >>> testUpcast(over) + Traceback (innermost last): + TypeError: extension class 'OverloadTest' is not convertible into 'Base'. + >>> der1 = Derived1(333) + >>> der1.x() + 333 + >>> testUpcast(der1) + 333 + >>> der1 = derived1Factory(1000) + >>> testDowncast1(der1) + 1000 + >>> testDowncast2(der1) + Traceback (innermost last): + TypeError: extension class 'Base' is not convertible into 'Derived2'. + >>> der2 = Derived2(444) + >>> der2.x() + 444 + >>> testUpcast(der2) + 444 + >>> der2 = derived2Factory(1111) + >>> testDowncast2(der2) + Traceback (innermost last): + TypeError: extension class 'Base' is not convertible into 'Derived2'. + +Testing interaction between callbacks, base declarations, and overloading +- testCallback() calls callback() (within C++) +- callback() is overloaded (in the wrapped class CallbackTest) +- callback() is redefined in RedefineCallback (overloading is simulated by type casing) +- testCallback() should use the redefined callback() + + >>> c = CallbackTest() + >>> c.testCallback(1) + 2 + >>> c.testCallback('foo') + Traceback (innermost last): + File "", line 1, in ? + TypeError: illegal argument type for built-in operation + >>> c.callback(1) + 2 + >>> c.callback('foo') + 'foo 1' + + >>> import types + >>> class RedefineCallback(CallbackTest): + ... def callback(self, x): + ... if type(x) is types.IntType: + ... return x - 2 + ... else: + ... return CallbackTest.callback(self,x) + ... + >>> r = RedefineCallback() + >>> r.callback(1) + -1 + >>> r.callback('foo') + 'foo 1' + >>> r.testCallback('foo') + Traceback (innermost last): + File "", line 1, in ? + TypeError: illegal argument type for built-in operation + >>> r.testCallback(1) + -1 + >>> testCallback(r, 1) + -1 + +Regression test for a reference-counting bug thanks to Mark Evans +() + >>> sizelist([]) + 0.0 + >>> sizelist([1, 2, 4]) + 3.0 + +And another for doubles + >>> vector_double().push_back(3.0) + +Tests for method lookup in the context of inheritance +Set up the tests + + >>> a1 = A1() + >>> a2 = A2() + >>> b1 = B1() + >>> b2 = B2() + >>> pa1_a1 = factoryA1asA1() + >>> pb1_a1 = factoryB1asA1() + >>> pb2_a1 = factoryB2asA1() + >>> pc_a1 = factoryCasA1() + >>> pa2_a2 = factoryA2asA2() + >>> pb1_a2 = factoryB1asA2() + >>> pb1_b1 = factoryB1asB1() + >>> pc_b1 = factoryCasB1() + >>> class DA1(A1): + ... def overrideA1(self): + ... return 'DA1.overrideA1' + ... + >>> da1 = DA1() + >>> class DB1(B1): + ... def overrideA1(self): + ... return 'DB1.overrideA1' + ... def overrideB1(self): + ... return 'DB1.overrideB1' + ... + >>> db1 = DB1() + >>> class DB2(B2): pass + ... + >>> db2 = DB2() + +test overrideA1 + + >>> a1.overrideA1() + 'A1::overrideA1' + >>> b1.overrideA1() + 'B1::overrideA1' + >>> b2.overrideA1() + 'B2::overrideA1' + >>> da1.overrideA1() + 'DA1.overrideA1' + >>> db1.overrideA1() + 'DB1.overrideA1' + >>> pa1_a1.overrideA1() + 'A1::overrideA1' + >>> pb1_a1.overrideA1() + 'B1::overrideA1' + >>> pb2_a1.overrideA1() + 'B2::overrideA1' + >>> pb1_b1.overrideA1() + 'B1::overrideA1' + >>> pc_a1.overrideA1() + 'B1::overrideA1' + >>> pc_b1.overrideA1() + 'B1::overrideA1' + +test call_overrideA1 + + >>> call_overrideA1(a1) + 'A1::overrideA1' + >>> call_overrideA1(b1) + 'B1::overrideA1' + >>> call_overrideA1(b2) + 'B2::overrideA1' + >>> call_overrideA1(da1) + 'DA1.overrideA1' + >>> call_overrideA1(db1) + 'DB1.overrideA1' + >>> call_overrideA1(pa1_a1) + 'A1::overrideA1' + >>> call_overrideA1(pb1_a1) + 'B1::overrideA1' + >>> call_overrideA1(pb2_a1) + 'B2::overrideA1' + >>> call_overrideA1(pb1_b1) + 'B1::overrideA1' + >>> call_overrideA1(pc_a1) + 'B1::overrideA1' + >>> call_overrideA1(pc_b1) + 'B1::overrideA1' + +test inheritA1 + + >>> a1.inheritA1() + 'A1::inheritA1' + >>> b1.inheritA1() + 'A1::inheritA1' + >>> b2.inheritA1() + 'A1::inheritA1' + >>> da1.inheritA1() + 'A1::inheritA1' + >>> db1.inheritA1() + 'A1::inheritA1' + >>> pa1_a1.inheritA1() + 'A1::inheritA1' + >>> pb1_a1.inheritA1() + 'A1::inheritA1' + >>> pb2_a1.inheritA1() + 'A1::inheritA1' + >>> pb1_b1.inheritA1() + 'A1::inheritA1' + >>> pc_a1.inheritA1() + 'A1::inheritA1' + >>> pc_b1.inheritA1() + 'A1::inheritA1' + +test call_inheritA1 + + >>> call_inheritA1(a1) + 'A1::inheritA1' + >>> call_inheritA1(b1) + 'A1::inheritA1' + >>> call_inheritA1(b2) + 'A1::inheritA1' + >>> call_inheritA1(da1) + 'A1::inheritA1' + >>> call_inheritA1(db1) + 'A1::inheritA1' + >>> call_inheritA1(pa1_a1) + 'A1::inheritA1' + >>> call_inheritA1(pb1_a1) + 'A1::inheritA1' + >>> call_inheritA1(pb2_a1) + 'A1::inheritA1' + >>> call_inheritA1(pb1_b1) + 'A1::inheritA1' + >>> call_inheritA1(pc_a1) + 'A1::inheritA1' + >>> call_inheritA1(pc_b1) + 'A1::inheritA1' + +test inheritA2 + + >>> a2.inheritA2() + 'A2::inheritA2' + >>> b1.inheritA2() + 'A2::inheritA2' + >>> b2.inheritA2() + 'A2::inheritA2' + >>> db1.inheritA2() + 'A2::inheritA2' + >>> pa2_a2.inheritA2() + 'A2::inheritA2' + >>> pb1_a2.inheritA2() + 'A2::inheritA2' + >>> pb1_b1.inheritA2() + 'A2::inheritA2' + +test overrideB1 + + >>> b1.overrideB1() + 'B1::overrideB1' + >>> db1.overrideB1() + 'DB1.overrideB1' + >>> pb1_b1.overrideB1() + 'B1::overrideB1' + >>> pc_b1.overrideB1() + 'C::overrideB1' + +test call_overrideB1 + + >>> call_overrideB1(b1) + 'B1::overrideB1' + >>> call_overrideB1(db1) + 'DB1.overrideB1' + >>> call_overrideB1(pb1_a1) + 'B1::overrideB1' + >>> call_overrideB1(pc_a1) + 'C::overrideB1' + >>> call_overrideB1(pb1_b1) + 'B1::overrideB1' + >>> call_overrideB1(pc_b1) + 'C::overrideB1' + +test inheritB2 + + >>> b2.inheritB2() + 'B2::inheritB2' + >>> db2.inheritB2() + 'B2::inheritB2' + +========= test the new def_raw() feature ========== + + >>> r = RawTest(1) + >>> raw(r,1,third=1,fourth=1) + 4 + >>> r.raw(1,third=1,fourth=1) + 4 + >>> raw(r,1,third=1,f=1) + Traceback (innermost last): + KeyError: fourth + >>> raw(r,1,third=1) + Traceback (innermost last): + TypeError: wrong number of arguments + >>> raw(r,1) + Traceback (innermost last): + TypeError: wrong number of arguments + >>> raw() + Traceback (innermost last): + TypeError: wrong number of arguments + >>> raw1(1,second=1) + 2 + >>> raw1(1) + 1 + >>> raw1(second=1) + 1 + >>> raw1() + 0 + >>> raw2(1,second=1) + 2 + >>> raw2(1) + 1 + >>> raw2(second=1) + 1 + >>> raw2() + 0 + +========= test export of operators ========== + + >>> i = Int(2) + >>> j = i+i + >>> j.i() + 4 + >>> j = i-i + >>> j.i() + 0 + >>> j = i*i + >>> j.i() + 4 + >>> i>> cmp(i,i) + 0 + >>> k = Int(5) + >>> j = divmod(k, i) + >>> j[0].i() + 2 + >>> j[1].i() + 1 + >>> j = pow(i, k) + >>> j.i() + 32 + >>> j = pow(i, k, k) + >>> j.i() + 2 + >>> j = -i + >>> j.i() + -2 + >>> str(i) + '2' + >>> j = i/i + Traceback (innermost last): + TypeError: bad operand type(s) for / + >>> j = abs(i) + Traceback (innermost last): + TypeError: bad operand type for abs() + >>> j = i+1 + >>> j.i() + 3 + >>> j = i-1 + >>> j.i() + 1 + >>> j = i*1 + >>> j.i() + 2 + >>> i<1 + 0 + >>> cmp(i,1) + 1 + >>> j = pow(i, 5) + >>> j.i() + 32 + >>> j = pow(i, 5, k) + Traceback (innermost last): + TypeError: bad operand type(s) for pow() + >>> j = pow(i, 5, 5) + Traceback (innermost last): + TypeError: bad operand type(s) for pow() + >>> j = i/1 + Traceback (innermost last): + TypeError: bad operand type(s) for / + >>> j = 1+i + >>> j.i() + 3 + >>> j = 1-i + >>> j.i() + -1 + >>> j = 1*i + >>> j.i() + 2 + >>> 1>> cmp(1,i) + -1 + >>> j = 1/i + Traceback (innermost last): + TypeError: bad operand type(s) for / + >>> pow(1,i) + Traceback (innermost last): + TypeError: bad operand type(s) for pow() + +Test operator export to a subclass + + # force method table sharing + >>> class IntDerived1(Int): pass + ... + + >>> class IntDerived(Int): + ... def __init__(self, i): + ... Int.__init__(self, i) + ... def __str__(self): + ... return 'IntDerived: ' + str(self.i()) + ... + >>> f = IntDerived(3) + >>> str(f) + 'IntDerived: 3' + >>> j = f * f + >>> j.i() + 9 + >>> j = f * i + >>> j.i() + 6 + >>> j = f * 5 + >>> j.i() + 15 + >>> j = i * f + >>> j.i() + 6 + >>> j = 5 * f + >>> j.i() + 15 + + +========= Prove that the "phantom base class" issue is resolved ========== + + >>> assert pa1_a1.__class__ == A1 + >>> assert pb1_a1.__class__ == A1 + >>> assert pb2_a1.__class__ == A1 + >>> assert pc_a1.__class__ == A1 + >>> assert pa2_a2.__class__ == A2 + >>> assert pb1_a2.__class__ == A2 + >>> assert pb1_b1.__class__ == B1 + >>> assert pc_b1.__class__ == B1 + >>> assert A1 in B1.__bases__ + >>> assert A2 in B1.__bases__ + >>> assert A1 in B2.__bases__ + >>> assert A2 in B2.__bases__ + >>> assert A1 in DA1.__bases__ + >>> assert B1 in DB1.__bases__ + >>> assert B2 in DB2.__bases__ + +=============================================================== +test methodologies for wrapping functions that return a pointer + + >>> get_record().value + 1234 + + In this methodology, the referent is copied + >>> get_record() == get_record() + 0 + +======== Enums and non-method class attributes ============== + >>> eo = EnumOwner(EnumOwner.one, EnumOwner.two) + >>> eo.first + 1 + >>> eo.second + 2 + >>> eo.first = EnumOwner.three + >>> eo.second = EnumOwner.one + >>> eo.first + 3 + >>> eo.second + 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()