mirror of
https://github.com/boostorg/python.git
synced 2026-01-24 18:12:43 +00:00
This commit was manufactured by cvs2svn to create branch
'coercion_experiments'. [SVN r8021]
This commit is contained in:
@@ -59,4 +59,4 @@ inline BaseObject<PythonType>::~BaseObject()
|
||||
|
||||
}
|
||||
|
||||
#endif BASE_OBJECT_DWA051600_H_
|
||||
#endif // BASE_OBJECT_DWA051600_H_
|
||||
|
||||
@@ -20,9 +20,6 @@ class ClassWrapper
|
||||
public:
|
||||
ClassWrapper(Module& module, const char* name)
|
||||
: m_class(new ExtensionClass<T, U>(name))
|
||||
#if 0 // def PY_MSVC6_OR_EARLIER
|
||||
, m_msvc_hack(name)
|
||||
#endif
|
||||
{
|
||||
module.add(Ptr(as_object(m_class.get()), Ptr::new_ref), name);
|
||||
}
|
||||
@@ -68,11 +65,47 @@ class ClassWrapper
|
||||
template <class MemberType>
|
||||
void def_read_write(MemberType T::*pm, const char* name)
|
||||
{ m_class->def_read_write(pm, name); }
|
||||
|
||||
// declare the given class a base class of this one and register
|
||||
// conversion functions
|
||||
template <class S, class V>
|
||||
void declare_base(ClassWrapper<S, V> const & base)
|
||||
{
|
||||
m_class->declare_base(base.get_extension_class());
|
||||
}
|
||||
|
||||
// declare the given class a base class of this one and register
|
||||
// upcast conversion function
|
||||
template <class S, class V>
|
||||
void declare_base(ClassWrapper<S, V> const & base, WithoutDowncast)
|
||||
{
|
||||
m_class->declare_base(base.get_extension_class(), without_downcast);
|
||||
}
|
||||
|
||||
// declare the given class a base class of this one and register
|
||||
// conversion functions
|
||||
template <class S, class V>
|
||||
void declare_base(ExtensionClass<S, V> * base)
|
||||
{
|
||||
m_class->declare_base(base);
|
||||
}
|
||||
|
||||
// declare the given class a base class of this one and register
|
||||
// upcast conversion function
|
||||
template <class S, class V>
|
||||
void declare_base(ExtensionClass<S, V> * base, WithoutDowncast)
|
||||
{
|
||||
m_class->declare_base(base, without_downcast);
|
||||
}
|
||||
|
||||
// get the embedded ExtensioClass object
|
||||
ExtensionClass<T, U> * get_extension_class() const
|
||||
{
|
||||
return m_class.get();
|
||||
}
|
||||
|
||||
private:
|
||||
PyPtr<ExtensionClass<T, U> > m_class;
|
||||
#if 0 // def PY_MSVC6_OR_EARLIER
|
||||
PyExtensionClassConverters<T, U> m_msvc_hack;
|
||||
#endif
|
||||
};
|
||||
|
||||
// The bug mentioned at the top of this file is that on certain compilers static
|
||||
|
||||
176
comparisons.html
Normal file
176
comparisons.html
Normal file
@@ -0,0 +1,176 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
|
||||
"http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<title>
|
||||
Comparisons with Other Systems
|
||||
</title>
|
||||
<div>
|
||||
<h1>
|
||||
<img width="277" height="86" id="_x0000_i1025" align="center"
|
||||
src="c++boost.gif" alt= "c++boost.gif (8819 bytes)">Comparisons with
|
||||
Other Systems
|
||||
</h1>
|
||||
|
||||
<h2>CXX</h2>
|
||||
<p>
|
||||
Like py_cpp, <a href="http://cxx.sourceforge.net/">CXX</a> attempts to
|
||||
provide a C++-oriented interface to Python. In most cases, like py_cpp,
|
||||
it relieves the user from worrying about reference-counts. As far as I
|
||||
can tell, there is no support for subclassing C++ extension types in
|
||||
Python. An even more-significant difference is that a user's C++ code is
|
||||
still basically "dealing with Python objects", though they are wrapped
|
||||
in C++ classes. This means such jobs as argument parsing and conversion
|
||||
are still left to be done explicitly by the user. This is not entirely a
|
||||
bad thing, as you can do some Pythonic things with CXX (e.g. variable
|
||||
and keyword arguments) that I haven't yet figured out how to enable with
|
||||
py_cpp. As far as I can tell, also CXX enables one to write what is
|
||||
essentially idiomatic Python code in C++, manipulating Python objects
|
||||
through the same fully-generic interfaces we use in Python. That use is
|
||||
also supported (less-well) by the py_cpp object wrappers. <a
|
||||
href="mailto:dubois1@llnl.gov">Paul F. Dubois</a>, the CXX maintainer,
|
||||
has told me that what I've described is only half of the picture with
|
||||
CXX, but I never understood his explanation well-enough to fill in the
|
||||
other half. I hope that you, dear reader, may be able to help me
|
||||
complete my comparitive analysis of CXX.
|
||||
|
||||
|
||||
<h2>SWIG</h2>
|
||||
<p>
|
||||
<a href= "http://www.swig.org/">SWIG</a> is an impressively mature tool
|
||||
for exporting an existing ANSI 'C' interface into various scripting
|
||||
languages. Swig relies on a parser to read your source code and produce
|
||||
additional source code files which can be compiled into a Python (or
|
||||
Perl or Tcl) extension module. It has been successfully used to create
|
||||
many Python extension modules. Like py_cpp, SWIG is trying to allow an
|
||||
existing interface to be wrapped with little or no change to the
|
||||
existing code. The documentation says "SWIG parses a form of ANSI C
|
||||
syntax that has been extended with a number of special directives. As a
|
||||
result, interfaces are usually built by grabbing a header file and
|
||||
tweaking it a little bit." For C++ interfaces, the tweaking has often
|
||||
proven to amount to more than just a little bit. One user
|
||||
writes:<blockquote> "The problem with swig (when I used it) is that it
|
||||
couldnt handle templates, didnt do func overloading properly etc. For
|
||||
ANSI C libraries this was fine. But for usual C++ code this was a
|
||||
problem. Simple things work. But for anything very complicated (or
|
||||
realistic), one had to write code by hand. I believe py_cpp doesnt have
|
||||
this problem[sic]... IMHO overloaded functions are very important to
|
||||
wrap correctly."<br><i>-Prabhu Ramachandran</i>
|
||||
</blockquote>
|
||||
|
||||
<p>
|
||||
By contrast, py_cpp doesn't attempt to parse C++ - the problem is simply
|
||||
too complex to do correctly. Technically, one does write code by hand to
|
||||
use py_cpp. The goal, however, has been to make that code nearly as
|
||||
simple as listing the names of the classes and member functions you want
|
||||
to expose in Python.
|
||||
|
||||
<h2>SIP</h2>
|
||||
<p>
|
||||
<a
|
||||
href="http://www.thekompany.com/projects/pykde/background.php3?dhtml_ok=1">SIP</a>
|
||||
is a system similar to SWIG, though seemingly more
|
||||
C++-oriented. The author says that like py_cpp, SIP supports overriding
|
||||
extension class member functions in Python subclasses. It appears to
|
||||
have been designed specifically to directly support some features of
|
||||
PyQt/PyKDE, which is its primary client. Documentation is almost
|
||||
entirely missing at the time of this writing, so a detailed comparison
|
||||
is difficult.
|
||||
|
||||
<h2>ILU</h2>
|
||||
<p>
|
||||
<a
|
||||
href="http://www.cl.cam.ac.uk/Research/Rainbow/projects/origami/ilu-1.8-manual">ILU</a>
|
||||
is a very ambitious project which tries to describe a module's interface
|
||||
(types and functions) in terms of an <a
|
||||
href="http://www.cl.cam.ac.uk/Research/Rainbow/projects/origami/ilu-1.8-manual/manual_2.html">Interface
|
||||
Specification Language</a> (ISL) so that it can be uniformly interfaced
|
||||
to a wide range of computer languages, including Common Lisp, C++, C,
|
||||
Modula-3, and Python. ILU can parse the ISL to generate a C++ language
|
||||
header file describing the interface, of which the user is expected to
|
||||
provide an implementation. Unlike py_cpp, this means that the system
|
||||
imposes implementation details on your C++ code at the deepest level. It
|
||||
is worth noting that some of the C++ names generated by ILU are supposed
|
||||
to be reserved to the C++ implementation. It is unclear from the
|
||||
documentation whether ILU supports overriding C++ virtual functions in Python.
|
||||
|
||||
<h2>Zope ExtensionClasses</h2>
|
||||
<p>
|
||||
<a href="http:http://www.digicool.com/releases/ExtensionClass">
|
||||
ExtensionClasses in Zope</a> use the same underlying mechanism as py_cpp
|
||||
to support subclassing of extension types in Python, including
|
||||
multiple-inheritance. Both systems rely on the same "<a
|
||||
href="http://www.python.org/workshops/1994-11/BuiltInClasses/Welcome.html">Don
|
||||
Beaudry Hack</a>" that also inspired Don's MESS System.
|
||||
<p>
|
||||
The major differences are:
|
||||
<ul>
|
||||
<li>
|
||||
py_cpp lifts the burden on the user to parse and convert function
|
||||
argument types. Zope provides no such facility.
|
||||
<li>
|
||||
py_cpp lifts the burden on the user to maintain Python
|
||||
reference-counts.
|
||||
<li>
|
||||
py_cpp supports function overloading; Zope does not.
|
||||
<li>
|
||||
py_cpp supplies a simple mechanism for exposing read-only and
|
||||
read/write access to data members of the wrapped C++ type as Python
|
||||
attributes.
|
||||
<li>
|
||||
Writing a Zope ExtensionClass is significantly more complex than
|
||||
exposing a C++ class to python using py_cpp (mostly a summary of the
|
||||
previous 4 items). <a href=
|
||||
"http://www.digicool.com/releases/ExtensionClass/MultiMapping.html">A
|
||||
Zope Example</a> illustrates the differences.
|
||||
<li>
|
||||
Zope's ExtensionClasses are specifically motivated by "the need for a
|
||||
C-based persistence mechanism". Py_cpp's are motivated by the desire
|
||||
to simply reflect a C++ API into Python with as little modification as
|
||||
possible.
|
||||
<li>
|
||||
Thus, Zope's ExtensionClasses support pickling. Currently py_cpp
|
||||
ExtensionClasses do not.
|
||||
<li>
|
||||
The following Zope restriction does not apply to py_cpp: "At most one
|
||||
base extension direct or indirect super class may define C data
|
||||
members. If an extension subclass inherits from multiple base
|
||||
extension classes, then all but one must be mix-in classes that
|
||||
provide extension methods but no data."
|
||||
<li>
|
||||
Zope's Extension SubClasses admit multiple-inheritance from both Zope
|
||||
ExtensionClasses <i>and</i> Python classes. This capability is currently
|
||||
not available in py_cpp.
|
||||
<li>
|
||||
Zope supplies the standard special Python class attributes __doc__,
|
||||
__name__, __bases__, __dict__, and __module__ on its
|
||||
ExtensionClasses; py_cpp does not (coming soon)
|
||||
<li>
|
||||
Zope supplies some creative but esoteric idioms such as <a href=
|
||||
"http://www.digicool.com/releases/ExtensionClass/Acquisition.html">
|
||||
Acquisition</a>.
|
||||
<li>
|
||||
Zope's ComputedAttribute support is designed to be used from Python.
|
||||
<a href="special.html#getter_setter">The analogous feature of
|
||||
py_cpp</a> can be used from C++ or Python. The feature is arguably
|
||||
easier to use in py_cpp.
|
||||
</ul>
|
||||
<p>
|
||||
Also, the Zope docs say: "The first superclass listed in the class
|
||||
statement defining an extension subclass must be either a base
|
||||
extension class or an extension subclass. This restriction will be
|
||||
removed in Python-1.5." I believe that this promise was made
|
||||
prematurely. I have looked at the Python 1.5.2 source code and I don't
|
||||
believe it is possible to deliver on it.
|
||||
<p>
|
||||
Previous: <a href="extending.html">A Brief Introduction to writing Python Extension Modules</a>
|
||||
Next: <a href="example1.html">A Simple Example Using py_cpp</a>
|
||||
Up: <a href="py_cpp.html">Top</a>
|
||||
<p>
|
||||
© Copyright David Abrahams 2000. Permission to copy, use, modify,
|
||||
sell and distribute this document is granted provided this copyright
|
||||
notice appears in all copies. This document is provided "as is" without
|
||||
express or implied warranty, and with no claim as to its suitability
|
||||
for any purpose.
|
||||
<p>
|
||||
Updated: Oct 18, 2000
|
||||
</div>
|
||||
|
||||
4
demo.bdf
4
demo.bdf
@@ -1,4 +0,0 @@
|
||||
TargetName=demo
|
||||
TargetType=dll
|
||||
SourceFiles=extclass_demo.cpp
|
||||
Libs=py_cpp python utils
|
||||
@@ -1,4 +0,0 @@
|
||||
TargetName=demo_d
|
||||
TargetType=dll
|
||||
SourceFiles=extclass_demo_d.cpp
|
||||
Libs=py_cpp_d python_d utils
|
||||
@@ -1,4 +0,0 @@
|
||||
TargetName=example1
|
||||
TargetType=dll
|
||||
SourceFiles=example1.cpp
|
||||
Libs=py_cpp python utils
|
||||
127
example1.html
Normal file
127
example1.html
Normal file
@@ -0,0 +1,127 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
|
||||
"http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<title>
|
||||
A Simple Example Using py_cpp
|
||||
</title>
|
||||
<div>
|
||||
<h1>
|
||||
<img width="277" height="86" id="_x0000_i1025" src="c++boost.gif" alt=
|
||||
"c++boost.gif (8819 bytes)">
|
||||
</h1>
|
||||
<h1>
|
||||
A Simple Example Using py_cpp
|
||||
</h1>
|
||||
<p>
|
||||
Suppose we have the following C++ API which we want to expose in
|
||||
Python:
|
||||
<blockquote>
|
||||
<pre>
|
||||
namespace hello {
|
||||
class world
|
||||
{
|
||||
public:
|
||||
world(int);
|
||||
~world();
|
||||
const char* get() const { return "hi, world"; }
|
||||
...
|
||||
};
|
||||
void length(const world& x) { return std::strlen(x.get()); }
|
||||
}
|
||||
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Here is the C++ code for a python module called <code>hello</code>
|
||||
which exposes the API using py_cpp:
|
||||
<blockquote>
|
||||
<pre>
|
||||
#include <py_cpp/class_wrapper.h>
|
||||
// Python requires an exported function called init<module-name> in every
|
||||
// extension module. This is where we build the module contents.
|
||||
extern "C"
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
void inithello()
|
||||
{
|
||||
try
|
||||
{
|
||||
// create an object representing this extension module
|
||||
py::Module hello("hello");
|
||||
// Create the Python type object for our extension class
|
||||
py::ClassWrapper<hello::world> world_class(hello, "world");
|
||||
// Add the __init__ function
|
||||
world_class.def(py::Constructor<int>());
|
||||
// Add a regular member function
|
||||
world_class.def(&hello::world::get, "get");
|
||||
// Add a regular function to the module
|
||||
hello.def(hello::length, "length");
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
py::handle_exception(); // Deal with the exception for Python
|
||||
}
|
||||
}
|
||||
// Win32 DLL boilerplate
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif // _WIN32
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
That's it! If we build this shared library and put it on our <code>
|
||||
PYTHONPATH</code> we can now access our C++ class and function from
|
||||
Python.
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> import hello
|
||||
>>> hi_world = hello.world(3)
|
||||
>>> hi_world.get()
|
||||
'hi, world'
|
||||
>>> hello.length(hi_world)
|
||||
9
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
We can even make a subclass of <code>hello.world</code>:
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> class my_subclass(hello.world):
|
||||
... def get(self):
|
||||
... return 'hello, world'
|
||||
...
|
||||
>>> y = my_subclass(4)
|
||||
>>> y.get()
|
||||
'hello, world'
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Pretty cool! You can't do that with an ordinary Python extension type!
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> hello.length(y)
|
||||
9
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Of course, you may now have a slightly empty feeling in the pit of
|
||||
your little pythonic stomach. Perhaps you feel your subclass deserves
|
||||
to have a <code>length()</code> of <code>12</code>? If so, <a href=
|
||||
"overriding.html">read on</a>...
|
||||
<p>
|
||||
Previous: <a href="comparisons.html">Comparisons with other systems</a> Next: <a href="overriding.html">Overridable virtual functions</a> Up:
|
||||
<a href="py_cpp.html">Top</a>
|
||||
<p>
|
||||
© Copyright David Abrahams 2000. Permission to copy, use, modify,
|
||||
sell and distribute this document is granted provided this copyright
|
||||
notice appears in all copies. This document is provided "as is" without
|
||||
express or implied warranty, and with no claim as to its suitability
|
||||
for any purpose.
|
||||
<p>
|
||||
Updated: Oct 15, 2000
|
||||
</div>
|
||||
|
||||
62
extclass.cpp
62
extclass.cpp
@@ -157,7 +157,7 @@ void report_missing_instance_data(
|
||||
}
|
||||
else
|
||||
{
|
||||
two_string_error(PyExc_TypeError, "extension class '%.*s' is not derived from '%.*s'.",
|
||||
two_string_error(PyExc_TypeError, "extension class '%.*s' is not convertible into '%.*s'.",
|
||||
instance->ob_type->tp_name, target_class->tp_name);
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,66 @@ ExtensionClassBase::ExtensionClassBase(const char* name)
|
||||
{
|
||||
}
|
||||
|
||||
// This function is used in from_python() to convert wrapped classes that are
|
||||
// related by inheritance. The problem is this: although C++ provides all necessary
|
||||
// conversion operators, source and target of a conversion must be known at compile
|
||||
// time. However, in Python we want to convert classes at runtime. The solution is to
|
||||
// generate conversion functions at compile time, register them within the appropriate
|
||||
// class objects and call them when a particular runtime conversion is required.
|
||||
|
||||
// If functions for any possible conversion have to be stored, their number will grow
|
||||
// qudratically. To reduce this number, we actually store only conversion functions
|
||||
// between adjacent levels in the inheritance tree. By traversing the tree recursively,
|
||||
// we can build any allowed conversion as a concatenation of simple conversions. This
|
||||
// traversal is done in the functions try_base_class_conversions() and
|
||||
// try_derived_class_conversions(). If a particular conversion is impossible, all
|
||||
// conversion functions will return a NULL pointer.
|
||||
|
||||
// The function extract_object_from_holder() attempts to actually extract the pointer
|
||||
// to the contained object from an InstanceHolderBase (a wrapper class). A conversion
|
||||
// of the held object to 'T *' is allowed when the conversion
|
||||
// 'dynamic_cast<InstanceHolder<T> *>(an_instance_holder_base)' succeeds.
|
||||
void* ExtensionClassBase::try_class_conversions(InstanceHolderBase* object) const
|
||||
{
|
||||
void* result = try_derived_class_conversions(object);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
return try_base_class_conversions(object);
|
||||
}
|
||||
|
||||
void* ExtensionClassBase::try_base_class_conversions(InstanceHolderBase* object) const
|
||||
{
|
||||
for (std::size_t i = 0; i < base_classes().size(); ++i)
|
||||
{
|
||||
if(base_classes()[i].convert == 0)
|
||||
continue;
|
||||
void* result1 = base_classes()[i].class_object->extract_object_from_holder(object);
|
||||
if (result1)
|
||||
return (*base_classes()[i].convert)(result1);
|
||||
|
||||
void* result2 = base_classes()[i].class_object->try_base_class_conversions(object);
|
||||
if (result2)
|
||||
return (*base_classes()[i].convert)(result2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* ExtensionClassBase::try_derived_class_conversions(InstanceHolderBase* object) const
|
||||
{
|
||||
for (std::size_t i = 0; i < derived_classes().size(); ++i)
|
||||
{
|
||||
void* result1 = derived_classes()[i].class_object->extract_object_from_holder(object);
|
||||
if (result1)
|
||||
return (*derived_classes()[i].convert)(result1);
|
||||
|
||||
void* result2 = derived_classes()[i].class_object->try_derived_class_conversions(object);
|
||||
if (result2)
|
||||
return (*derived_classes()[i].convert)(result2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExtensionClassBase::add_method(Function* method, const char* name)
|
||||
{
|
||||
add_method(PyPtr<Function>(method), name);
|
||||
|
||||
279
extclass.h
279
extclass.h
@@ -5,6 +5,9 @@
|
||||
//
|
||||
// The author gratefully acknowleges the support of Dragon Systems, Inc., in
|
||||
// producing this work.
|
||||
//
|
||||
// This file automatically generated for 5-argument constructors by
|
||||
// gen_extclass.py
|
||||
|
||||
#ifndef EXTENSION_CLASS_DWA052000_H_
|
||||
# define EXTENSION_CLASS_DWA052000_H_
|
||||
@@ -24,6 +27,7 @@ namespace py {
|
||||
|
||||
// forward declarations
|
||||
class ExtensionInstance;
|
||||
class ExtensionClassBase;
|
||||
template <class T> class InstanceHolder;
|
||||
template <class T, class U> class InstanceValueHolder;
|
||||
template <class Ptr, class T> class InstancePtrHolder;
|
||||
@@ -50,10 +54,39 @@ T* check_non_null(T* p)
|
||||
|
||||
template <class T> class HeldInstance;
|
||||
|
||||
namespace detail {
|
||||
typedef void* (*ConversionFunction)(void*);
|
||||
|
||||
struct BaseClassInfo
|
||||
{
|
||||
BaseClassInfo(ExtensionClassBase* t, ConversionFunction f)
|
||||
:class_object(t), convert(f)
|
||||
{}
|
||||
|
||||
ExtensionClassBase* class_object;
|
||||
ConversionFunction convert;
|
||||
};
|
||||
|
||||
typedef BaseClassInfo DerivedClassInfo;
|
||||
}
|
||||
|
||||
class ExtensionClassBase : public Class<ExtensionInstance>
|
||||
{
|
||||
public:
|
||||
ExtensionClassBase(const char* name);
|
||||
|
||||
public:
|
||||
// the purpose of try_class_conversions() and its related functions
|
||||
// is explained in extclass.cpp
|
||||
void* try_class_conversions(InstanceHolderBase*) const;
|
||||
void* try_base_class_conversions(InstanceHolderBase*) const;
|
||||
void* try_derived_class_conversions(InstanceHolderBase*) const;
|
||||
|
||||
private:
|
||||
virtual void* extract_object_from_holder(InstanceHolderBase* v) const = 0;
|
||||
virtual std::vector<py::detail::BaseClassInfo> const& base_classes() const = 0;
|
||||
virtual std::vector<py::detail::DerivedClassInfo> const& derived_classes() const = 0;
|
||||
|
||||
protected:
|
||||
void add_method(PyPtr<Function> method, const char* name);
|
||||
void add_default_method(PyPtr<Function> method, const char* name);
|
||||
@@ -69,12 +102,24 @@ template <class T>
|
||||
class ClassRegistry
|
||||
{
|
||||
public:
|
||||
static Class<ExtensionInstance>* class_object()
|
||||
static ExtensionClassBase* class_object()
|
||||
{ return static_class_object; }
|
||||
static void register_class(py::Class<py::ExtensionInstance>*);
|
||||
static void unregister_class(py::Class<py::ExtensionInstance>*);
|
||||
|
||||
// Register/unregister the Python class object corresponding to T
|
||||
static void register_class(ExtensionClassBase*);
|
||||
static void unregister_class(ExtensionClassBase*);
|
||||
|
||||
// Establish C++ inheritance relationships
|
||||
static void register_base_class(py::detail::BaseClassInfo const&);
|
||||
static void register_derived_class(py::detail::DerivedClassInfo const&);
|
||||
|
||||
// Query the C++ inheritance relationships
|
||||
static std::vector<py::detail::BaseClassInfo> const& base_classes();
|
||||
static std::vector<py::detail::DerivedClassInfo> const& derived_classes();
|
||||
private:
|
||||
static py::Class<py::ExtensionInstance>* static_class_object;
|
||||
static ExtensionClassBase* static_class_object;
|
||||
static std::vector<py::detail::BaseClassInfo> static_base_class_info;
|
||||
static std::vector<py::detail::DerivedClassInfo> static_derived_class_info;
|
||||
};
|
||||
|
||||
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back to global namespace for this GCC bug
|
||||
@@ -106,8 +151,6 @@ class PyExtensionClassConverters
|
||||
{ return py::Type<U>(); }
|
||||
#endif
|
||||
|
||||
PyExtensionClassConverters() {}
|
||||
|
||||
// Convert to T*
|
||||
friend T* from_python(PyObject* obj, py::Type<T*>)
|
||||
{
|
||||
@@ -120,6 +163,11 @@ class PyExtensionClassConverters
|
||||
py::InstanceHolder<T>* held = dynamic_cast<py::InstanceHolder<T>*>(*p);
|
||||
if (held != 0)
|
||||
return held->target();
|
||||
|
||||
// see extclass.cpp for an explanation of try_class_conversions()
|
||||
void * target = py::ClassRegistry<T>::class_object()->try_class_conversions(*p);
|
||||
if(target)
|
||||
return static_cast<T *>(target);
|
||||
}
|
||||
py::report_missing_instance_data(self, py::ClassRegistry<T>::class_object(), typeid(T));
|
||||
throw py::ArgumentError();
|
||||
@@ -168,7 +216,6 @@ class PyExtensionClassConverters
|
||||
return py::PyPtr<py::ExtensionInstance>(new py::ExtensionInstance(class_));
|
||||
}
|
||||
|
||||
|
||||
// Convert to const T*
|
||||
friend const T* from_python(PyObject* p, py::Type<const T*>)
|
||||
{ return from_python(p, py::Type<T*>()); }
|
||||
@@ -247,6 +294,28 @@ class ReadOnlySetattrFunction : public Function
|
||||
String m_name;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <class From, class To>
|
||||
struct DefineConversion
|
||||
{
|
||||
static void * upcast_ptr(void * v)
|
||||
{
|
||||
return static_cast<To *>(static_cast<From *>(v));
|
||||
}
|
||||
|
||||
static void * downcast_ptr(void * v)
|
||||
{
|
||||
return dynamic_cast<To *>(static_cast<From *>(v));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
enum WithoutDowncast { without_downcast };
|
||||
|
||||
|
||||
// An easy way to make an extension base class which wraps T. Note that Python
|
||||
// subclasses of this class will simply be Class<ExtensionInstance> objects.
|
||||
//
|
||||
@@ -279,10 +348,12 @@ class ExtensionClass
|
||||
// define constructors
|
||||
template <class A1, class A2, class A3, class A4, class A5>
|
||||
void def(Constructor<A1, A2, A3, A4, A5>)
|
||||
{ add_constructor( // the following incantation builds a Signature1,
|
||||
prepend(Type<A1>::Id(), // Signature2, ... constructor. It _should_ all
|
||||
prepend(Type<A2>::Id(), // get optimized away. Just another workaround
|
||||
prepend(Type<A3>::Id(), // for the lack of partial specialization in MSVC
|
||||
// The following incantation builds a Signature1, Signature2,... object. It
|
||||
// should _all_ get optimized away.
|
||||
{ add_constructor(
|
||||
prepend(Type<A1>::Id(),
|
||||
prepend(Type<A2>::Id(),
|
||||
prepend(Type<A3>::Id(),
|
||||
prepend(Type<A4>::Id(),
|
||||
prepend(Type<A5>::Id(),
|
||||
Signature0()))))));
|
||||
@@ -339,10 +410,49 @@ class ExtensionClass
|
||||
this->def_getter(pm, name);
|
||||
this->def_setter(pm, name);
|
||||
}
|
||||
|
||||
// declare the given class a base class of this one and register
|
||||
// up and down conversion functions
|
||||
template <class S, class V>
|
||||
void declare_base(ExtensionClass<S, V> * base)
|
||||
{
|
||||
// see extclass.cpp for an explanation of why we need to register
|
||||
// conversion functions
|
||||
detail::BaseClassInfo baseInfo(base,
|
||||
&detail::DefineConversion<S, T>::downcast_ptr);
|
||||
ClassRegistry<T>::register_base_class(baseInfo);
|
||||
add_base(Ptr(as_object(base), Ptr::new_ref));
|
||||
|
||||
detail::DerivedClassInfo derivedInfo(this,
|
||||
&detail::DefineConversion<T, S>::upcast_ptr);
|
||||
ClassRegistry<S>::register_derived_class(derivedInfo);
|
||||
}
|
||||
|
||||
// declare the given class a base class of this one and register
|
||||
// only up conversion function
|
||||
template <class S, class V>
|
||||
void declare_base(ExtensionClass<S, V> * base, WithoutDowncast)
|
||||
{
|
||||
// see extclass.cpp for an explanation of why we need to register
|
||||
// conversion functions
|
||||
detail::BaseClassInfo baseInfo(base, 0);
|
||||
ClassRegistry<T>::register_base_class(baseInfo);
|
||||
add_base(Ptr(as_object(base), Ptr::new_ref));
|
||||
|
||||
detail::DerivedClassInfo derivedInfo(this,
|
||||
&detail::DefineConversion<T, S>::upcast_ptr);
|
||||
ClassRegistry<S>::register_derived_class(derivedInfo);
|
||||
}
|
||||
|
||||
private:
|
||||
private: // types
|
||||
typedef InstanceValueHolder<T,U> Holder;
|
||||
|
||||
|
||||
private: // ExtensionClassBase virtual function implementations
|
||||
std::vector<detail::BaseClassInfo> const& base_classes() const;
|
||||
std::vector<detail::DerivedClassInfo> const& derived_classes() const;
|
||||
void* extract_object_from_holder(InstanceHolderBase* v) const;
|
||||
|
||||
private: // Utility functions
|
||||
template <class Signature>
|
||||
void add_constructor(Signature sig)
|
||||
{
|
||||
@@ -350,7 +460,69 @@ class ExtensionClass
|
||||
}
|
||||
};
|
||||
|
||||
#include "extclass_pygen.h"
|
||||
// A simple wrapper over a T which allows us to use ExtensionClass<T> with a
|
||||
// single template parameter only. See ExtensionClass<T>, above.
|
||||
template <class T>
|
||||
class HeldInstance : public T
|
||||
{
|
||||
// There are no member functions: we want to avoid inadvertently overriding
|
||||
// any virtual functions in T.
|
||||
public:
|
||||
HeldInstance(PyObject* p) : T(), m_self(p) {}
|
||||
template <class A1>
|
||||
HeldInstance(PyObject* p, const A1& a1) : T(a1), m_self(p) {}
|
||||
template <class A1, class A2>
|
||||
HeldInstance(PyObject* p, const A1& a1, const A2& a2) : T(a1, a2), m_self(p) {}
|
||||
template <class A1, class A2, class A3>
|
||||
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3) : T(a1, a2, a3), m_self(p) {}
|
||||
template <class A1, class A2, class A3, class A4>
|
||||
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4) : T(a1, a2, a3, a4), m_self(p) {}
|
||||
template <class A1, class A2, class A3, class A4, class A5>
|
||||
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) : T(a1, a2, a3, a4, a5), m_self(p) {}
|
||||
protected:
|
||||
PyObject* m_self; // Not really needed; doesn't really hurt.
|
||||
};
|
||||
|
||||
class InstanceHolderBase
|
||||
{
|
||||
public:
|
||||
virtual ~InstanceHolderBase() {}
|
||||
};
|
||||
|
||||
template <class Held>
|
||||
class InstanceHolder : public InstanceHolderBase
|
||||
{
|
||||
public:
|
||||
virtual Held *target() = 0;
|
||||
};
|
||||
|
||||
template <class Held, class Wrapper>
|
||||
class InstanceValueHolder : public InstanceHolder<Held>
|
||||
{
|
||||
public:
|
||||
Held* target() { return &m_held; }
|
||||
Wrapper* value_target() { return &m_held; }
|
||||
|
||||
InstanceValueHolder(ExtensionInstance* p) :
|
||||
m_held(p) {}
|
||||
template <class A1>
|
||||
InstanceValueHolder(ExtensionInstance* p, const A1& a1) :
|
||||
m_held(p, a1) {}
|
||||
template <class A1, class A2>
|
||||
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2) :
|
||||
m_held(p, a1, a2) {}
|
||||
template <class A1, class A2, class A3>
|
||||
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3) :
|
||||
m_held(p, a1, a2, a3) {}
|
||||
template <class A1, class A2, class A3, class A4>
|
||||
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4) :
|
||||
m_held(p, a1, a2, a3, a4) {}
|
||||
template <class A1, class A2, class A3, class A4, class A5>
|
||||
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) :
|
||||
m_held(p, a1, a2, a3, a4, a5) {}
|
||||
private:
|
||||
Wrapper m_held;
|
||||
};
|
||||
|
||||
template <class PtrType, class HeldType>
|
||||
class InstancePtrHolder : public InstanceHolder<HeldType>
|
||||
@@ -358,7 +530,7 @@ class InstancePtrHolder : public InstanceHolder<HeldType>
|
||||
public:
|
||||
HeldType* target() { return &*m_ptr; }
|
||||
PtrType& ptr() { return m_ptr; }
|
||||
|
||||
|
||||
InstancePtrHolder(PtrType ptr) : m_ptr(ptr) {}
|
||||
private:
|
||||
PtrType m_ptr;
|
||||
@@ -397,28 +569,38 @@ ExtensionClass<T, U>::ExtensionClass(const char* name)
|
||||
ClassRegistry<T>::register_class(this);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
inline
|
||||
std::vector<detail::BaseClassInfo> const &
|
||||
ExtensionClass<T, U>::base_classes() const
|
||||
{
|
||||
return ClassRegistry<T>::base_classes();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
inline
|
||||
std::vector<detail::DerivedClassInfo> const &
|
||||
ExtensionClass<T, U>::derived_classes() const
|
||||
{
|
||||
return ClassRegistry<T>::derived_classes();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
void* ExtensionClass<T, U>::extract_object_from_holder(InstanceHolderBase* v) const
|
||||
{
|
||||
py::InstanceHolder<T>* held = dynamic_cast<py::InstanceHolder<T>*>(v);
|
||||
if(held) return held->target();
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
ExtensionClass<T, U>::~ExtensionClass()
|
||||
{
|
||||
ClassRegistry<T>::unregister_class(this);
|
||||
}
|
||||
|
||||
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // Back to the global namespace for this GCC bug
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Static data member declaration.
|
||||
//
|
||||
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // Back from the global namespace for this GCC bug
|
||||
namespace py {
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
Class<py::ExtensionInstance>* ClassRegistry<T>::static_class_object;
|
||||
|
||||
template <class T>
|
||||
inline void ClassRegistry<T>::register_class(Class<ExtensionInstance>* p)
|
||||
inline void ClassRegistry<T>::register_class(ExtensionClassBase* p)
|
||||
{
|
||||
// You're not expected to create more than one of these!
|
||||
assert(static_class_object == 0);
|
||||
@@ -426,7 +608,7 @@ inline void ClassRegistry<T>::register_class(Class<ExtensionInstance>* p)
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void ClassRegistry<T>::unregister_class(Class<ExtensionInstance>* p)
|
||||
inline void ClassRegistry<T>::unregister_class(ExtensionClassBase* p)
|
||||
{
|
||||
// The user should be destroying the same object they created.
|
||||
assert(static_class_object == p);
|
||||
@@ -434,6 +616,41 @@ inline void ClassRegistry<T>::unregister_class(Class<ExtensionInstance>* p)
|
||||
static_class_object = 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void ClassRegistry<T>::register_base_class(py::detail::BaseClassInfo const & i)
|
||||
{
|
||||
static_base_class_info.push_back(i);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void ClassRegistry<T>::register_derived_class(py::detail::DerivedClassInfo const & i)
|
||||
{
|
||||
static_derived_class_info.push_back(i);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<py::detail::BaseClassInfo> const& ClassRegistry<T>::base_classes()
|
||||
{
|
||||
return static_base_class_info;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<py::detail::DerivedClassInfo> const& ClassRegistry<T>::derived_classes()
|
||||
{
|
||||
return static_derived_class_info;
|
||||
}
|
||||
|
||||
//
|
||||
// Static data member declaration.
|
||||
//
|
||||
template <class T>
|
||||
ExtensionClassBase* ClassRegistry<T>::static_class_object;
|
||||
template <class T>
|
||||
std::vector<py::detail::BaseClassInfo> ClassRegistry<T>::static_base_class_info;
|
||||
template <class T>
|
||||
std::vector<py::detail::DerivedClassInfo> ClassRegistry<T>::static_derived_class_info;
|
||||
|
||||
} // namespace py
|
||||
|
||||
#endif // EXTENSION_CLASS_DWA052000_H_
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "extclass.cpp"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "extclass_demo.h"
|
||||
#include "class_wrapper.h"
|
||||
#include <stdio.h> // used for portability on broken compilers
|
||||
#include <boost/rational.hpp>
|
||||
|
||||
namespace extclass_demo {
|
||||
|
||||
@@ -290,6 +291,245 @@ long range_hash(const Range& r)
|
||||
return r.m_start * 123 + r.m_finish;
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
/* */
|
||||
/* some functions to test overloading */
|
||||
/* */
|
||||
/************************************************************/
|
||||
|
||||
static std::string testVoid()
|
||||
{
|
||||
return std::string("Hello world!");
|
||||
}
|
||||
|
||||
static int testInt(int i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
static std::string testString(std::string i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
static int test2(int i1, int i2)
|
||||
{
|
||||
return i1+i2;
|
||||
}
|
||||
|
||||
static int test3(int i1, int i2, int i3)
|
||||
{
|
||||
return i1+i2+i3;
|
||||
}
|
||||
|
||||
static int test4(int i1, int i2, int i3, int i4)
|
||||
{
|
||||
return i1+i2+i3+i4;
|
||||
}
|
||||
|
||||
static int test5(int i1, int i2, int i3, int i4, int i5)
|
||||
{
|
||||
return i1+i2+i3+i4+i5;
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
/* */
|
||||
/* a class to test overloading */
|
||||
/* */
|
||||
/************************************************************/
|
||||
|
||||
struct OverloadTest
|
||||
{
|
||||
OverloadTest(): x_(1000) {}
|
||||
OverloadTest(int x): x_(x) {}
|
||||
OverloadTest(int x,int y): x_(x+y) { }
|
||||
OverloadTest(int x,int y,int z): x_(x+y+z) {}
|
||||
OverloadTest(int x,int y,int z, int a): x_(x+y+z+a) {}
|
||||
OverloadTest(int x,int y,int z, int a, int b): x_(x+y+z+a+b) {}
|
||||
|
||||
int x() const { return x_; }
|
||||
void setX(int x) { x_ = x; }
|
||||
|
||||
int p1(int x) { return x; }
|
||||
int p2(int x, int y) { return x + y; }
|
||||
int p3(int x, int y, int z) { return x + y + z; }
|
||||
int p4(int x, int y, int z, int a) { return x + y + z + a; }
|
||||
int p5(int x, int y, int z, int a, int b) { return x + y + z + a + b; }
|
||||
private:
|
||||
int x_;
|
||||
};
|
||||
|
||||
static int getX(OverloadTest * u)
|
||||
{
|
||||
return u->x();
|
||||
}
|
||||
|
||||
|
||||
/************************************************************/
|
||||
/* */
|
||||
/* classes to test base declarations and conversions */
|
||||
/* */
|
||||
/************************************************************/
|
||||
|
||||
struct Dummy
|
||||
{
|
||||
virtual ~Dummy() {}
|
||||
int dummy_;
|
||||
};
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual int x() const { return 999; };
|
||||
virtual ~Base() {}
|
||||
};
|
||||
|
||||
// inherit Dummy so that the Base part of Concrete starts at an offset
|
||||
// otherwise, typecast tests wouldn't be very meaningful
|
||||
struct Derived1 : public Dummy, public Base
|
||||
{
|
||||
Derived1(int x): x_(x) {}
|
||||
virtual int x() const { return x_; }
|
||||
|
||||
private:
|
||||
int x_;
|
||||
};
|
||||
|
||||
struct Derived2 : public Dummy, public Base
|
||||
{
|
||||
Derived2(int x): x_(x) {}
|
||||
virtual int x() const { return x_; }
|
||||
|
||||
private:
|
||||
int x_;
|
||||
};
|
||||
|
||||
static int testUpcast(Base * b)
|
||||
{
|
||||
return b->x();
|
||||
}
|
||||
|
||||
static std::auto_ptr<Base> derived1Factory(int i)
|
||||
{
|
||||
return std::auto_ptr<Base>(new Derived1(i));
|
||||
}
|
||||
|
||||
static std::auto_ptr<Base> derived2Factory(int i)
|
||||
{
|
||||
return std::auto_ptr<Base>(new Derived2(i));
|
||||
}
|
||||
|
||||
static int testDowncast1(Derived1 * d)
|
||||
{
|
||||
return d->x();
|
||||
}
|
||||
|
||||
static int testDowncast2(Derived2 * d)
|
||||
{
|
||||
return d->x();
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
/* */
|
||||
/* test classes for interaction of overloading, */
|
||||
/* base declarations, and callbacks */
|
||||
/* */
|
||||
/************************************************************/
|
||||
|
||||
struct CallbackTestBase
|
||||
{
|
||||
virtual int testCallback(int i) { return callback(i); }
|
||||
virtual int callback(int i) = 0;
|
||||
virtual ~CallbackTestBase() {}
|
||||
};
|
||||
|
||||
struct CallbackTest : public CallbackTestBase
|
||||
{
|
||||
virtual int callback(int i) { return i + 1; }
|
||||
virtual std::string callbackString(std::string const & i) { return i + " 1"; }
|
||||
};
|
||||
|
||||
struct CallbackTestCallback : public CallbackTest
|
||||
{
|
||||
CallbackTestCallback(PyObject* self)
|
||||
: m_self(self)
|
||||
{}
|
||||
|
||||
int callback(int x)
|
||||
{
|
||||
return py::Callback<int>::call_method(m_self, "callback", x);
|
||||
}
|
||||
std::string callbackString(std::string const & x)
|
||||
{
|
||||
return py::Callback<std::string>::call_method(m_self, "callback", x);
|
||||
}
|
||||
|
||||
static int default_callback(CallbackTest * self, int x)
|
||||
{
|
||||
return self->CallbackTest::callback(x);
|
||||
}
|
||||
static std::string default_callbackString(CallbackTest * self, std::string x)
|
||||
{
|
||||
return self->CallbackTest::callbackString(x);
|
||||
}
|
||||
|
||||
PyObject * m_self;
|
||||
};
|
||||
|
||||
int testCallback(CallbackTestBase * b, int i)
|
||||
{
|
||||
return b->testCallback(i);
|
||||
}
|
||||
|
||||
typedef boost::rational<int> Ratio;
|
||||
|
||||
py::String ratio_str(const Ratio& r)
|
||||
{
|
||||
char buf[200];
|
||||
|
||||
if (r.denominator() == 1)
|
||||
sprintf(buf, "%d", r.numerator());
|
||||
else
|
||||
sprintf(buf, "%d/%d", r.numerator(), r.denominator());
|
||||
|
||||
return py::String(buf);
|
||||
}
|
||||
|
||||
py::String ratio_repr(const Ratio& r)
|
||||
{
|
||||
char buf[200];
|
||||
sprintf(buf, "Rational(%d, %d)", r.numerator(), r.denominator());
|
||||
return py::String(buf);
|
||||
}
|
||||
|
||||
py::Tuple ratio_coerce(const Ratio& r1, int r2)
|
||||
{
|
||||
return py::Tuple(r1, Ratio(r2));
|
||||
}
|
||||
|
||||
// The most reliable way, across compilers, to grab the particular abs function
|
||||
// we're interested in.
|
||||
Ratio ratio_abs(const Ratio& r)
|
||||
{
|
||||
return boost::abs(r);
|
||||
}
|
||||
|
||||
// An experiment, to be integrated into the py_cpp library at some point.
|
||||
template <class T>
|
||||
struct StandardOps
|
||||
{
|
||||
static T add(const T& x, const T& y) { return x + y; }
|
||||
static T sub(const T& x, const T& y) { return x - y; }
|
||||
static T mul(const T& x, const T& y) { return x * y; }
|
||||
static T div(const T& x, const T& y) { return x / y; }
|
||||
static T cmp(const T& x, const T& y) { return std::less<T>()(x, y) ? -1 : std::less<T>()(y, x) ? 1 : 0; }
|
||||
};
|
||||
|
||||
/************************************************************/
|
||||
/* */
|
||||
/* init the module */
|
||||
/* */
|
||||
/************************************************************/
|
||||
|
||||
void init_module(py::Module& m)
|
||||
{
|
||||
m.add(new Foo::PythonClass);
|
||||
@@ -309,6 +549,21 @@ void init_module(py::Module& m)
|
||||
m.def(first_string, "first_string");
|
||||
m.def(second_string, "second_string");
|
||||
|
||||
// This shows the wrapping of a 3rd-party numeric type.
|
||||
py::ClassWrapper<boost::rational<int> > rational(m, "Rational");
|
||||
rational.def(py::Constructor<int, int>());
|
||||
rational.def(py::Constructor<int>());
|
||||
rational.def(py::Constructor<>());
|
||||
rational.def(StandardOps<Ratio>::add, "__add__");
|
||||
rational.def(StandardOps<Ratio>::sub, "__sub__");
|
||||
rational.def(StandardOps<Ratio>::mul, "__mul__");
|
||||
rational.def(StandardOps<Ratio>::div, "__div__");
|
||||
rational.def(StandardOps<Ratio>::cmp, "__cmp__");
|
||||
rational.def(ratio_coerce, "__coerce__");
|
||||
rational.def(ratio_str, "__str__");
|
||||
rational.def(ratio_repr, "__repr__");
|
||||
rational.def(ratio_abs, "__abs__");
|
||||
|
||||
py::ClassWrapper<Range> range(m, "Range");
|
||||
range.def(py::Constructor<int>());
|
||||
range.def(py::Constructor<int, int>());
|
||||
@@ -321,6 +576,64 @@ void init_module(py::Module& m)
|
||||
range.def(&range_hash, "__hash__");
|
||||
range.def_readonly(&Range::m_start, "start");
|
||||
range.def_readonly(&Range::m_finish, "finish");
|
||||
|
||||
m.def(&testVoid, "overloaded");
|
||||
m.def(&testInt, "overloaded");
|
||||
m.def(&testString, "overloaded");
|
||||
m.def(&test2, "overloaded");
|
||||
m.def(&test3, "overloaded");
|
||||
m.def(&test4, "overloaded");
|
||||
m.def(&test5, "overloaded");
|
||||
|
||||
py::ClassWrapper<OverloadTest> over(m, "OverloadTest");
|
||||
over.def(py::Constructor<py::Void>());
|
||||
over.def(py::Constructor<OverloadTest const &>());
|
||||
over.def(py::Constructor<int>());
|
||||
over.def(py::Constructor<int, int>());
|
||||
over.def(py::Constructor<int, int, int>());
|
||||
over.def(py::Constructor<int, int, int, int>());
|
||||
over.def(py::Constructor<int, int, int, int, int>());
|
||||
over.def(&getX, "getX");
|
||||
over.def(&OverloadTest::setX, "setX");
|
||||
over.def(&OverloadTest::x, "overloaded");
|
||||
over.def(&OverloadTest::p1, "overloaded");
|
||||
over.def(&OverloadTest::p2, "overloaded");
|
||||
over.def(&OverloadTest::p3, "overloaded");
|
||||
over.def(&OverloadTest::p4, "overloaded");
|
||||
over.def(&OverloadTest::p5, "overloaded");
|
||||
|
||||
py::ClassWrapper<Base> base(m, "Base");
|
||||
base.def(&Base::x, "x");
|
||||
|
||||
py::ClassWrapper<Derived1> derived1(m, "Derived1");
|
||||
// this enables conversions between Base and Derived1
|
||||
// and makes wrapped methods of Base available
|
||||
derived1.declare_base(base);
|
||||
derived1.def(py::Constructor<int>());
|
||||
|
||||
py::ClassWrapper<Derived2> derived2(m, "Derived2");
|
||||
// don't enable downcast from Base to Derived2
|
||||
derived2.declare_base(base, py::without_downcast);
|
||||
derived2.def(py::Constructor<int>());
|
||||
|
||||
m.def(&testUpcast, "testUpcast");
|
||||
m.def(&derived1Factory, "derived1Factory");
|
||||
m.def(&derived2Factory, "derived2Factory");
|
||||
m.def(&testDowncast1, "testDowncast1");
|
||||
m.def(&testDowncast2, "testDowncast2");
|
||||
|
||||
py::ClassWrapper<CallbackTestBase> callbackTestBase(m, "CallbackTestBase");
|
||||
callbackTestBase.def(&CallbackTestBase::testCallback, "testCallback");
|
||||
m.def(&testCallback, "testCallback");
|
||||
|
||||
py::ClassWrapper<CallbackTest, CallbackTestCallback> callbackTest(m, "CallbackTest");
|
||||
callbackTest.def(py::Constructor<py::Void>());
|
||||
callbackTest.def(&CallbackTest::callback, "callback",
|
||||
&CallbackTestCallback::default_callback);
|
||||
callbackTest.def(&CallbackTest::callbackString, "callback",
|
||||
&CallbackTestCallback::default_callbackString);
|
||||
|
||||
callbackTest.declare_base(callbackTestBase);
|
||||
}
|
||||
|
||||
void init_module()
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "extclass_demo.cpp"
|
||||
@@ -1,80 +0,0 @@
|
||||
// (C) Copyright David Abrahams 2000. Permission to copy, use, modify, sell and
|
||||
// distribute this software is granted provided this copyright notice appears
|
||||
// in all copies. This software is provided "as is" without express or implied
|
||||
// warranty, and with no claim as to its suitability for any purpose.
|
||||
//
|
||||
// The author gratefully acknowleges the support of Dragon Systems, Inc., in
|
||||
// producing this work.
|
||||
//
|
||||
// This file automatically generated for 5-argument constructors by
|
||||
// gen_extclass.py
|
||||
|
||||
#ifndef EXTCLASS_PYGEN_DWA070900_H_
|
||||
# define EXTCLASS_PYGEN_DWA070900_H_
|
||||
|
||||
|
||||
// A simple wrapper over a T which allows us to use ExtensionClass<T> with a
|
||||
// single template parameter only. See ExtensionClass<T>, above.
|
||||
template <class T>
|
||||
class HeldInstance : public T
|
||||
{
|
||||
// There are no member functions: we want to avoid inadvertently overriding
|
||||
// any virtual functions in T.
|
||||
public:
|
||||
HeldInstance(PyObject* p) : T(), m_self(p) {}
|
||||
template <class A1>
|
||||
HeldInstance(PyObject* p, const A1& a1) : T(a1), m_self(p) {}
|
||||
template <class A1, class A2>
|
||||
HeldInstance(PyObject* p, const A1& a1, const A2& a2) : T(a1, a2), m_self(p) {}
|
||||
template <class A1, class A2, class A3>
|
||||
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3) : T(a1, a2, a3), m_self(p) {}
|
||||
template <class A1, class A2, class A3, class A4>
|
||||
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4) : T(a1, a2, a3, a4), m_self(p) {}
|
||||
template <class A1, class A2, class A3, class A4, class A5>
|
||||
HeldInstance(PyObject* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) : T(a1, a2, a3, a4, a5), m_self(p) {}
|
||||
protected:
|
||||
PyObject* m_self; // Not really needed; doesn't really hurt.
|
||||
};
|
||||
|
||||
class InstanceHolderBase
|
||||
{
|
||||
public:
|
||||
virtual ~InstanceHolderBase() {}
|
||||
};
|
||||
|
||||
template <class Held>
|
||||
class InstanceHolder : public InstanceHolderBase
|
||||
{
|
||||
public:
|
||||
virtual Held *target() = 0;
|
||||
};
|
||||
|
||||
template <class Held, class Wrapper>
|
||||
class InstanceValueHolder : public InstanceHolder<Held>
|
||||
{
|
||||
public:
|
||||
Held* target() { return &m_held; }
|
||||
Wrapper* value_target() { return &m_held; }
|
||||
|
||||
InstanceValueHolder(ExtensionInstance* p) :
|
||||
m_held(p) {}
|
||||
template <class A1>
|
||||
InstanceValueHolder(ExtensionInstance* p, const A1& a1) :
|
||||
m_held(p, a1) {}
|
||||
template <class A1, class A2>
|
||||
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2) :
|
||||
m_held(p, a1, a2) {}
|
||||
template <class A1, class A2, class A3>
|
||||
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3) :
|
||||
m_held(p, a1, a2, a3) {}
|
||||
template <class A1, class A2, class A3, class A4>
|
||||
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4) :
|
||||
m_held(p, a1, a2, a3, a4) {}
|
||||
template <class A1, class A2, class A3, class A4, class A5>
|
||||
InstanceValueHolder(ExtensionInstance* p, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) :
|
||||
m_held(p, a1, a2, a3, a4, a5) {}
|
||||
private:
|
||||
Wrapper m_held;
|
||||
};
|
||||
|
||||
#endif
|
||||
73
extending.html
Normal file
73
extending.html
Normal file
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<title>
|
||||
A Brief Introduction to writing Python extension modules
|
||||
</title>
|
||||
<h1>
|
||||
<img src="c++boost.gif" alt="c++boost.gif (8819 bytes)" align="center"
|
||||
width="277" height="86">
|
||||
</h1>
|
||||
<h1>
|
||||
A Brief Introduction to writing Python extension modules
|
||||
</h1>
|
||||
<p>
|
||||
Interfacing any language to Python involves building a module which can
|
||||
be loaded by the Python interpreter, but which isn't written in Python.
|
||||
This is known as an <em>extension module</em>. Many of the <a href=
|
||||
"http://www.python.org/doc/current/lib/lib.html">built-in Python
|
||||
libraries</a> are constructed in 'C' this way; Python even supplies its
|
||||
<a href="http://www.python.org/doc/current/lib/types.html">fundamental
|
||||
types</a> using the same mechanism. An extension module can be statically
|
||||
linked with the Python interpreter, but it more commonly resides in a
|
||||
shared library or DLL.
|
||||
<p>
|
||||
As you can see from <a href=
|
||||
"http://www.python.org/doc/current/ext/ext.html"> The Python Extending
|
||||
and Embedding Tutorial</a>, writing an extension module normally means
|
||||
worrying about
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://www.python.org/doc/current/ext/refcounts.html">
|
||||
maintaining reference counts</a>
|
||||
<li>
|
||||
<a href="http://www.python.org/doc/current/ext/callingPython.html"> how
|
||||
to call back into Python</a>
|
||||
<li>
|
||||
<a href="http://www.python.org/doc/current/ext/parseTuple.html">
|
||||
function argument parsing and typechecking</a>
|
||||
</ul>
|
||||
This last item typically occupies a great deal of code in an extension
|
||||
module. Remember that Python is a completely dynamic language. A callable
|
||||
object receives its arguments in a tuple; it is up to that object to
|
||||
extract those arguments from the tuple, check their types, and raise
|
||||
appropriate exceptions. There are numerous other tedious details that need
|
||||
to be managed; too many to mention here. Py_cpp is designed to lift most of
|
||||
that burden.<br>
|
||||
<br>
|
||||
|
||||
<p>
|
||||
Another obstacle that most people run into eventually when extending
|
||||
Python is that there's no way to make a true Python class in an extension
|
||||
module. The typical solution is to create a new Python type in the
|
||||
extension module, and then write an additional module in 100% Python. The
|
||||
Python module defines a Python class which dispatches to an instance of
|
||||
the extension type, which it contains. This allows users to write
|
||||
subclasses of the class in the Python module, almost as though they were
|
||||
sublcassing the extension type. Aside from being tedious, it's not really
|
||||
the same as having a true class, because there's no way for the user to
|
||||
override a method of the extension type which is called from the
|
||||
extension module. Py_cpp solves this problem by taking advantage of <a
|
||||
href="http://www.python.org/doc/essays/metaclasses/">Python's metaclass
|
||||
feature</a> to provide objects which look, walk, and hiss almost exactly
|
||||
like regular Python classes. Py_cpp classes are actually cleaner than
|
||||
Python classes in some subtle ways; a more detailed discussion will
|
||||
follow (someday).</p>
|
||||
<p>Next: <a href="comparisons.html">Comparisons with Other Systems</a> Up: <a
|
||||
href="py_cpp.html">Top</a> </p>
|
||||
<p>
|
||||
© Copyright David Abrahams 2000. Permission to copy, use, modify,
|
||||
sell and distribute this document is granted provided this copyright
|
||||
notice appears in all copies. This document is provided "as is" without
|
||||
express or implied warranty, and with no claim as to its suitability for
|
||||
any purpose.</p>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "functions.cpp"
|
||||
12
gcc.mak
12
gcc.mak
@@ -11,7 +11,15 @@ LIBSRC = \
|
||||
LIBOBJ = $(LIBSRC:.cpp=.o)
|
||||
OBJ = $(LIBOBJ) extclass_demo.o
|
||||
|
||||
|
||||
ifeq "$(OS)" "Windows_NT"
|
||||
PYTHON_LIB=c:/tools/python/libs/python15.lib
|
||||
INC = -Ic:/cygnus/usr/include/g++-3 -Ic:/cygnus/usr/include -Ic:/boost -Ic:/tools/python/include
|
||||
MODULE_EXTENSION=dll
|
||||
else
|
||||
INC = -I/home/koethe/include -I/home/koethe/C++/boost -I/home/koethe/python/include/python1.5
|
||||
MODULE_EXTENSION=so
|
||||
endif
|
||||
|
||||
%.o: %.cpp
|
||||
g++ -fPIC $(INC) -c $*.cpp
|
||||
@@ -23,11 +31,11 @@ INC = -I/home/koethe/include -I/home/koethe/C++/boost -I/home/koethe/python/incl
|
||||
[ -s $@ ] || rm -f $@
|
||||
|
||||
demo: extclass_demo.o libpycpp.a
|
||||
g++ -shared -o demomodule.so extclass_demo.o -L. -lpycpp
|
||||
g++ -shared -o demomodule.$(MODULE_EXTENSION) $(PYTHON_LIB) extclass_demo.o -L. -lpycpp
|
||||
python test_extclass.py
|
||||
|
||||
clean:
|
||||
rm -rf *.o *.so *.a *.d *.pyc *.bak a.out
|
||||
rm -rf *.o *.$(MODULE_EXTENSION) *.a *.d *.pyc *.bak a.out
|
||||
|
||||
libpycpp.a: $(LIBOBJ)
|
||||
rm -f libpycpp.a
|
||||
|
||||
@@ -11,7 +11,7 @@ def gen_all(args):
|
||||
open('init_function.h', 'w').write(gen_init_function(args))
|
||||
open('signatures.h', 'w').write(gen_signatures(args))
|
||||
open('singleton.h', 'w').write(gen_singleton(args))
|
||||
open('extclass_pygen.h', 'w').write(gen_extclass(args))
|
||||
open('extclass.h', 'w').write(gen_extclass(args))
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
427
gen_extclass.py
427
gen_extclass.py
@@ -23,10 +23,352 @@ def gen_extclass(args):
|
||||
// This file automatically generated for %d-argument constructors by
|
||||
// gen_extclass.py
|
||||
|
||||
#ifndef EXTCLASS_PYGEN_DWA070900_H_
|
||||
# define EXTCLASS_PYGEN_DWA070900_H_
|
||||
#ifndef EXTENSION_CLASS_DWA052000_H_
|
||||
# define EXTENSION_CLASS_DWA052000_H_
|
||||
|
||||
# include "pyconfig.h"
|
||||
# include "subclass.h"
|
||||
# include <vector>
|
||||
# include "none.h"
|
||||
# include "objects.h"
|
||||
# include "functions.h"
|
||||
# include <memory>
|
||||
# include "init_function.h"
|
||||
# include <typeinfo>
|
||||
# include <boost/smart_ptr.hpp>
|
||||
|
||||
namespace py {
|
||||
|
||||
// forward declarations
|
||||
class ExtensionInstance;
|
||||
template <class T> class InstanceHolder;
|
||||
template <class T, class U> class InstanceValueHolder;
|
||||
template <class Ptr, class T> class InstancePtrHolder;
|
||||
|
||||
MetaClass<ExtensionInstance>* extension_meta_class();
|
||||
ExtensionInstance* get_extension_instance(PyObject* p);
|
||||
void report_missing_instance_data(ExtensionInstance*, Class<ExtensionInstance>*, const std::type_info&);
|
||||
void report_missing_ptr_data(ExtensionInstance*, Class<ExtensionInstance>*, const std::type_info&);
|
||||
void report_missing_class_object(const std::type_info&);
|
||||
void report_released_smart_pointer(const std::type_info&);
|
||||
|
||||
template <class T, class U>
|
||||
struct ExtensionClassFromPython
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
T* check_non_null(T* p)
|
||||
{
|
||||
if (p == 0)
|
||||
report_released_smart_pointer(typeid(T));
|
||||
return p;
|
||||
}
|
||||
|
||||
template <class T> class HeldInstance;
|
||||
|
||||
class ExtensionClassBase : public Class<ExtensionInstance>
|
||||
{
|
||||
public:
|
||||
ExtensionClassBase(const char* name);
|
||||
protected:
|
||||
void add_method(PyPtr<Function> method, const char* name);
|
||||
void add_default_method(PyPtr<Function> method, const char* name);
|
||||
void add_method(Function* method, const char* name);
|
||||
void add_default_method(Function* method, const char* name);
|
||||
|
||||
void add_constructor_object(Function*);
|
||||
void add_setter_method(Function*, const char* name);
|
||||
void add_getter_method(Function*, const char* name);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class ClassRegistry
|
||||
{
|
||||
public:
|
||||
static Class<ExtensionInstance>* class_object()
|
||||
{ return static_class_object; }
|
||||
static void register_class(py::Class<py::ExtensionInstance>*);
|
||||
static void unregister_class(py::Class<py::ExtensionInstance>*);
|
||||
private:
|
||||
static py::Class<py::ExtensionInstance>* static_class_object;
|
||||
};
|
||||
|
||||
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back to global namespace for this GCC bug
|
||||
}
|
||||
#endif
|
||||
|
||||
// This class' only job is to define from_python and to_python converters for T
|
||||
// and U. T is the class the user really intends to wrap. U is a class derived
|
||||
// from T with some virtual function overriding boilerplate, or if there are no
|
||||
// virtual functions, U = HeldInstance<T>.
|
||||
template <class T, class U = py::HeldInstance<T> >
|
||||
class PyExtensionClassConverters
|
||||
{
|
||||
public:
|
||||
#ifdef BOOST_MSVC
|
||||
// Convert return values of type T to python objects. What happens if T is
|
||||
// not copyable? Apparently there is no problem with g++ or MSVC unless this
|
||||
// is actually used. With a conforming compiler we will have a problem.
|
||||
friend PyObject* to_python(const T& x)
|
||||
{
|
||||
py::PyPtr<py::ExtensionInstance> result(create_instance(false));
|
||||
result->add_implementation(
|
||||
std::auto_ptr<py::InstanceHolderBase>(
|
||||
new py::InstanceValueHolder<T,U>(result.get(), x)));
|
||||
return result.release();
|
||||
}
|
||||
#else
|
||||
friend py::Type<U> py_holder_type(const T&)
|
||||
{ return py::Type<U>(); }
|
||||
#endif
|
||||
|
||||
PyExtensionClassConverters() {}
|
||||
|
||||
// Convert to T*
|
||||
friend T* from_python(PyObject* obj, py::Type<T*>)
|
||||
{
|
||||
// Downcast to an ExtensionInstance, then find the actual T
|
||||
py::ExtensionInstance* self = py::get_extension_instance(obj);
|
||||
typedef std::vector<py::InstanceHolderBase*>::const_iterator Iterator;
|
||||
for (Iterator p = self->wrapped_objects().begin();
|
||||
p != self->wrapped_objects().end(); ++p)
|
||||
{
|
||||
py::InstanceHolder<T>* held = dynamic_cast<py::InstanceHolder<T>*>(*p);
|
||||
if (held != 0)
|
||||
return held->target();
|
||||
}
|
||||
py::report_missing_instance_data(self, py::ClassRegistry<T>::class_object(), typeid(T));
|
||||
throw py::ArgumentError();
|
||||
}
|
||||
|
||||
// Convert to PtrType, where PtrType can be dereferenced to obtain a T.
|
||||
template <class PtrType>
|
||||
static PtrType& ptr_from_python(PyObject* obj, py::Type<PtrType>)
|
||||
{
|
||||
// Downcast to an ExtensionInstance, then find the actual T
|
||||
py::ExtensionInstance* self = py::get_extension_instance(obj);
|
||||
typedef std::vector<py::InstanceHolderBase*>::const_iterator Iterator;
|
||||
for (Iterator p = self->wrapped_objects().begin();
|
||||
p != self->wrapped_objects().end(); ++p)
|
||||
{
|
||||
py::InstancePtrHolder<PtrType, T>* held =
|
||||
dynamic_cast<py::InstancePtrHolder<PtrType, T>*>(*p);
|
||||
if (held != 0)
|
||||
return held->ptr();
|
||||
}
|
||||
py::report_missing_ptr_data(self, py::ClassRegistry<T>::class_object(), typeid(T));
|
||||
throw py::ArgumentError();
|
||||
}
|
||||
|
||||
template <class PtrType>
|
||||
static PyObject* ptr_to_python(PtrType x)
|
||||
{
|
||||
py::PyPtr<py::ExtensionInstance> result(create_instance(true));
|
||||
result->add_implementation(
|
||||
std::auto_ptr<py::InstanceHolderBase>(
|
||||
new py::InstancePtrHolder<PtrType,T>(x)));
|
||||
return result.release();
|
||||
}
|
||||
|
||||
static py::PyPtr<py::ExtensionInstance> create_instance(bool seek_base)
|
||||
{
|
||||
if (py::ClassRegistry<T>::class_object() == 0)
|
||||
py::report_missing_class_object(typeid(T));
|
||||
|
||||
py::Class<py::ExtensionInstance>* class_
|
||||
= seek_base && py::ClassRegistry<T>::class_object()->bases().size() > 0
|
||||
? py::Downcast<py::Class<py::ExtensionInstance> >(
|
||||
py::ClassRegistry<T>::class_object()->bases()[0].get()).get()
|
||||
: py::ClassRegistry<T>::class_object();
|
||||
|
||||
return py::PyPtr<py::ExtensionInstance>(new py::ExtensionInstance(class_));
|
||||
}
|
||||
|
||||
|
||||
// Convert to const T*
|
||||
friend const T* from_python(PyObject* p, py::Type<const T*>)
|
||||
{ return from_python(p, py::Type<T*>()); }
|
||||
|
||||
// Convert to T&
|
||||
friend T& from_python(PyObject* p, py::Type<T&>)
|
||||
{ return *py::check_non_null(from_python(p, py::Type<T*>())); }
|
||||
|
||||
// Convert to const T&
|
||||
friend const T& from_python(PyObject* p, py::Type<const T&>)
|
||||
{ return from_python(p, py::Type<T&>()); }
|
||||
|
||||
// Convert to T
|
||||
friend const T& from_python(PyObject* p, py::Type<T>)
|
||||
{ return from_python(p, py::Type<T&>()); }
|
||||
|
||||
friend std::auto_ptr<T>& from_python(PyObject* p, py::Type<std::auto_ptr<T>&>)
|
||||
{ return ptr_from_python(p, py::Type<std::auto_ptr<T> >()); }
|
||||
|
||||
friend std::auto_ptr<T>& from_python(PyObject* p, py::Type<std::auto_ptr<T> >)
|
||||
{ return ptr_from_python(p, py::Type<std::auto_ptr<T> >()); }
|
||||
|
||||
friend const std::auto_ptr<T>& from_python(PyObject* p, py::Type<const std::auto_ptr<T>&>)
|
||||
{ return ptr_from_python(p, py::Type<std::auto_ptr<T> >()); }
|
||||
|
||||
friend PyObject* to_python(std::auto_ptr<T> x)
|
||||
{ return ptr_to_python(x); }
|
||||
|
||||
friend boost::shared_ptr<T>& from_python(PyObject* p, py::Type<boost::shared_ptr<T>&>)
|
||||
{ return ptr_from_python(p, py::Type<boost::shared_ptr<T> >()); }
|
||||
|
||||
friend boost::shared_ptr<T>& from_python(PyObject* p, py::Type<boost::shared_ptr<T> >)
|
||||
{ return ptr_from_python(p, py::Type<boost::shared_ptr<T> >()); }
|
||||
|
||||
friend const boost::shared_ptr<T>& from_python(PyObject* p, py::Type<const boost::shared_ptr<T>&>)
|
||||
{ return ptr_from_python(p, py::Type<boost::shared_ptr<T> >()); }
|
||||
|
||||
friend PyObject* to_python(boost::shared_ptr<T> x)
|
||||
{ return ptr_to_python(x); }
|
||||
};
|
||||
|
||||
#ifndef BOOST_MSVC
|
||||
template <class T, class U>
|
||||
py::InstanceHolderBase*
|
||||
py_copy_to_new_value_holder(py::ExtensionInstance* p, const T& x, py::Type<U>)
|
||||
{
|
||||
return new py::InstanceValueHolder<T,U>(p, x);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* to_python(const T& x)
|
||||
{
|
||||
py::PyPtr<py::ExtensionInstance> result(
|
||||
PyExtensionClassConverters<T>::create_instance(false));
|
||||
result->add_implementation(
|
||||
std::auto_ptr<py::InstanceHolderBase>(
|
||||
py_copy_to_new_value_holder(result.get(), x, py_holder_type(x))));
|
||||
return result.release();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back from global namespace for this GCC bug
|
||||
namespace py {
|
||||
using ::PyExtensionClassConverters;
|
||||
#endif
|
||||
|
||||
template <class T> class InstanceHolder;
|
||||
|
||||
class ReadOnlySetattrFunction : public Function
|
||||
{
|
||||
public:
|
||||
ReadOnlySetattrFunction(const char* name);
|
||||
PyObject* do_call(PyObject* args, PyObject* keywords) const;
|
||||
const char* description() const;
|
||||
private:
|
||||
String m_name;
|
||||
};
|
||||
|
||||
// An easy way to make an extension base class which wraps T. Note that Python
|
||||
// subclasses of this class will simply be Class<ExtensionInstance> objects.
|
||||
//
|
||||
// U should be a class derived from T which overrides virtual functions with
|
||||
// boilerplate code to call back into Python. See extclass_demo.h for examples.
|
||||
//
|
||||
// U is optional, but you won't be able to override any member functions in
|
||||
// Python which are called from C++ if you don't supply it. If you just want to
|
||||
// be able to use T in python without overriding member functions, you can omit
|
||||
// U.
|
||||
template <class T, class U = HeldInstance<T> >
|
||||
class ExtensionClass
|
||||
: public PyExtensionClassConverters<T, U>, // This generates the to_python/from_python functions
|
||||
public ExtensionClassBase
|
||||
{
|
||||
public:
|
||||
typedef T WrappedType;
|
||||
typedef U CallbackType;
|
||||
|
||||
// Construct with a name that comes from typeid(T).name(). The name only
|
||||
// affects the objects of this class are represented through repr()
|
||||
ExtensionClass();
|
||||
|
||||
// Construct with the given name. The name only affects the objects of this
|
||||
// class are represented through repr()
|
||||
ExtensionClass(const char* name);
|
||||
|
||||
~ExtensionClass();
|
||||
|
||||
// define constructors
|
||||
""" % args
|
||||
+ gen_function(
|
||||
""" template <%(class A%n%:, %)>
|
||||
void def(Constructor<%(A%n%:, %)>)
|
||||
// The following incantation builds a Signature1, Signature2,... object. It
|
||||
// should _all_ get optimized away.
|
||||
{ add_constructor(
|
||||
%(prepend(Type<A%n>::Id(),
|
||||
%) Signature0()%()%));
|
||||
}
|
||||
""", args)
|
||||
+
|
||||
"""
|
||||
// define member functions. In fact this works for free functions, too -
|
||||
// they act like static member functions, or if they start with the
|
||||
// appropriate self argument (as a pointer), they can be used just like
|
||||
// ordinary member functions -- just like Python!
|
||||
template <class Fn>
|
||||
void def(Fn fn, const char* name)
|
||||
{
|
||||
this->add_method(new_wrapped_function(fn), name);
|
||||
}
|
||||
|
||||
// Define a virtual member function with a default implementation.
|
||||
// default_fn should be a function which provides the default implementation.
|
||||
// Be careful that default_fn does not in fact call fn virtually!
|
||||
template <class Fn, class DefaultFn>
|
||||
void def(Fn fn, const char* name, DefaultFn default_fn)
|
||||
{
|
||||
this->add_default_method(new_wrapped_function(default_fn), name);
|
||||
this->add_method(new_wrapped_function(fn), name);
|
||||
}
|
||||
|
||||
// Provide a function which implements x.<name>, reading from the given
|
||||
// member (pm) of the T instance
|
||||
template <class MemberType>
|
||||
void def_getter(MemberType T::*pm, const char* name)
|
||||
{
|
||||
this->add_getter_method(new GetterFunction<T, MemberType>(pm), name);
|
||||
}
|
||||
|
||||
// Provide a function which implements assignment to x.<name>, writing to
|
||||
// the given member (pm) of the T instance
|
||||
template <class MemberType>
|
||||
void def_setter(MemberType T::*pm, const char* name)
|
||||
{
|
||||
this->add_setter_method(new SetterFunction<T, MemberType>(pm), name);
|
||||
}
|
||||
|
||||
// Expose the given member (pm) of the T instance as a read-only attribute
|
||||
template <class MemberType>
|
||||
void def_readonly(MemberType T::*pm, const char* name)
|
||||
{
|
||||
this->add_setter_method(new ReadOnlySetattrFunction(name), name);
|
||||
this->def_getter(pm, name);
|
||||
}
|
||||
|
||||
// Expose the given member (pm) of the T instance as a read/write attribute
|
||||
template <class MemberType>
|
||||
void def_read_write(MemberType T::*pm, const char* name)
|
||||
{
|
||||
this->def_getter(pm, name);
|
||||
this->def_setter(pm, name);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef InstanceValueHolder<T,U> Holder;
|
||||
|
||||
template <class Signature>
|
||||
void add_constructor(Signature sig)
|
||||
{
|
||||
this->add_constructor_object(InitFunction<Holder>::create(sig));
|
||||
}
|
||||
};
|
||||
|
||||
// A simple wrapper over a T which allows us to use ExtensionClass<T> with a
|
||||
// single template parameter only. See ExtensionClass<T>, above.
|
||||
template <class T>
|
||||
@@ -34,7 +376,7 @@ class HeldInstance : public T
|
||||
{
|
||||
// There are no member functions: we want to avoid inadvertently overriding
|
||||
// any virtual functions in T.
|
||||
public:""" % args
|
||||
public:"""
|
||||
+ gen_functions(held_instance, args)
|
||||
+ """
|
||||
protected:
|
||||
@@ -66,8 +408,85 @@ public:
|
||||
private:
|
||||
Wrapper m_held;
|
||||
};
|
||||
""" +
|
||||
"""
|
||||
template <class PtrType, class HeldType>
|
||||
class InstancePtrHolder : public InstanceHolder<HeldType>
|
||||
{
|
||||
public:
|
||||
HeldType* target() { return &*m_ptr; }
|
||||
PtrType& ptr() { return m_ptr; }
|
||||
|
||||
#endif
|
||||
InstancePtrHolder(PtrType ptr) : m_ptr(ptr) {}
|
||||
private:
|
||||
PtrType m_ptr;
|
||||
};
|
||||
|
||||
class ExtensionInstance : public Instance
|
||||
{
|
||||
public:
|
||||
ExtensionInstance(PyTypeObject* class_);
|
||||
~ExtensionInstance();
|
||||
|
||||
void add_implementation(std::auto_ptr<InstanceHolderBase> holder);
|
||||
|
||||
typedef std::vector<InstanceHolderBase*> WrappedObjects;
|
||||
const WrappedObjects& wrapped_objects() const
|
||||
{ return m_wrapped_objects; }
|
||||
private:
|
||||
WrappedObjects m_wrapped_objects;
|
||||
};
|
||||
|
||||
//
|
||||
// Template function implementations
|
||||
//
|
||||
|
||||
template <class T, class U>
|
||||
ExtensionClass<T, U>::ExtensionClass()
|
||||
: ExtensionClassBase(typeid(T).name())
|
||||
{
|
||||
ClassRegistry<T>::register_class(this);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
ExtensionClass<T, U>::ExtensionClass(const char* name)
|
||||
: ExtensionClassBase(name)
|
||||
{
|
||||
ClassRegistry<T>::register_class(this);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
ExtensionClass<T, U>::~ExtensionClass()
|
||||
{
|
||||
ClassRegistry<T>::unregister_class(this);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void ClassRegistry<T>::register_class(Class<ExtensionInstance>* p)
|
||||
{
|
||||
// You're not expected to create more than one of these!
|
||||
assert(static_class_object == 0);
|
||||
static_class_object = p;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void ClassRegistry<T>::unregister_class(Class<ExtensionInstance>* p)
|
||||
{
|
||||
// The user should be destroying the same object they created.
|
||||
assert(static_class_object == p);
|
||||
(void)p; // unused in shipping version
|
||||
static_class_object = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Static data member declaration.
|
||||
//
|
||||
template <class T>
|
||||
Class<py::ExtensionInstance>* ClassRegistry<T>::static_class_object;
|
||||
|
||||
} // namespace py
|
||||
|
||||
#endif // EXTENSION_CLASS_DWA052000_H_
|
||||
""")
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "init_function.cpp"
|
||||
48
makefile
48
makefile
@@ -1,48 +0,0 @@
|
||||
#########################################################
|
||||
#
|
||||
# makefile - Manhattan py_cpp makefile
|
||||
#
|
||||
# Author: David Abrahams
|
||||
# Date: 04-May-2000
|
||||
# Copyright (c) 2000 Dragon Systems, Inc.
|
||||
#
|
||||
# Revision history at bottom.
|
||||
#
|
||||
#########################################################
|
||||
|
||||
# Set up $(ROOT_DIR)
|
||||
include ../make/userdirs.mak
|
||||
include $(ROOT_DIR)/make/common.mak
|
||||
include $(ROOT_DIR)/python/pythonhelp.mak
|
||||
include $(ROOT_DIR)/utils/utilhelp.mak
|
||||
include $(ROOT_DIR)/py_cpp/py_cpphelp.mak
|
||||
|
||||
MAKEFILE_INCLUDES = $(PYTHON_INCLUDES)
|
||||
|
||||
# Look in this directory, then the output subdirectory (BIN_DIR) for modules to load without qualification.
|
||||
export PYTHONPATH := $(THISDIR)$(PYTHONPATH_SEP)$(THISDIR)/$(BIN_DIR)$(PYTHONPATH_SEP)$(PYTHONPATH)
|
||||
|
||||
# In order to get the automatic dependency generation working correctly, it
|
||||
# is necessary to list the source files here.
|
||||
SRC_FILES = extclass.cpp init_function.cpp subclass.cpp functions.cpp module.cpp newtypes.cpp py.cpp objects.cpp
|
||||
SRC_FILES += extclass_demo.cpp example1.cpp
|
||||
|
||||
SRC_FILES += extclass_d.cpp init_function_d.cpp subclass_d.cpp functions_d.cpp module_d.cpp newtypes_d.cpp py_d.cpp objects_d.cpp
|
||||
SRC_FILES += extclass_demo_d.cpp
|
||||
LIBS=$(PYTHON_LIB) $(PYTHON_D_LIB)
|
||||
|
||||
test : demo
|
||||
$(PYTHON_EXE) test_extclass.py $(ARGS)
|
||||
|
||||
test_d : demo_d
|
||||
$(DEBUGGER) $(PYTHON_D_EXE) test_extclass.py $(ARGS)
|
||||
|
||||
ifndef PYTHON_D_LIB
|
||||
PYTHON_D_LIB = $(SPACE_CHAR)
|
||||
endif
|
||||
|
||||
-include py_cpp.mk1
|
||||
-include py_cpp_d.mk1
|
||||
-include demo.mk1
|
||||
-include demo_d.mk1
|
||||
-include example1.mk1
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "module.cpp"
|
||||
536
newtypes.cpp
536
newtypes.cpp
@@ -86,21 +86,13 @@ static MethodStruct* enable_method(const MethodStruct* base, MemberPtr p, Fn f)
|
||||
return const_cast<MethodStruct*>(detail::UniquePodSet::instance().get(new_value));
|
||||
}
|
||||
|
||||
// TODO: is there a problem with calling convention here, or can I really pass a
|
||||
// pointer to a C++ linkage function as a C-linkage function pointer? The
|
||||
// compilers seem to swallow it, but is it legal? Symantec C++ for Mac didn't
|
||||
// behave this way, FWIW.
|
||||
// Using C++ linkage allows us to keep the virtual function members of
|
||||
// TypeObjectBase private and use friendship to get them called.
|
||||
namespace {
|
||||
|
||||
extern "C" {
|
||||
|
||||
static PyObject* do_instance_repr(PyObject* instance)
|
||||
PyObject* call(PyObject* instance, PyObject* (TypeObjectBase::*f)(PyObject*) const)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_repr(instance);
|
||||
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
@@ -109,12 +101,14 @@ static PyObject* do_instance_repr(PyObject* instance)
|
||||
}
|
||||
}
|
||||
|
||||
static int do_instance_compare(PyObject* instance, PyObject* other)
|
||||
// Naming this differently allows us to use it for functions returning long on
|
||||
// compilers without partial ordering
|
||||
template <class R>
|
||||
R int_call(PyObject* instance, R (TypeObjectBase::*f)(PyObject*) const)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_compare(instance, other);
|
||||
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
@@ -123,12 +117,18 @@ static int do_instance_compare(PyObject* instance, PyObject* other)
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject* do_instance_str(PyObject* instance)
|
||||
// Implemented in terms of int_call, above
|
||||
int call(PyObject* instance, int (TypeObjectBase::*f)(PyObject*) const)
|
||||
{
|
||||
return int_call(instance, f);
|
||||
}
|
||||
|
||||
template <class A1>
|
||||
PyObject* call(PyObject* instance, PyObject* (TypeObjectBase::*f)(PyObject*, A1) const, A1 a1)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_str(instance);
|
||||
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance, a1);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
@@ -137,12 +137,40 @@ static PyObject* do_instance_str(PyObject* instance)
|
||||
}
|
||||
}
|
||||
|
||||
static long do_instance_hash(PyObject* instance)
|
||||
template <class A1>
|
||||
int call(PyObject* instance, int (TypeObjectBase::*f)(PyObject*, A1) const, A1 a1)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_hash(instance);
|
||||
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance, a1);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
template <class A1, class A2>
|
||||
PyObject* call(PyObject* instance, PyObject* (TypeObjectBase::*f)(PyObject*, A1, A2) const, A1 a1, A2 a2)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance, a1, a2);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <class A1, class A2>
|
||||
int call(PyObject* instance, int (TypeObjectBase::*f)(PyObject*, A1, A2) const, A1 a1, A2 a2)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance, a1, a2);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
@@ -151,20 +179,70 @@ static long do_instance_hash(PyObject* instance)
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject* do_instance_call(PyObject* instance, PyObject* args, PyObject* keywords)
|
||||
template <class A1, class A2, class A3>
|
||||
int call(PyObject* instance, int (TypeObjectBase::*f)(PyObject*, A1, A2, A3) const, A1 a1, A2 a2, A3 a3)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_call(instance, args, keywords);
|
||||
return (static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance, a1, a2, a3);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int call_length_function(PyObject* instance, int (TypeObjectBase::*f)(PyObject*) const)
|
||||
{
|
||||
try
|
||||
{
|
||||
const int outcome =
|
||||
(static_cast<TypeObjectBase*>(instance->ob_type)->*f)(instance);
|
||||
|
||||
if (outcome < 0)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "__len__() should return >= 0");
|
||||
return -1;
|
||||
}
|
||||
return outcome;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
static PyObject* do_instance_repr(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_repr);
|
||||
}
|
||||
|
||||
static int do_instance_compare(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_compare, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_str(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_str);
|
||||
}
|
||||
|
||||
static long do_instance_hash(PyObject* instance)
|
||||
{
|
||||
return int_call(instance, &TypeObjectBase::instance_hash);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_call(PyObject* instance, PyObject* args, PyObject* keywords)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_call, args, keywords);
|
||||
}
|
||||
|
||||
static void do_instance_dealloc(PyObject* instance)
|
||||
{
|
||||
try
|
||||
@@ -181,88 +259,29 @@ static void do_instance_dealloc(PyObject* instance)
|
||||
|
||||
static PyObject* do_instance_getattr(PyObject* instance, char* name)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_getattr(instance, name);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return 0;
|
||||
}
|
||||
const char* name_ = name;
|
||||
return call(instance, &TypeObjectBase::instance_getattr, name_);
|
||||
}
|
||||
|
||||
static int do_instance_setattr(PyObject* instance, char* name, PyObject* value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_setattr(instance, name, value);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return -1;
|
||||
}
|
||||
const char* name_ = name;
|
||||
return call(instance, &TypeObjectBase::instance_setattr, name_, value);
|
||||
}
|
||||
|
||||
static int do_instance_mp_length(PyObject* instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
const int outcome =
|
||||
static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_mapping_length(instance);
|
||||
|
||||
if (outcome < 0)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "__len__() should return >= 0");
|
||||
return -1;
|
||||
}
|
||||
return outcome;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return -1;
|
||||
}
|
||||
return call_length_function(instance, &TypeObjectBase::instance_mapping_length);
|
||||
}
|
||||
|
||||
static int do_instance_sq_length(PyObject* instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
const int outcome =
|
||||
static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_sequence_length(instance);
|
||||
|
||||
if (outcome < 0)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "__len__() should return >= 0");
|
||||
return -1;
|
||||
}
|
||||
return outcome;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return -1;
|
||||
}
|
||||
return call_length_function(instance, &TypeObjectBase::instance_sequence_length);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_mp_subscript(PyObject* instance, PyObject* index)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_mapping_subscript(instance, index);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return 0;
|
||||
}
|
||||
return call(instance, &TypeObjectBase::instance_mapping_subscript, index);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_sq_item(PyObject* instance, int index)
|
||||
@@ -293,88 +312,149 @@ static PyObject* do_instance_sq_item(PyObject* instance, int index)
|
||||
|
||||
static int do_instance_mp_ass_subscript(PyObject* instance, PyObject* index, PyObject* value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_mapping_ass_subscript(instance, index, value);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return -1;
|
||||
}
|
||||
return call(instance, &TypeObjectBase::instance_mapping_ass_subscript, index, value);
|
||||
}
|
||||
|
||||
static int do_instance_sq_ass_item(PyObject* instance, int index, PyObject* value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_sequence_ass_item(instance, index, value);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return -1;
|
||||
}
|
||||
return call(instance, &TypeObjectBase::instance_sequence_ass_item, index, value);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_sq_concat(PyObject* instance, PyObject* other)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_sequence_concat(instance, other);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return 0;
|
||||
}
|
||||
return call(instance, &TypeObjectBase::instance_sequence_concat, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_sq_repeat(PyObject* instance, int n)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_sequence_repeat(instance, n);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return 0;
|
||||
}
|
||||
return call(instance, &TypeObjectBase::instance_sequence_repeat, n);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_sq_slice(
|
||||
PyObject* instance, int start, int finish)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_sequence_slice(instance, start, finish);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return 0;
|
||||
}
|
||||
return call(instance, &TypeObjectBase::instance_sequence_slice, start, finish);
|
||||
}
|
||||
|
||||
static int do_instance_sq_ass_slice(
|
||||
PyObject* instance, int start, int finish, PyObject* value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return static_cast<TypeObjectBase*>(instance->ob_type)
|
||||
->instance_sequence_ass_slice(instance, start, finish, value);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handle_exception();
|
||||
return -1;
|
||||
}
|
||||
return call(instance, &TypeObjectBase::instance_sequence_ass_slice, start, finish, value);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_add(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_add, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_subtract(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_subtract, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_multiply(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_multiply, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_divide(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_divide, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_remainder(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_remainder, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_divmod(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_divmod, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_power(PyObject* instance, PyObject* exponent, PyObject* modulus)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_power, exponent, modulus);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_negative(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_negative);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_positive(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_positive);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_absolute(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_absolute);
|
||||
}
|
||||
|
||||
static int do_instance_nb_nonzero(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_nonzero);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_invert(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_invert);
|
||||
}
|
||||
|
||||
|
||||
static PyObject* do_instance_nb_lshift(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_lshift, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_rshift(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_rshift, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_and(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_and, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_xor(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_xor, other);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_or(PyObject* instance, PyObject* other)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_or, other);
|
||||
}
|
||||
|
||||
static int do_instance_nb_coerce(PyObject**instance, PyObject**other)
|
||||
{
|
||||
return call(*instance, &TypeObjectBase::instance_number_coerce, instance, other);
|
||||
}
|
||||
static PyObject* do_instance_nb_int(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_int);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_long(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_long);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_float(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_float);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_oct(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_oct);
|
||||
}
|
||||
|
||||
static PyObject* do_instance_nb_hex(PyObject* instance)
|
||||
{
|
||||
return call(instance, &TypeObjectBase::instance_number_hex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -384,24 +464,25 @@ template <std::size_t> struct category_type;
|
||||
|
||||
#define DECLARE_CAPABILITY_TYPE(field, sub_structure) \
|
||||
template <> \
|
||||
struct category_type<(offsetof(PyTypeObject, tp_as_##field))> \
|
||||
struct category_type<(PY_OFFSETOF(PyTypeObject, tp_as_##field))> \
|
||||
{ \
|
||||
typedef sub_structure type; \
|
||||
}
|
||||
|
||||
#define CAPABILITY(field) \
|
||||
{ offsetof(PyTypeObject, tp_##field), 0, Dispatch(do_instance_##field), 0, -1 }
|
||||
{ PY_OFFSETOF(PyTypeObject, tp_##field), 0, Dispatch(do_instance_##field), 0, -1 }
|
||||
|
||||
#define CAPABILITY2(category, field) \
|
||||
{ offsetof(PyTypeObject, tp_as_##category), \
|
||||
offsetof(category_type<offsetof(PyTypeObject, tp_as_##category)>::type, field), \
|
||||
{ PY_OFFSETOF(PyTypeObject, tp_as_##category), \
|
||||
PY_OFFSETOF(category_type<PY_OFFSETOF(PyTypeObject, tp_as_##category)>::type, field), \
|
||||
Dispatch(do_instance_##field), \
|
||||
sizeof(category_type<offsetof(PyTypeObject, tp_as_##category)>::type), \
|
||||
offsetof(AllMethods, category) \
|
||||
sizeof(category_type<PY_OFFSETOF(PyTypeObject, tp_as_##category)>::type), \
|
||||
PY_OFFSETOF(AllMethods, category) \
|
||||
}
|
||||
|
||||
DECLARE_CAPABILITY_TYPE(mapping, PyMappingMethods);
|
||||
DECLARE_CAPABILITY_TYPE(sequence, PySequenceMethods);
|
||||
DECLARE_CAPABILITY_TYPE(number, PyNumberMethods);
|
||||
|
||||
const CapabilityEntry capabilities[] = {
|
||||
CAPABILITY(hash),
|
||||
@@ -422,7 +503,31 @@ const CapabilityEntry capabilities[] = {
|
||||
CAPABILITY2(sequence, sq_concat),
|
||||
CAPABILITY2(sequence, sq_repeat),
|
||||
CAPABILITY2(sequence, sq_slice),
|
||||
CAPABILITY2(sequence, sq_ass_slice)
|
||||
CAPABILITY2(sequence, sq_ass_slice),
|
||||
|
||||
CAPABILITY2(number, nb_add),
|
||||
CAPABILITY2(number, nb_subtract),
|
||||
CAPABILITY2(number, nb_multiply),
|
||||
CAPABILITY2(number, nb_divide),
|
||||
CAPABILITY2(number, nb_remainder),
|
||||
CAPABILITY2(number, nb_divmod),
|
||||
CAPABILITY2(number, nb_power),
|
||||
CAPABILITY2(number, nb_negative),
|
||||
CAPABILITY2(number, nb_positive),
|
||||
CAPABILITY2(number, nb_absolute),
|
||||
CAPABILITY2(number, nb_nonzero),
|
||||
CAPABILITY2(number, nb_invert),
|
||||
CAPABILITY2(number, nb_lshift),
|
||||
CAPABILITY2(number, nb_rshift),
|
||||
CAPABILITY2(number, nb_and),
|
||||
CAPABILITY2(number, nb_xor),
|
||||
CAPABILITY2(number, nb_or),
|
||||
CAPABILITY2(number, nb_coerce),
|
||||
CAPABILITY2(number, nb_int),
|
||||
CAPABILITY2(number, nb_long),
|
||||
CAPABILITY2(number, nb_float),
|
||||
CAPABILITY2(number, nb_oct),
|
||||
CAPABILITY2(number, nb_hex)
|
||||
};
|
||||
|
||||
const std::size_t num_capabilities = PY_ARRAY_LENGTH(capabilities);
|
||||
@@ -445,7 +550,7 @@ const CapabilityEntry capabilities[] = {
|
||||
if (c.substructure_size == 0)
|
||||
{
|
||||
if (src == 0 ||
|
||||
#if defined(__MWERKS__) && __MWERKS__ <= 0x4000
|
||||
#if defined(__MWERKS__) && __MWERKS__ <= 0x4000 || defined(__alpha) && defined(__osf__)
|
||||
((const Dispatch*)src)
|
||||
#else
|
||||
reinterpret_cast<const Dispatch*>(src)
|
||||
@@ -621,6 +726,121 @@ int TypeObjectBase::instance_sequence_ass_slice(PyObject*, int, int, PyObject*)
|
||||
return unimplemented("instance_sequence_ass_slice");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_add(PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_add");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_subtract(PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_subtract");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_multiply(PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_multiply");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_divide(PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_divide");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_remainder(PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_remainder");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_divmod(PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_divmod");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_power(PyObject*, PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_divmod");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_negative(PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_negative");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_positive(PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_positive");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_absolute(PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_absolute");
|
||||
}
|
||||
|
||||
int TypeObjectBase::instance_number_nonzero(PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_nonzero");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_invert(PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_invert");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_lshift(PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_lshift");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_rshift(PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_rshift");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_and(PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_and");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_xor(PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_xor");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_or(PyObject*, PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_or");
|
||||
}
|
||||
|
||||
int TypeObjectBase::instance_number_coerce(PyObject*, PyObject**, PyObject**) const
|
||||
{
|
||||
return unimplemented("instance_number_coerce");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_int(PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_int");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_long(PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_long");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_float(PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_float");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_oct(PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_oct");
|
||||
}
|
||||
|
||||
PyObject* TypeObjectBase::instance_number_hex(PyObject*) const
|
||||
{
|
||||
return unimplemented("instance_number_hex");
|
||||
}
|
||||
|
||||
TypeObjectBase::~TypeObjectBase()
|
||||
{
|
||||
}
|
||||
|
||||
37
newtypes.h
37
newtypes.h
@@ -30,6 +30,8 @@
|
||||
|
||||
namespace py {
|
||||
|
||||
class InstanceHolderBase;
|
||||
|
||||
class TypeObjectBase : public PythonType
|
||||
{
|
||||
public:
|
||||
@@ -42,7 +44,14 @@ class TypeObjectBase : public PythonType
|
||||
hash, call, str, getattr, setattr, compare, repr,
|
||||
mapping_length, mapping_subscript, mapping_ass_subscript,
|
||||
sequence_length, sequence_item, sequence_ass_item,
|
||||
sequence_concat, sequence_repeat, sequence_slice, sequence_ass_slice
|
||||
sequence_concat, sequence_repeat, sequence_slice, sequence_ass_slice,
|
||||
|
||||
number_add, number_subtract, number_multiply, number_divide,
|
||||
number_remainder, number_divmod, number_power, number_negative,
|
||||
number_positive, number_absolute, number_nonzero, number_invert,
|
||||
number_lshift, number_rshift, number_and, number_xor, number_or,
|
||||
number_coerce, number_int, number_long, number_float, number_oct,
|
||||
number_hex
|
||||
};
|
||||
|
||||
void enable(Capability);
|
||||
@@ -75,7 +84,31 @@ class TypeObjectBase : public PythonType
|
||||
virtual PyObject* instance_sequence_slice(PyObject* instance, int start, int finish) const;
|
||||
virtual int instance_sequence_ass_item(PyObject* instance, int n, PyObject* value) const;
|
||||
virtual int instance_sequence_ass_slice(PyObject* instance, int start, int finish, PyObject* value) const;
|
||||
|
||||
|
||||
public: // Callbacks for number methods
|
||||
virtual PyObject* instance_number_add(PyObject*, PyObject*) const;
|
||||
virtual PyObject* instance_number_subtract(PyObject*, PyObject*) const;
|
||||
virtual PyObject* instance_number_multiply(PyObject*, PyObject*) const;
|
||||
virtual PyObject* instance_number_divide(PyObject*, PyObject*) const;
|
||||
virtual PyObject* instance_number_remainder(PyObject*, PyObject*) const;
|
||||
virtual PyObject* instance_number_divmod(PyObject*, PyObject*) const;
|
||||
virtual PyObject* instance_number_power(PyObject*, PyObject*, PyObject*) const;
|
||||
virtual PyObject* instance_number_negative(PyObject*) const;
|
||||
virtual PyObject* instance_number_positive(PyObject*) const;
|
||||
virtual PyObject* instance_number_absolute(PyObject*) const;
|
||||
virtual int instance_number_nonzero(PyObject*) const;
|
||||
virtual PyObject* instance_number_invert(PyObject*) const;
|
||||
virtual PyObject* instance_number_lshift(PyObject*, PyObject*) const;
|
||||
virtual PyObject* instance_number_rshift(PyObject*, PyObject*) const;
|
||||
virtual PyObject* instance_number_and(PyObject*, PyObject*) const;
|
||||
virtual PyObject* instance_number_xor(PyObject*, PyObject*) const;
|
||||
virtual PyObject* instance_number_or(PyObject*, PyObject*) const;
|
||||
virtual int instance_number_coerce(PyObject*, PyObject**, PyObject**) const;
|
||||
virtual PyObject* instance_number_int(PyObject*) const;
|
||||
virtual PyObject* instance_number_long(PyObject*) const;
|
||||
virtual PyObject* instance_number_float(PyObject*) const;
|
||||
virtual PyObject* instance_number_oct(PyObject*) const;
|
||||
virtual PyObject* instance_number_hex(PyObject*) const;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
|
||||
26
objects.h
26
objects.h
@@ -13,6 +13,7 @@
|
||||
# include "pyconfig.h"
|
||||
# include "pyptr.h"
|
||||
# include "boost/operators.hpp"
|
||||
# include <utility>
|
||||
|
||||
namespace py {
|
||||
|
||||
@@ -36,8 +37,31 @@ class Tuple : public Object
|
||||
public:
|
||||
Tuple(std::size_t n = 0);
|
||||
explicit Tuple(Ptr p);
|
||||
|
||||
template <class First, class Second>
|
||||
Tuple(const std::pair<First,Second>& x)
|
||||
: Object(Ptr(PyTuple_New(2)))
|
||||
{
|
||||
set_item(0, Ptr(to_python(x.first)));
|
||||
set_item(1, Ptr(to_python(x.second)));
|
||||
}
|
||||
|
||||
Tuple(const Ptr* start, const Ptr* finish); // not yet implemented.
|
||||
template <class First, class Second>
|
||||
Tuple(const First& first, const Second& second)
|
||||
: Object(Ptr(PyTuple_New(2)))
|
||||
{
|
||||
set_item(0, Ptr(to_python(first)));
|
||||
set_item(1, Ptr(to_python(second)));
|
||||
}
|
||||
|
||||
template <class First, class Second, class Third>
|
||||
Tuple(const First& first, const Second& second, const Third& third)
|
||||
: Object(Ptr(PyTuple_New(3)))
|
||||
{
|
||||
set_item(0, Ptr(to_python(first)));
|
||||
set_item(1, Ptr(to_python(second)));
|
||||
set_item(2, Ptr(to_python(third)));
|
||||
}
|
||||
|
||||
static PyTypeObject* type_object();
|
||||
static bool accepts(Ptr p);
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "objects.cpp"
|
||||
139
overloading.html
Normal file
139
overloading.html
Normal file
@@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
|
||||
"http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<title>
|
||||
Function Overloading
|
||||
</title>
|
||||
<div>
|
||||
<h1>
|
||||
<img width="277" height="86" id="_x0000_i1025" align="center"
|
||||
src="c++boost.gif" alt= "c++boost.gif (8819 bytes)">Function Overloading
|
||||
</h1>
|
||||
|
||||
<h2>An Example</h2>
|
||||
<p>
|
||||
To expose overloaded functions in Python, simply <code>def()</code> each
|
||||
one with the same Python name:
|
||||
<blockquote>
|
||||
<pre>
|
||||
inline int f1() { return 3; }
|
||||
inline int f2(int x) { return x + 1; }
|
||||
|
||||
class X {
|
||||
public:
|
||||
X() : m_value(0) {}
|
||||
X(int n) : m_value(n) {}
|
||||
int value() const { return m_value; }
|
||||
void value(int v) { m_value = v; }
|
||||
private:
|
||||
int m_value;
|
||||
};
|
||||
...
|
||||
|
||||
void initoverload_demo()
|
||||
{
|
||||
try
|
||||
{
|
||||
py::Module overload_demo("overload_demo");
|
||||
// Overloaded functions at module scope
|
||||
overload_demo.def(f1, "f");
|
||||
overload_demo.def(f2, "f");
|
||||
|
||||
py::ClassWrapper<X> x_class(overload_demo, "X");
|
||||
// Overloaded constructors
|
||||
x_class.def(py::Constructor<>());
|
||||
x_class.def(py::Constructor<int>());
|
||||
|
||||
// Overloaded member functions
|
||||
x_class.def((int (X::*)() const)&X::value, "value");
|
||||
x_class.def((void (X::*)(int))&X::value, "value");
|
||||
...
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>
|
||||
Now in Python:
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> from overload_demo import *
|
||||
>>> x0 = X()
|
||||
>>> x1 = X(1)
|
||||
>>> x0.value()
|
||||
0
|
||||
>>> x1.value()
|
||||
1
|
||||
>>> x0.value(3)
|
||||
>>> x0.value()
|
||||
3
|
||||
>>> X('hello')
|
||||
TypeError: No overloaded functions match (X, string). Candidates are:
|
||||
void (*)()
|
||||
void (*)(int)
|
||||
>>> f()
|
||||
3
|
||||
>>> f(4)
|
||||
5
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<h2>Discussion</h2>
|
||||
<p>
|
||||
Notice that overloading in the Python module was produced three ways:<ol>
|
||||
<li>by combining the non-overloaded C++ functions <code>int f1()</code>
|
||||
and <code>int f2(int)</code> and exposing them as <code>f</code> in Python.
|
||||
<li>by exposing the overloaded constructors of <code>class X</code>
|
||||
<li>by exposing the overloaded member functions <code>X::value</code>.
|
||||
</ol>
|
||||
<p>
|
||||
Techniques 1. and 3. above are really alternatives. In case 3, you need
|
||||
to form a pointer to each of the overloaded functions. The casting
|
||||
syntax shown above is one way to do that in C++. Case 1 does not require
|
||||
complicated-looking casts, but may not be viable if you can't change
|
||||
your C++ interface. N.B. There's really nothing unsafe about casting an
|
||||
overloaded (member) function address this way: the compiler won't let
|
||||
you write it at all unless you get it right.
|
||||
|
||||
<h2>An Alternative to Casting</h2>
|
||||
<p>
|
||||
This approach is not neccessarily better, but may be preferable for some
|
||||
people who have trouble writing out the types of (member) function
|
||||
pointers or simply prefer to avoid all casts as a matter of principle:
|
||||
<blockquote>
|
||||
<pre>
|
||||
// Forwarding functions for X::value
|
||||
inline void set_x_value(X& self, int v) { self.value(v); }
|
||||
inline int get_x_value(X& self) { return self.value(); }
|
||||
...
|
||||
// Overloaded member functions
|
||||
x_class.def(set_x_value, "value");
|
||||
x_class.def(get_x_value, "value");
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>Here we are taking advantage of the ability to expose C++ functions at
|
||||
namespace scope as Python member functions.
|
||||
|
||||
<h2>Overload Resolution</h2>
|
||||
<p>
|
||||
The function overload resolution mechanism in py_cpp works as
|
||||
follows:<ul>
|
||||
<li>Attribute lookup for extension classes proceeds in the usual way. When a
|
||||
class is found which has a matching attribute, only functions overloaded
|
||||
in the context of that class are candidates for overload resolution.
|
||||
<li>Within a name-space context (extension class or module), overloaded
|
||||
functions are tried in the same order they were
|
||||
<code>def()</code>ed. The first function whose signature can be made to
|
||||
match each argument passed is the one which is ultimately called.
|
||||
</ul>
|
||||
<p>
|
||||
Prev: <a href="overloading.html">Function Overloading</a>
|
||||
Next: <a href="special.html">Special Method Names</a>
|
||||
Up: <a href="py_cpp.html">Top</a>
|
||||
<p>
|
||||
© Copyright David Abrahams 2000. Permission to copy, use, modify,
|
||||
sell and distribute this document is granted provided this copyright
|
||||
notice appears in all copies. This document is provided "as is" without
|
||||
express or implied warranty, and with no claim as to its suitability
|
||||
for any purpose.
|
||||
<p>
|
||||
Updated: Oct 15, 2000
|
||||
</div>
|
||||
|
||||
175
overriding.html
Normal file
175
overriding.html
Normal file
@@ -0,0 +1,175 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<title>
|
||||
Overridable Virtual Functions
|
||||
</title>
|
||||
<h1>
|
||||
<img src="c++boost.gif" alt="c++boost.gif (8819 bytes)" align="center"
|
||||
width="277" height="86">
|
||||
</h1>
|
||||
<h1>
|
||||
Overridable Virtual Functions
|
||||
</h1>
|
||||
<p>
|
||||
In the <a href="example1.html">previous example</a> we exposed a simple
|
||||
C++ class in Python and showed that we could write a subclass. We even
|
||||
redefined one of the functions in our derived class. Now we will learn
|
||||
how to make the function <em>behave virtually</em>. Of course, the first
|
||||
step if we want it to act like a virtual function when called from our
|
||||
C++ code, is to <em>make</em> it virtual:
|
||||
<blockquote>
|
||||
<pre>
|
||||
class world
|
||||
{
|
||||
...
|
||||
<strong>virtual</strong> const char* get() const { return "hi, world"; }
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Then we'll need a derived class<a href="#why_derived">*</a> to help us
|
||||
dispatch the call to Python:
|
||||
<blockquote>
|
||||
<pre>
|
||||
struct world_callback : hello::world
|
||||
{
|
||||
// The first argument must by a PyObject* (the corresponding Python object)
|
||||
// The rest of the argument list should match the base class constructor
|
||||
world_callback(PyObject* self, int x)
|
||||
: world(x), // dispatch to base object
|
||||
m_self(self) {} // hang onto the Python object
|
||||
|
||||
// Dispatch the call to Python
|
||||
const char* get() const
|
||||
{
|
||||
// Any function arguments would go at the end of the argument list
|
||||
// The return type is a template parameter
|
||||
return py::Callback<const char*>::call_method(m_self, "get");
|
||||
}
|
||||
|
||||
// <a name=
|
||||
"default_implementation">Something Python can call</a> in case there is no override of get()
|
||||
const char* default_get() const
|
||||
{ return this->hello::world::get(); }
|
||||
private:
|
||||
PyObject* m_self; // A way to hold onto the Python object
|
||||
};
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Finally, we add <code>world_callback</code> to the <code>
|
||||
ClassWrapper<></code> declaration in our module initialization
|
||||
function:
|
||||
<blockquote>
|
||||
<pre>
|
||||
// Create the <a name=
|
||||
"world_class">Python type object</a> for our extension class
|
||||
py::ClassWrapper<hello::world<strong>,world_callback></strong> world_class(hello, "world");
|
||||
...
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
...and when we define the function, we must tell py_cpp about the default
|
||||
implementation:
|
||||
<blockquote>
|
||||
<pre>
|
||||
// Add a virtual member function
|
||||
world_class.def(&hello::world::get, "get", &world_callback::default_get);
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Now our subclass of <code>hello.world</code> behaves as expected:
|
||||
<p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> class my_subclass(hello.world):
|
||||
... def get(self):
|
||||
... return 'hello, world'
|
||||
...
|
||||
>>> hello.length(my_subclass())
|
||||
12
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
<a name="why_derived">*</a>You may ask, "Why do we need this derived
|
||||
class? This could have been designed so that everything gets done right
|
||||
inside of <code>hello::world</code>." One of the goals of py_cpp is to be
|
||||
minimally intrusive on an existing C++ design. In principle, it should be
|
||||
possible to expose the interface for a 3rd party library without changing
|
||||
it.
|
||||
<h2>
|
||||
Pure Virtual Functions
|
||||
</h2>
|
||||
<p>
|
||||
A pure virtual function with no implementation is actually a lot easier
|
||||
to deal with than a virtual function with a default implementation. First
|
||||
of all, you obviously don't need to <a href="#default_implementation">
|
||||
supply a default implementation</a>. Secondly, you don't need to call
|
||||
<code>def()</code> on the <code>ExtensionClass<></code> instance
|
||||
for the virtual function. In fact, you wouldn't <em>want</em> to: if the
|
||||
corresponding attribute on the Python class stays undefined, you'll get
|
||||
an <code>AttributeError</code> in Python when you try to call the
|
||||
function, indicating that it should have been implemented. For example:
|
||||
<blockquote>
|
||||
<pre>
|
||||
struct baz
|
||||
{
|
||||
<strong>virtual</strong> void pure(int) = 0;
|
||||
};
|
||||
|
||||
struct baz_callback
|
||||
{
|
||||
void pure(int x) { py::Callback<void>::call_method(m_self, "pure", x); }
|
||||
};
|
||||
|
||||
extern "C"
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
initfoobar()
|
||||
{
|
||||
try
|
||||
{
|
||||
py::Module foobar("foobar");
|
||||
py::ClassWrapper<baz,baz_callback> baz_class("baz");
|
||||
baz_class.def(&baz::pure, "pure");
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
py::handle_exception(); // Deal with the exception for Python
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Now in Python:
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> from foobar import baz
|
||||
>>> x = baz()
|
||||
>>> x.pure()
|
||||
Traceback (innermost last):
|
||||
File "<stdin>", line 1, in ?
|
||||
AttributeError: pure
|
||||
>>> class mumble(baz):
|
||||
... def pure(self, z): pass
|
||||
...
|
||||
>>> y = mumble()
|
||||
>>> y.pure()
|
||||
>>>
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Prev: <a href="example1.html">A Simple Example Using py_cpp</a> Next: <a
|
||||
href="under-the-hood.html">A Peek Under the Hood</a> Up: <a href=
|
||||
"py_cpp.html">Top</a>
|
||||
<p>
|
||||
© Copyright David Abrahams 2000. Permission to copy, use, modify,
|
||||
sell and distribute this document is granted provided this copyright
|
||||
notice appears in all copies. This document is provided "as is" without
|
||||
express or implied warranty, and with no claim as to its suitability for
|
||||
any purpose.
|
||||
<p>
|
||||
Updated: Sept 30, 2000
|
||||
|
||||
23
py.cpp
23
py.cpp
@@ -7,7 +7,11 @@
|
||||
// producing this work.
|
||||
|
||||
#include "py.h"
|
||||
#include <boost/cast.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <typeinfo>
|
||||
#ifndef BOOST_NO_LIMITS
|
||||
# include <boost/cast.hpp>
|
||||
#endif
|
||||
|
||||
namespace py {
|
||||
|
||||
@@ -82,11 +86,19 @@ T integer_from_python(PyObject* p, py::Type<T>)
|
||||
{
|
||||
const long long_result = from_python(p, py::Type<long>());
|
||||
|
||||
#ifndef BOOST_NO_LIMITS
|
||||
try
|
||||
{
|
||||
return boost::numeric_cast<T>(long_result);
|
||||
}
|
||||
catch(const boost::bad_numeric_cast&)
|
||||
#else
|
||||
if (static_cast<T>(long_result) == long_result)
|
||||
{
|
||||
return static_cast<T>(long_result);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
char buffer[256];
|
||||
const char message[] = "%ld out of range for %s";
|
||||
@@ -103,12 +115,17 @@ template <class T>
|
||||
PyObject* integer_to_python(T value)
|
||||
{
|
||||
long value_as_long;
|
||||
|
||||
|
||||
#ifndef BOOST_NO_LIMITS
|
||||
try
|
||||
{
|
||||
value_as_long = boost::numeric_cast<T>(value);
|
||||
value_as_long = boost::numeric_cast<long>(value);
|
||||
}
|
||||
catch(const boost::bad_numeric_cast&)
|
||||
#else
|
||||
value_as_long = static_cast<long>(value);
|
||||
if (value_as_long != value)
|
||||
#endif
|
||||
{
|
||||
const char message[] = "value out of range for Python int";
|
||||
PyErr_SetString(PyExc_ValueError, message);
|
||||
|
||||
5
py.h
5
py.h
@@ -165,6 +165,11 @@ PyObject* to_python(boost::shared_ptr<T> p)
|
||||
// inline implementations
|
||||
//
|
||||
|
||||
inline PyObject* to_python(double d)
|
||||
{
|
||||
return PyFloat_FromDouble(d);
|
||||
}
|
||||
|
||||
inline PyObject* to_python(long l)
|
||||
{
|
||||
return PyInt_FromLong(l);
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
TargetName=py_cpp
|
||||
TargetType=lib
|
||||
SourceFiles=extclass.cpp init_function.cpp subclass.cpp functions.cpp objects.cpp
|
||||
SourceFiles+=module.cpp newtypes.cpp py.cpp
|
||||
122
py_cpp.html
Normal file
122
py_cpp.html
Normal file
@@ -0,0 +1,122 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
|
||||
"http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<title>
|
||||
py_cpp Python/C++ binding documentation
|
||||
</title>
|
||||
<h1>
|
||||
<img src="c++boost.gif" alt="c++boost.gif (8819 bytes)" width="277"
|
||||
align="center" height="86"> py_cpp<a href="#naming_contest">*</a>
|
||||
</h1>
|
||||
<p>
|
||||
Py_cpp is a system for quickly and easily interfacing C++ code with <a
|
||||
href="http:www.python.org">Python</a> such that the Python interface is
|
||||
very similar to the C++ interface. It is designed to be minimally
|
||||
intrusive on your C++ design. In most cases, you should not have to alter
|
||||
your C++ classes in any way in order to use them with py_cpp. The system
|
||||
<em>should</em> simply "reflect" your C++ classes and functions into
|
||||
Python.
|
||||
<p>
|
||||
The source code for py_cpp, including a MSVC demo project is available
|
||||
<a href="py_cpp_20001019.zip">here</a>. It has been tested against Python
|
||||
1.5.2 with GCC 2.95.2, Metrowerks CodeWarrior Pro6 and with Microsoft
|
||||
Visual C++ 6 sp4 using both the <a href="http://www.stlport.org">
|
||||
STLport</a> standard library implementation <em>and</em> the library
|
||||
implementation which ships with the compiler. It has also been tested
|
||||
against Python 2.0c1 with MSVC++ 6sp4 by Alex Martelli.
|
||||
Py_cpp requires the <a href="http://www.boost.org">Boost</a> libraries,
|
||||
and is currently under formal review on <a href=
|
||||
"http://www.egroups.com/list/boost">the boost mailing list</a> for
|
||||
acceptance into boost.
|
||||
<p>
|
||||
py_cpp was originally written by David Abrahams. Ullrich Koethe supplied
|
||||
an early version of the overloading support and wrote the support for
|
||||
implicit conversions of arguments that have a C++ inheritance
|
||||
relationship. Alex Martelli supplied the first tests against Python 2.0.
|
||||
The members of the boost mailing list and the Python community supplied
|
||||
invaluable early feedback. The development of py_cpp wouldn't have been
|
||||
possible without the generous support of Dragon Systems/Lernout and
|
||||
Hauspie, Inc.
|
||||
<h2>
|
||||
Table of Contents
|
||||
</h2>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="extending.html">A Brief Introduction to writing Python
|
||||
extension modules</a>
|
||||
<li>
|
||||
<a href="comparisons.html">Comparisons between py_cpp and other systems
|
||||
for extending Python</a>
|
||||
<li>
|
||||
<a href="example1.html">A Simple Example Using py_cpp</a>
|
||||
<li>
|
||||
<a href="overriding.html">Overridable Virtual Functions</a>
|
||||
<li>
|
||||
<a href="overloading.html">Function Overloading</a>
|
||||
<li>
|
||||
<a href="special.html">Special Method Name Support</a>
|
||||
<li>
|
||||
<a href="under-the-hood.html">A Peek Under the Hood</a>
|
||||
</ol>
|
||||
<p>
|
||||
More sophisticated examples, including examples which demonstrate that
|
||||
these ExtensionClasses support some of Python's "special" member
|
||||
functions (e.g. <code>__getattr__(self, name)</code>), are given in
|
||||
<code>extclass_demo.cpp</code>, <code> extclass_demo.h</code>, and <code>
|
||||
test_extclass.py</code> in the <a href="py_cpp.tgz">source code
|
||||
archive</a>. There's much more here, and much more documentation to
|
||||
come...
|
||||
<p>
|
||||
Questions should be directed to <a href=
|
||||
"http://www.egroups.com/list/boost">the boost mailing list</a> or to <a
|
||||
href="mailto:abrahams@mediaone.net"> David Abrahams</a>, the primary
|
||||
author and maintainer.
|
||||
<h3>
|
||||
Naming Contest
|
||||
</h3>
|
||||
<p>
|
||||
Yes, I know py_cpp is a lousy name. Problem is, the best names my puny
|
||||
imagination can muster (IDLE and GRAIL) are taken, so I'm holding a
|
||||
naming contest. First prize? You get to pick the name<0.2wink> and
|
||||
you will be credited in the documentation. Names that have been suggested
|
||||
so far include:
|
||||
<ul>
|
||||
<li>
|
||||
Py++
|
||||
<li>
|
||||
Python++
|
||||
<li>
|
||||
Coil
|
||||
<li>
|
||||
SnakeSkin
|
||||
<li>
|
||||
CCCP - <strong>C</strong>onvert <strong>C</strong>++ <strong>
|
||||
C</strong>lasses to <strong>P</strong>ython
|
||||
<li>
|
||||
C<sup>3</sup>PO - <strong>C</strong>onvert <strong>C</strong>++
|
||||
<strong>C</strong>lasses to <strong>P</strong>ython <strong>
|
||||
O</strong>bjects
|
||||
<li>
|
||||
PALIN - <strong>P</strong>ython <strong>
|
||||
A</strong>ugmented-<strong>L</strong>anguage <strong>
|
||||
IN</strong>tegration
|
||||
<li>
|
||||
C-thru
|
||||
<li>
|
||||
SeamlessC
|
||||
<li>
|
||||
BorderCrossing
|
||||
</ul>
|
||||
Please <a href="http://www.egroups.com/list/boost">post</a> or send <a
|
||||
href="http:mailto:abrahams@mediaone.net">me</a> your suggestions!<br>
|
||||
<br>
|
||||
|
||||
<p>
|
||||
© Copyright David Abrahams 2000. Permission to copy, use, modify,
|
||||
sell and distribute this document is granted provided this copyright
|
||||
notice appears in all copies. This document is provided "as is" without
|
||||
express or implied warranty, and with no claim as to its suitability for
|
||||
any purpose.
|
||||
<p>
|
||||
Updated: Oct 18, 2000
|
||||
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
TargetName=py_cpp_d
|
||||
TargetType=lib
|
||||
SourceFiles=extclass_d.cpp init_function_d.cpp subclass_d.cpp functions_d.cpp objects_d.cpp
|
||||
SourceFiles+=module_d.cpp newtypes_d.cpp py_d.cpp
|
||||
@@ -1,21 +0,0 @@
|
||||
ifndef PY_CPPHELP_MAK_INCLUDED
|
||||
PY_CPPHELP_MAK_INCLUDED=1
|
||||
|
||||
ifndef DONT_BUILD_IMPORTS
|
||||
|
||||
|
||||
PY_CPP_DIR=$(ROOT_DIR)/py_cpp
|
||||
PY_CPP_LIB=$(PY_CPP_DIR)/$(OBJDIR)/py_cpp.lib
|
||||
PY_CPP_D_LIB=$(PY_CPP_DIR)/$(OBJDIR)/py_cpp_d.lib
|
||||
|
||||
$(PY_CPP_LIB) : FORCE
|
||||
$(RECURSIVE_MAKE) -C $(PY_CPP_DIR) py_cpp
|
||||
|
||||
$(PY_CPP_D_LIB) : FORCE
|
||||
$(RECURSIVE_MAKE) -C $(PY_CPP_DIR) py_cpp_d
|
||||
|
||||
# end ifndef DONT_BUILD_IMPORTS
|
||||
endif
|
||||
|
||||
# end ifndef PY_CPPHELP_MAK_INCLUDED
|
||||
endif
|
||||
13
pyconfig.h
13
pyconfig.h
@@ -19,7 +19,7 @@
|
||||
# define PY_INLINE_FRIEND ::py
|
||||
# endif
|
||||
|
||||
# if !defined(__GNUC__) && !defined(__MWERKS__) && !defined(__BORLANDC__) && defined(_MSC_VER)
|
||||
# if defined(BOOST_MSVC)
|
||||
# define PY_COMPILER_IS_MSVC 1
|
||||
# if _MSC_VER <= 1200
|
||||
# define PY_MSVC6_OR_EARLIER 1
|
||||
@@ -29,6 +29,17 @@
|
||||
|
||||
# endif
|
||||
|
||||
// Work around the broken library implementation/strict ansi checking on some
|
||||
// EDG-based compilers (e.g. alpha), which incorrectly warn that the result of
|
||||
// offsetof() is not an integer constant expression.
|
||||
# if defined(__DECCXX_VER) && __DECCXX_VER <= 60290024
|
||||
# define PY_OFFSETOF(s_name, s_member) \
|
||||
((size_t)__INTADDR__(&(((s_name *)0)->s_member)))
|
||||
# else
|
||||
# define PY_OFFSETOF(s_name, s_member) \
|
||||
offsetof(s_name, s_member)
|
||||
# endif
|
||||
|
||||
// The STLport puts all of the standard 'C' library names in std (as far as the
|
||||
// user is concerned), but without it you need a fix if you're using MSVC.
|
||||
# if defined(PY_MSVC6_OR_EARLIER) && !defined(__STLPORT)
|
||||
|
||||
401
special.html
Normal file
401
special.html
Normal file
@@ -0,0 +1,401 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
|
||||
"http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<title>
|
||||
Special Method Name Support
|
||||
</title>
|
||||
<div>
|
||||
<h1>
|
||||
<img width="277" height="86" id="_x0000_i1025" align="center" src=
|
||||
"c++boost.gif" alt="c++boost.gif (8819 bytes)">Special Method Name
|
||||
Support
|
||||
</h1>
|
||||
<h2>
|
||||
Overview
|
||||
</h2>
|
||||
<p>
|
||||
Py_cpp supports all of the standard <a href=
|
||||
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/specialnames.html">
|
||||
special method names</a> supported by real Python class instances <em>
|
||||
except:</em>
|
||||
<ul>
|
||||
<li><a href=
|
||||
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/customization.html">
|
||||
<code>__del__</code></a> (an oversight, to be corrected soon - but
|
||||
don't worry, your underlying C++ objects are still properly destroyed!)
|
||||
<li>the <code>__r<em><name></em>__</code> "reversed operand" <a href=
|
||||
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/numeric-types.html">
|
||||
numeric methods</a>, and
|
||||
<li><code>__complex__</code>
|
||||
</ul>
|
||||
|
||||
(more on the latter two <a href="#reverse">below</a>). So, for example, we can wrap a
|
||||
<code>std::map<std::size_t,std::string></code> as follows:
|
||||
<h2>
|
||||
Example
|
||||
</h2>
|
||||
<blockquote>
|
||||
<pre>
|
||||
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, py::converters::to_python(key));
|
||||
throw py::ErrorAlreadySet();
|
||||
}
|
||||
}
|
||||
|
||||
// Define some simple wrapper functions which match the Python protocol
|
||||
// for __getitem__, __setitem__, and __delitem__. Just as in Python, a
|
||||
// free function with a "self" first parameter makes a fine class method.
|
||||
|
||||
const std::string& 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);
|
||||
}
|
||||
|
||||
ClassWrapper<StringMap> string_map(my_module, "StringMap");
|
||||
string_map.def(py::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__");
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Then in Python:
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> 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[3] = 'farther'
|
||||
>>> len(m)
|
||||
1
|
||||
</pre>
|
||||
</blockquote>
|
||||
<h2>
|
||||
<a name="getter_setter">Getters and Setters</a>
|
||||
</h2>
|
||||
<p>
|
||||
Py_cpp extension classes support some additional "special method"
|
||||
protocols not supported by built-in Python classes. Because writing
|
||||
<code>__getattr__</code>, <code> __setattr__</code>, and
|
||||
<code>__delattr__</code> functions can be tedious in the common case
|
||||
where the attributes being accessed are known statically, py_cpp checks
|
||||
the special names
|
||||
<ul>
|
||||
<li>
|
||||
<code>__getattr__<em><name></em>__</code>
|
||||
<li>
|
||||
<code>__setattr__<em><name></em>__</code>
|
||||
<li>
|
||||
<code>__delattr__<em><name></em>__</code>
|
||||
</ul>
|
||||
to provide functional access to the attribute <em><name></em>. This
|
||||
facility can be used from C++ or entirely from Python. For example, the
|
||||
following shows how we can implement a "computed attribute" in Python:
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> 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
|
||||
</pre>
|
||||
</blockquote>
|
||||
<h2>
|
||||
Direct Access to Data Members
|
||||
</h2>
|
||||
<p>
|
||||
Py_cpp uses the special <code>
|
||||
__xxxattr__<em><name></em>__</code> functionality described above
|
||||
to allow direct access to data members through the following special
|
||||
functions on <code>ClassWrapper<></code> and <code>
|
||||
ExtensionClass<></code>:
|
||||
<ul>
|
||||
<li>
|
||||
<code>def_getter(<em>pointer-to-member</em>, <em>name</em>)</code> //
|
||||
read access to the member via attribute <em>name</em>
|
||||
<li>
|
||||
<code>def_setter(<em>pointer-to-member</em>, <em>name</em>)</code> //
|
||||
write access to the member via attribute <em>name</em>
|
||||
<li>
|
||||
<code>def_readonly(<em>pointer-to-member</em>, <em>name</em>)</code>
|
||||
// read-only access to the member via attribute <em>name</em>
|
||||
<li>
|
||||
<code>def_read_write(<em>pointer-to-member</em>, <em>
|
||||
name</em>)</code> // read/write access to the member via attribute
|
||||
<em>name</em>
|
||||
</ul>
|
||||
<p>
|
||||
Note that the first two functions, used alone, may produce surprising
|
||||
behavior. For example, when <code>def_getter()</code> is used, the
|
||||
default functionality for <code>setattr()</code> and <code>
|
||||
delattr()</code> remains in effect, operating on items in the extension
|
||||
instance's name-space (i.e., its <code>__dict__</code>). For that
|
||||
reason, you'll usually want to stick with <code>def_readonly</code> and
|
||||
<code>def_read_write</code>.
|
||||
<p>
|
||||
For example, to expose a <code>std::pair<int,long></code> we
|
||||
might write:
|
||||
<blockquote>
|
||||
<pre>
|
||||
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");
|
||||
|
||||
ClassWrapper<Pil> pair_int_long(my_module, "Pair");
|
||||
pair_int_long.def(py::Constructor<>());
|
||||
pair_int_long.def(py::Constructor<int,long>());
|
||||
pair_int_long.def_read_write(&Pil::first, "first");
|
||||
pair_int_long.def_read_write(&Pil::second, "second");
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Now your Python class has attributes <code>first</code> and <code>
|
||||
second</code> which, when accessed, actually modify or reflect the
|
||||
values of corresponding data members of the underlying C++ object. Now
|
||||
in Python:
|
||||
<blockquote>
|
||||
<pre>
|
||||
>>> 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
|
||||
</pre>
|
||||
</blockquote>
|
||||
<h2>
|
||||
<a name="numerics">Numeric Method Support</a>
|
||||
</h2>
|
||||
<p>
|
||||
Py_cpp supports the following <a href=
|
||||
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/numeric-types.html">
|
||||
Python special numeric method names</a>:
|
||||
<p>
|
||||
<table summary="special numeric methods" cellpadding="5" border="1">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
Name
|
||||
<td>
|
||||
Notes
|
||||
<tr>
|
||||
<td>
|
||||
<code>__add__(self, other)</code>
|
||||
<td>
|
||||
<code>operator+(const T&, const T&)</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__sub__(self, other)</code>
|
||||
<td>
|
||||
<code>operator-(const T&, const T&)</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__mul__(self, other)</code>
|
||||
<td>
|
||||
<code>operator*(const T&, const T&)</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__div__(self, other)</code>
|
||||
<td>
|
||||
<code>operator/(const T&, const T&)</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__mod__(self, other)</code>
|
||||
<td>
|
||||
<code>operator%(const T&, const T&)</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__divmod__(self, other)</code>
|
||||
<td>
|
||||
return a <code> py::Tuple</code> initialized with <code>
|
||||
(</code><em>quotient</em><code>,</code> <em>
|
||||
remainder</em><code>)</code>.
|
||||
<tr>
|
||||
<td>
|
||||
<code>__pow__(self, other [, modulo])</code>
|
||||
<td>
|
||||
use <a href="overloading.html">overloading</a> to support both
|
||||
forms of __pow__
|
||||
<tr>
|
||||
<td>
|
||||
<code>__lshift__(self, other)</code>
|
||||
<td>
|
||||
<code>operator<<(const T&, const T&)</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__rshift__(self, other)</code>
|
||||
<td>
|
||||
<code>operator>>(const T&, const T&)</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__and__(self, other)</code>
|
||||
<td>
|
||||
<code>operator&(const T&, const T&)</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__xor__(self, other)</code>
|
||||
<td>
|
||||
<code>operator^(const T&, const T&)</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__or__(self, other)</code>
|
||||
<td>
|
||||
<code>operator|(const T&, const T&)</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__neg__(self)</code>
|
||||
<td>
|
||||
<code>operator-(const T&)</code> (unary negation)
|
||||
<tr>
|
||||
<td>
|
||||
<code>__pos__(self)</code>
|
||||
<td>
|
||||
<code>operator+(const T&)</code> (identity)
|
||||
<tr>
|
||||
<td>
|
||||
<code>__abs__(self)</code>
|
||||
<td>
|
||||
Called to implement the built-in function abs()
|
||||
<tr>
|
||||
<td>
|
||||
<code>__invert__(self)</code>
|
||||
<td>
|
||||
<code>operator~(const T&)</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__int__(self)</code>
|
||||
<td>
|
||||
<code>operator long() const</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__long__(self)</code>
|
||||
<td>
|
||||
Should return a Python <code>long</code> object. Can be
|
||||
implemented with <code>PyLong_FromLong(<em>value</em>)</code>,
|
||||
for example.
|
||||
<tr>
|
||||
<td>
|
||||
<code>__float__(self)</code>
|
||||
<td>
|
||||
<code>operator double() const</code>
|
||||
<tr>
|
||||
<td>
|
||||
<code>__oct__(self)</code>
|
||||
<td>
|
||||
Called to implement the built-in function oct(). Should return a
|
||||
string value.
|
||||
<tr>
|
||||
<td>
|
||||
<code>__hex__(self)</code>
|
||||
<td>
|
||||
Called to implement the built-in function hex(). Should return a
|
||||
string value.
|
||||
<tr>
|
||||
<td>
|
||||
<code>__coerce__(self, other)</code>
|
||||
<td>
|
||||
Should return a Python 2-<em>tuple</em> (C++ code may return a
|
||||
<code>py::Tuple</code>) where the elements represent the values
|
||||
of <code> self</code> and <code>other</code> converted to the
|
||||
same type.
|
||||
</table>
|
||||
|
||||
<h2><a name="reverse">Where are the <code>__r</code><i><name></i><code>__</code>
|
||||
functions?</a></h2>
|
||||
|
||||
<p>
|
||||
At first we thought that supporting <code>__radd__</code> and its ilk would be
|
||||
impossible, since Python doesn't supply any direct support and in fact
|
||||
implements a special case for its built-in class instances. <a
|
||||
href="http://starship.python.net/crew/arcege/extwriting/pyextnum.html">This
|
||||
article</a> gives a pretty good overview of the direct support for numerics
|
||||
that Python supplies for extension types. We've since discovered that it can
|
||||
be done, but there are some pretty convincing <a
|
||||
href="http://starship.python.net/crew/lemburg/CoercionProposal.html">arguments</a>
|
||||
out there that this arrangement is less-than-ideal. Instead of supplying a
|
||||
sub-optimal solution for the sake of compatibility with built-in Python
|
||||
classes, we're doing the neccessary research so we can "do it right". This
|
||||
will also give us a little time to hear from users about what they want. The
|
||||
direction we're headed in is based on the idea of <a
|
||||
href="http://www.sff.net/people/neelk/open-source/Multimethod.py">multimethods</a>
|
||||
rather than on trying to find a coercion function bound to one of the
|
||||
arguments.
|
||||
|
||||
<h3>And what about <code>__complex__</code>?</h3>
|
||||
<p>That, dear reader, is one problem we don't know how to solve. The Python
|
||||
source contains the following fragment, indicating the special-case code really
|
||||
is hardwired:
|
||||
<blockquote>
|
||||
<pre>
|
||||
/* XXX Hack to support classes with __complex__ method */
|
||||
if (PyInstance_Check(r)) { ...
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>
|
||||
Previous: <a href="overloading.html">Function Overloading</a> Next: <a
|
||||
href="under-the-hood.html">A Peek Under the Hood</a> Up: <a href=
|
||||
"py_cpp.html">Top</a>
|
||||
<p>
|
||||
© Copyright David Abrahams 2000. Permission to copy, use, modify,
|
||||
sell and distribute this document is granted provided this copyright
|
||||
notice appears in all copies. This document is provided "as is" without
|
||||
express or implied warranty, and with no claim as to its suitability
|
||||
for any purpose.
|
||||
<p>
|
||||
Updated: Oct 19, 2000
|
||||
</div>
|
||||
|
||||
160
subclass.cpp
160
subclass.cpp
@@ -38,12 +38,8 @@ PyObject* Instance::getattr(const char* name, bool use_special_function)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Suspend the error while we try special methods method (if any).
|
||||
#if 0
|
||||
SuspendError suspended_error(SuspendError::discard_new_error);
|
||||
#else
|
||||
// Clear the error while we try special methods method (if any).
|
||||
PyErr_Clear();
|
||||
#endif
|
||||
|
||||
// First we try the special method that comes from concatenating
|
||||
// "__getattr__" and <name> and 2 trailing underscores. This is an
|
||||
@@ -67,15 +63,11 @@ PyObject* Instance::getattr(const char* name, bool use_special_function)
|
||||
}
|
||||
|
||||
// If there is no such method, throw now.
|
||||
#if 0
|
||||
suspended_error.throw_if_error();
|
||||
#else
|
||||
if (PyErr_Occurred())
|
||||
{
|
||||
PyErr_SetString(PyExc_AttributeError, name);
|
||||
throw ErrorAlreadySet();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Take ownership of the method
|
||||
Ptr owner(getattr_method);
|
||||
@@ -224,6 +216,131 @@ void Instance::set_slice(int start, int finish, PyObject* value)
|
||||
Callback<void>::call_method(this, "__setslice__", start, finish, value);
|
||||
}
|
||||
|
||||
PyObject* Instance::add(PyObject* other)
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__add__", other);
|
||||
}
|
||||
|
||||
PyObject* Instance::subtract(PyObject* other)
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__sub__", other);
|
||||
}
|
||||
|
||||
PyObject* Instance::multiply(PyObject* other)
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__mul__", other);
|
||||
}
|
||||
|
||||
PyObject* Instance::divide(PyObject* other)
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__div__", other);
|
||||
}
|
||||
|
||||
PyObject* Instance::remainder(PyObject* other)
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__mod__", other);
|
||||
}
|
||||
|
||||
PyObject* Instance::divmod(PyObject* other)
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__divmod__", other);
|
||||
}
|
||||
|
||||
PyObject* Instance::power(PyObject* exponent, PyObject* modulus)
|
||||
{
|
||||
if (as_object(modulus->ob_type) == Py_None)
|
||||
return Callback<PyObject*>::call_method(this, "__pow__", exponent);
|
||||
else
|
||||
return Callback<PyObject*>::call_method(this, "__pow__", exponent, modulus);
|
||||
}
|
||||
|
||||
PyObject* Instance::negative()
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__neg__");
|
||||
}
|
||||
|
||||
PyObject* Instance::positive()
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__pos__");
|
||||
}
|
||||
|
||||
PyObject* Instance::absolute()
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__abs__");
|
||||
}
|
||||
|
||||
int Instance::nonzero()
|
||||
{
|
||||
return Callback<bool>::call_method(this, "__nonzero__");
|
||||
}
|
||||
|
||||
PyObject* Instance::invert()
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__invert__");
|
||||
}
|
||||
|
||||
PyObject* Instance::lshift(PyObject* other)
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__lshift__", other);
|
||||
}
|
||||
|
||||
PyObject* Instance::rshift(PyObject* other)
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__rshift__", other);
|
||||
}
|
||||
|
||||
PyObject* Instance::do_and(PyObject* other)
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__and__", other);
|
||||
}
|
||||
|
||||
PyObject* Instance::do_xor(PyObject* other)
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__xor__", other);
|
||||
}
|
||||
|
||||
PyObject* Instance::do_or(PyObject* other)
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__or__", other);
|
||||
}
|
||||
|
||||
int Instance::coerce(PyObject** x, PyObject** y)
|
||||
{
|
||||
assert(this == *x);
|
||||
|
||||
// Coerce must return a tuple
|
||||
Tuple result(Callback<Tuple>::call_method(this, "__coerce__", *y));
|
||||
|
||||
*x = result[0].release();
|
||||
*y = result[1].release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* Instance::as_int()
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__int__");
|
||||
}
|
||||
|
||||
PyObject* Instance::as_long()
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__long__");
|
||||
}
|
||||
|
||||
PyObject* Instance::as_float()
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__float__");
|
||||
}
|
||||
|
||||
PyObject* Instance::oct()
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__oct__");
|
||||
}
|
||||
|
||||
PyObject* Instance::hex()
|
||||
{
|
||||
return Callback<PyObject*>::call_method(this, "__hex__");
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct NamedCapability
|
||||
{
|
||||
@@ -250,7 +367,30 @@ namespace {
|
||||
{ "__delitem__", TypeObjectBase::sequence_ass_item },
|
||||
{ "__getslice__", TypeObjectBase::sequence_slice },
|
||||
{ "__setslice__", TypeObjectBase::sequence_ass_slice },
|
||||
{ "__delslice__", TypeObjectBase::sequence_ass_slice }
|
||||
{ "__delslice__", TypeObjectBase::sequence_ass_slice },
|
||||
{ "__add__", TypeObjectBase::number_add },
|
||||
{ "__sub__", TypeObjectBase::number_subtract },
|
||||
{ "__mul__", TypeObjectBase::number_multiply },
|
||||
{ "__div__", TypeObjectBase::number_divide },
|
||||
{ "__mod__", TypeObjectBase::number_remainder },
|
||||
{ "__divmod__", TypeObjectBase::number_divmod },
|
||||
{ "__pow__", TypeObjectBase::number_power },
|
||||
{ "__neg__", TypeObjectBase::number_negative },
|
||||
{ "__pos__", TypeObjectBase::number_positive },
|
||||
{ "__abs__", TypeObjectBase::number_absolute },
|
||||
{ "__nonzero__", TypeObjectBase::number_nonzero },
|
||||
{ "__invert__", TypeObjectBase::number_invert },
|
||||
{ "__lshift__", TypeObjectBase::number_lshift },
|
||||
{ "__rshift__", TypeObjectBase::number_rshift },
|
||||
{ "__and__", TypeObjectBase::number_and },
|
||||
{ "__xor__", TypeObjectBase::number_xor },
|
||||
{ "__or__", TypeObjectBase::number_or },
|
||||
{ "__coerce__", TypeObjectBase::number_coerce },
|
||||
{ "__int__", TypeObjectBase::number_int },
|
||||
{ "__long__", TypeObjectBase::number_long },
|
||||
{ "__float__", TypeObjectBase::number_float },
|
||||
{ "__oct__", TypeObjectBase::number_oct },
|
||||
{ "__hex__", TypeObjectBase::number_hex }
|
||||
};
|
||||
|
||||
bool is_prefix(const char* s1, const char* s2)
|
||||
|
||||
190
subclass.h
190
subclass.h
@@ -44,7 +44,32 @@ class Instance : public PythonObject
|
||||
// Sequence methods
|
||||
PyObject* get_slice(int start, int finish);
|
||||
void set_slice(int start, int finish, PyObject* value);
|
||||
|
||||
|
||||
// Number methods
|
||||
PyObject* add(PyObject* other);
|
||||
PyObject* subtract(PyObject* other);
|
||||
PyObject* multiply(PyObject* other);
|
||||
PyObject* divide(PyObject* other);
|
||||
PyObject* remainder(PyObject* other);
|
||||
PyObject* divmod(PyObject* other);
|
||||
PyObject* power(PyObject*, PyObject*);
|
||||
PyObject* negative();
|
||||
PyObject* positive();
|
||||
PyObject* absolute();
|
||||
int nonzero();
|
||||
PyObject* invert();
|
||||
PyObject* lshift(PyObject* other);
|
||||
PyObject* rshift(PyObject* other);
|
||||
PyObject* do_and(PyObject* other);
|
||||
PyObject* do_xor(PyObject* other);
|
||||
PyObject* do_or(PyObject* other);
|
||||
int coerce(PyObject**, PyObject**);
|
||||
PyObject* as_int();
|
||||
PyObject* as_long();
|
||||
PyObject* as_float();
|
||||
PyObject* oct();
|
||||
PyObject* hex();
|
||||
|
||||
private: // noncopyable, without the size bloat
|
||||
Instance(const Instance&);
|
||||
void operator=(const Instance&);
|
||||
@@ -92,6 +117,31 @@ class Class
|
||||
PyObject* instance_sequence_slice(PyObject*, int start, int finish) const;
|
||||
int instance_sequence_ass_slice(PyObject*, int start, int finish, PyObject* value) const;
|
||||
|
||||
private: // Implement number methods on instances
|
||||
PyObject* instance_number_add(PyObject*, PyObject*) const;
|
||||
PyObject* instance_number_subtract(PyObject*, PyObject*) const;
|
||||
PyObject* instance_number_multiply(PyObject*, PyObject*) const;
|
||||
PyObject* instance_number_divide(PyObject*, PyObject*) const;
|
||||
PyObject* instance_number_remainder(PyObject*, PyObject*) const;
|
||||
PyObject* instance_number_divmod(PyObject*, PyObject*) const;
|
||||
PyObject* instance_number_power(PyObject*, PyObject*, PyObject*) const;
|
||||
PyObject* instance_number_negative(PyObject*) const;
|
||||
PyObject* instance_number_positive(PyObject*) const;
|
||||
PyObject* instance_number_absolute(PyObject*) const;
|
||||
int instance_number_nonzero(PyObject*) const;
|
||||
PyObject* instance_number_invert(PyObject*) const;
|
||||
PyObject* instance_number_lshift(PyObject*, PyObject*) const;
|
||||
PyObject* instance_number_rshift(PyObject*, PyObject*) const;
|
||||
PyObject* instance_number_and(PyObject*, PyObject*) const;
|
||||
PyObject* instance_number_xor(PyObject*, PyObject*) const;
|
||||
PyObject* instance_number_or(PyObject*, PyObject*) const;
|
||||
int instance_number_coerce(PyObject*, PyObject**, PyObject**) const;
|
||||
PyObject* instance_number_int(PyObject*) const;
|
||||
PyObject* instance_number_long(PyObject*) const;
|
||||
PyObject* instance_number_float(PyObject*) const;
|
||||
PyObject* instance_number_oct(PyObject*) const;
|
||||
PyObject* instance_number_hex(PyObject*) const;
|
||||
|
||||
private: // Miscellaneous "special" methods
|
||||
PyObject* instance_call(PyObject* instance, PyObject* args, PyObject* keywords) const;
|
||||
|
||||
@@ -316,6 +366,144 @@ PyObject* Class<T>::instance_call(PyObject* instance, PyObject* args, PyObject*
|
||||
return Downcast<T>(instance)->call(args, keywords);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_add(PyObject* instance, PyObject* other) const
|
||||
{
|
||||
return Downcast<T>(instance)->add(other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_subtract(PyObject* instance, PyObject* other) const
|
||||
{
|
||||
return Downcast<T>(instance)->subtract(other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_multiply(PyObject* instance, PyObject* other) const
|
||||
{
|
||||
return Downcast<T>(instance)->multiply(other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_divide(PyObject* instance, PyObject* other) const
|
||||
{
|
||||
return Downcast<T>(instance)->divide(other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_remainder(PyObject* instance, PyObject* other) const
|
||||
{
|
||||
return Downcast<T>(instance)->remainder(other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_divmod(PyObject* instance, PyObject* other) const
|
||||
{
|
||||
return Downcast<T>(instance)->divmod(other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_power(PyObject* instance, PyObject* exponent, PyObject* modulus) const
|
||||
{
|
||||
return Downcast<T>(instance)->power(exponent, modulus);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_negative(PyObject* instance) const
|
||||
{
|
||||
return Downcast<T>(instance)->negative();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_positive(PyObject* instance) const
|
||||
{
|
||||
return Downcast<T>(instance)->positive();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_absolute(PyObject* instance) const
|
||||
{
|
||||
return Downcast<T>(instance)->absolute();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
int Class<T>::instance_number_nonzero(PyObject* instance) const
|
||||
{
|
||||
return Downcast<T>(instance)->nonzero();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_invert(PyObject* instance) const
|
||||
{
|
||||
return Downcast<T>(instance)->invert();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_lshift(PyObject* instance, PyObject* other) const
|
||||
{
|
||||
return Downcast<T>(instance)->lshift(other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_rshift(PyObject* instance, PyObject* other) const
|
||||
{
|
||||
return Downcast<T>(instance)->rshift(other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_and(PyObject* instance, PyObject* other) const
|
||||
{
|
||||
return Downcast<T>(instance)->do_and(other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_xor(PyObject* instance, PyObject* other) const
|
||||
{
|
||||
return Downcast<T>(instance)->do_xor(other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_or(PyObject* instance, PyObject* other) const
|
||||
{
|
||||
return Downcast<T>(instance)->do_or(other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
int Class<T>::instance_number_coerce(PyObject* instance, PyObject** x, PyObject** y) const
|
||||
{
|
||||
return Downcast<T>(instance)->coerce(x, y);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_int(PyObject* instance) const
|
||||
{
|
||||
return Downcast<T>(instance)->as_int();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_long(PyObject* instance) const
|
||||
{
|
||||
return Downcast<T>(instance)->as_long();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_float(PyObject* instance) const
|
||||
{
|
||||
return Downcast<T>(instance)->as_float();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_oct(PyObject* instance) const
|
||||
{
|
||||
return Downcast<T>(instance)->oct();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* Class<T>::instance_number_hex(PyObject* instance) const
|
||||
{
|
||||
return Downcast<T>(instance)->hex();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Dict& Class<T>::dict()
|
||||
{
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_PYTHON
|
||||
#include "subclass.cpp"
|
||||
174
test_extclass.py
174
test_extclass.py
@@ -15,10 +15,11 @@ a single long parameter.
|
||||
File "<stdin>", line 1, in ?
|
||||
TypeError: function requires exactly 1 argument; 0 given
|
||||
|
||||
>>> ext = Foo('foo')
|
||||
Traceback (innermost last):
|
||||
File "<stdin>", line 1, in ?
|
||||
TypeError: illegal argument type for built-in operation
|
||||
>>> try: ext = Foo('foo')
|
||||
... except TypeError, err:
|
||||
... assert re.match(
|
||||
... '(illegal argument type for built-in operation)|(an integer is required)', str(err))
|
||||
... else: print 'no exception'
|
||||
|
||||
>>> ext = Foo(1)
|
||||
|
||||
@@ -129,7 +130,7 @@ But objects not derived from Bar cannot:
|
||||
>>> baz.pass_bar(baz)
|
||||
Traceback (innermost last):
|
||||
...
|
||||
TypeError: extension class 'Baz' is not derived from 'Bar'.
|
||||
TypeError: extension class 'Baz' is not convertible into 'Bar'.
|
||||
|
||||
The clone function on Baz returns a smart pointer; we wrap it into an
|
||||
ExtensionInstance and make it look just like any other Baz instance.
|
||||
@@ -144,6 +145,8 @@ Functions expecting an std::auto_ptr<Baz> parameter will not accept a raw Baz
|
||||
... except RuntimeError, err:
|
||||
... assert re.match("Object of extension class 'Baz' does not wrap <.*>.",
|
||||
... str(err))
|
||||
... else:
|
||||
... print 'no exception'
|
||||
|
||||
We can pass std::auto_ptr<Baz> where it is expected
|
||||
|
||||
@@ -159,6 +162,8 @@ And if the auto_ptr has given up ownership?
|
||||
... try: baz_clone.clone()
|
||||
... except RuntimeError, err:
|
||||
... assert re.match('Converting from python, pointer or smart pointer to <.*> is NULL.', str(err))
|
||||
... else:
|
||||
... print 'no exeption'
|
||||
|
||||
Polymorphism also works:
|
||||
|
||||
@@ -232,6 +237,7 @@ Overloading tests:
|
||||
... assert re.match(
|
||||
... 'No overloaded functions match [(]Range, string[)]\. Candidates are:\n.*\n.*',
|
||||
... str(e))
|
||||
... else: print 'no exception'
|
||||
|
||||
Sequence tests:
|
||||
>>> len(Range(3, 10))
|
||||
@@ -258,6 +264,22 @@ Sequence tests:
|
||||
>>> map(lambda x:x, Range(3, 10)[0:4])
|
||||
[3, 4, 5, 6]
|
||||
|
||||
Numeric tests:
|
||||
>>> x = Rational(2,3)
|
||||
>>> y = Rational(1,4)
|
||||
>>> print x + y
|
||||
11/12
|
||||
>>> print x - y
|
||||
5/12
|
||||
>>> print x * y
|
||||
1/6
|
||||
>>> print x / y
|
||||
8/3
|
||||
>>> print x + 1 # testing coercion
|
||||
5/3
|
||||
>>> print 1 + x # coercion the other way
|
||||
5/3
|
||||
|
||||
delete non-existent attribute:
|
||||
del m.foobar
|
||||
Traceback (innermost last):
|
||||
@@ -368,7 +390,7 @@ some __str__ and __repr__ tests:
|
||||
>>> range = Range(5, 20)
|
||||
>>> str(range)
|
||||
'(5, 20)'
|
||||
>>> assert re.match('<Range object at [0-9a-f]+>', repr(range))
|
||||
>>> assert re.match('<Range object at [0-9a-fA-F]+>', repr(range))
|
||||
|
||||
|
||||
__hash__ and __cmp__ tests:
|
||||
@@ -406,6 +428,146 @@ Testing __call__:
|
||||
0
|
||||
>>> comparator(couple2, couple)
|
||||
1
|
||||
|
||||
Testing overloaded free functions
|
||||
>>> overloaded()
|
||||
'Hello world!'
|
||||
>>> overloaded(1)
|
||||
1
|
||||
>>> overloaded('foo')
|
||||
'foo'
|
||||
>>> overloaded(1,2)
|
||||
3
|
||||
>>> overloaded(1,2,3)
|
||||
6
|
||||
>>> overloaded(1,2,3,4)
|
||||
10
|
||||
>>> overloaded(1,2,3,4,5)
|
||||
15
|
||||
>>> try: overloaded(1, 'foo')
|
||||
... except TypeError, err:
|
||||
... assert re.match("No overloaded functions match \(int, string\)\. Candidates are:",
|
||||
... str(err))
|
||||
... else:
|
||||
... print 'no exception'
|
||||
|
||||
Testing overloaded constructors
|
||||
|
||||
>>> over = OverloadTest()
|
||||
>>> over.getX()
|
||||
1000
|
||||
>>> over = OverloadTest(1)
|
||||
>>> over.getX()
|
||||
1
|
||||
>>> over = OverloadTest(1,1)
|
||||
>>> over.getX()
|
||||
2
|
||||
>>> over = OverloadTest(1,1,1)
|
||||
>>> over.getX()
|
||||
3
|
||||
>>> over = OverloadTest(1,1,1,1)
|
||||
>>> over.getX()
|
||||
4
|
||||
>>> over = OverloadTest(1,1,1,1,1)
|
||||
>>> over.getX()
|
||||
5
|
||||
>>> over = OverloadTest(over)
|
||||
>>> over.getX()
|
||||
5
|
||||
>>> try: over = OverloadTest(1, 'foo')
|
||||
... except TypeError, err:
|
||||
... assert re.match("No overloaded functions match \(OverloadTest, int, string\)\. Candidates are:",
|
||||
... str(err))
|
||||
... else:
|
||||
... print 'no exception'
|
||||
|
||||
Testing overloaded methods
|
||||
|
||||
>>> over.setX(3)
|
||||
>>> over.overloaded()
|
||||
3
|
||||
>>> over.overloaded(1)
|
||||
1
|
||||
>>> over.overloaded(1,1)
|
||||
2
|
||||
>>> over.overloaded(1,1,1)
|
||||
3
|
||||
>>> over.overloaded(1,1,1,1)
|
||||
4
|
||||
>>> over.overloaded(1,1,1,1,1)
|
||||
5
|
||||
>>> try: over.overloaded(1,'foo')
|
||||
... except TypeError, err:
|
||||
... assert re.match("No overloaded functions match \(OverloadTest, int, string\)\. Candidates are:",
|
||||
... str(err))
|
||||
... else:
|
||||
... print 'no exception'
|
||||
|
||||
Testing base class conversions
|
||||
|
||||
>>> testUpcast(over)
|
||||
Traceback (innermost last):
|
||||
TypeError: extension class 'OverloadTest' is not convertible into 'Base'.
|
||||
>>> der1 = Derived1(333)
|
||||
>>> der1.x()
|
||||
333
|
||||
>>> testUpcast(der1)
|
||||
333
|
||||
>>> der1 = derived1Factory(1000)
|
||||
>>> testDowncast1(der1)
|
||||
1000
|
||||
>>> testDowncast2(der1)
|
||||
Traceback (innermost last):
|
||||
TypeError: extension class 'Base' is not convertible into 'Derived2'.
|
||||
>>> der2 = Derived2(444)
|
||||
>>> der2.x()
|
||||
444
|
||||
>>> testUpcast(der2)
|
||||
444
|
||||
>>> der2 = derived2Factory(1111)
|
||||
>>> testDowncast2(der2)
|
||||
Traceback (innermost last):
|
||||
TypeError: extension class 'Base' is not convertible into 'Derived2'.
|
||||
|
||||
Testing interaction between callbacks, base declarations, and overloading
|
||||
- testCallback() calls callback() (within C++)
|
||||
- callback() is overloaded (in the wrapped class CallbackTest)
|
||||
- callback() is redefined in RedefineCallback (overloading is simulated by type casing)
|
||||
- testCallback() should use the redefined callback()
|
||||
|
||||
>>> c = CallbackTest()
|
||||
>>> c.testCallback(1)
|
||||
2
|
||||
>>> c.testCallback('foo')
|
||||
Traceback (innermost last):
|
||||
File "<stdin>", line 1, in ?
|
||||
TypeError: illegal argument type for built-in operation
|
||||
>>> c.callback(1)
|
||||
2
|
||||
>>> c.callback('foo')
|
||||
'foo 1'
|
||||
|
||||
>>> import types
|
||||
>>> class RedefineCallback(CallbackTest):
|
||||
... def callback(self, x):
|
||||
... if type(x) is types.IntType:
|
||||
... return x - 2
|
||||
... else:
|
||||
... return CallbackTest.callback(self,x)
|
||||
...
|
||||
>>> r = RedefineCallback()
|
||||
>>> r.callback(1)
|
||||
-1
|
||||
>>> r.callback('foo')
|
||||
'foo 1'
|
||||
>>> r.testCallback('foo')
|
||||
Traceback (innermost last):
|
||||
File "<stdin>", line 1, in ?
|
||||
TypeError: illegal argument type for built-in operation
|
||||
>>> r.testCallback(1)
|
||||
-1
|
||||
>>> testCallback(r, 1)
|
||||
-1
|
||||
'''
|
||||
|
||||
from demo import *
|
||||
|
||||
87
todo.txt
87
todo.txt
@@ -5,22 +5,95 @@ Document error-handling
|
||||
Consider renaming PyPtr to Reference.
|
||||
Report Cygwin linker memory issues
|
||||
handle more arguments
|
||||
MI from both ExtensionClasses and Python classes, or at least don't crash(!)
|
||||
Remove one level of indirection on type objects (no vtbl?).
|
||||
Much more testing, especially of things in objects.h
|
||||
Make multiple inheritance from real Python classes work.
|
||||
Make multiple inheritance from real Python classes work
|
||||
Handle polymorphism (passing a Wrapped<Derived> as a Base*).
|
||||
Specializations of Caller<> for commmon combinations of argument types (?)
|
||||
special member functions for numeric types
|
||||
pickling support
|
||||
testing with Python 2.0
|
||||
Make abstract classes non-instantiable (?)
|
||||
Much more testing, especially of things in objects.h
|
||||
Support for Python LONG types in Objects.h
|
||||
Concept checking for to_python<T>() template function
|
||||
Support for __del__()
|
||||
Reference-counting for UniquePodSet?
|
||||
|
||||
Documentation:
|
||||
building
|
||||
|
||||
overloading and the overload resolution mechanism
|
||||
|
||||
special member functions
|
||||
adding conversions for fundamental types
|
||||
generic conversions for template types (with partial spec).
|
||||
extending multiple-argument support.
|
||||
|
||||
differences between Python classes and ExtensionClasses
|
||||
additional capabilities of ExtensionClasses
|
||||
slice adjustment
|
||||
|
||||
exception handling
|
||||
building
|
||||
dealing with non-const reference/pointer parameters
|
||||
|
||||
Advanced Topics:
|
||||
Advanced Type Conversion
|
||||
adding conversions for fundamental types
|
||||
generic conversions for template types (with partial spec).
|
||||
|
||||
Interacting with built-in Python objects and types from C++
|
||||
|
||||
dealing with non-const reference/pointer parameters
|
||||
|
||||
Private virtual functions with default implementations
|
||||
|
||||
extending multiple-argument support using gen_all.py
|
||||
|
||||
limitations
|
||||
templates
|
||||
Yes. If you look at the examples in extclass_demo.cpp you'll see that I have
|
||||
exposed several template instantiations (e.g. std::pair<int,int>) in Python.
|
||||
Keep in mind, however, that you can only expose a template instantiation,
|
||||
not a template. In other words, MyTemplate<Foo> can be exposed. MyTemplate
|
||||
itself cannot.
|
||||
|
||||
Well, that's not strictly true. Wow, this is more complicated to explain
|
||||
than I thought.
|
||||
You can't make an ExtensionClass<MyTemplate>, since after all MyTemplate is
|
||||
not a type. You can only expose a concrete type to Python.
|
||||
|
||||
What you *can* do (if your compiler supports partial ordering of function
|
||||
templates - MSVC is broken and does not) is to write appropriate
|
||||
from_python() and to_python() functions for converting a whole class of
|
||||
template instantiations to/from Python. That won't let you create an
|
||||
instance of MyTemplate<SomePythonType> from Python, but it will let you
|
||||
pass/return arbitrary MyTemplate<SomeCplusplusType> instances to/from your
|
||||
wrapped C++ functions.
|
||||
|
||||
template <class T>
|
||||
MyTemplate<T> from_python(PyObject* x, py::Type<MyTemplate<T> >)
|
||||
{
|
||||
// code to convert x into a MyTemplate<T>... that part is up to you
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PyObject* from_python(const MyTemplate<T>&)
|
||||
{
|
||||
// code to convert MyTemplate<T> into a PyObject*... that part is up to
|
||||
you
|
||||
}
|
||||
|
||||
For example, you could use this to convert Python lists to/from
|
||||
std::vector<T> automatically.
|
||||
|
||||
enums
|
||||
|
||||
the importance of declaration order of ClassWrappers/ExtensionInstances
|
||||
|
||||
out parameters and non-const pointers
|
||||
|
||||
returning references to wrapped objects
|
||||
|
||||
About the vc6 project and the debug build
|
||||
|
||||
About doctest.py
|
||||
|
||||
Boost remarks:
|
||||
|
||||
|
||||
58
under-the-hood.html
Normal file
58
under-the-hood.html
Normal file
@@ -0,0 +1,58 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<title>
|
||||
A Peek Under the Hood
|
||||
</title>
|
||||
<h1>
|
||||
<img src="c++boost.gif" alt="c++boost.gif (8819 bytes)" align="center"
|
||||
width="277" height="86">
|
||||
</h1>
|
||||
<h1>
|
||||
A Peek Under the Hood
|
||||
</h1>
|
||||
<p>
|
||||
<code> ExtensionClass<T></code> is a subclass of <code>
|
||||
PyTypeObject</code>, the <code> struct</code> which Python's 'C' API uses
|
||||
to describe a type. <a href="example1.html#world_class">An instance of
|
||||
the <code>ExtensionClass<></code></a> becomes the Python type
|
||||
object corresponding to <code>hello::world</code>. When we <a href=
|
||||
"example1.html#add_world_class">add it to the module</a> it goes into the
|
||||
module's dictionary to be looked up under the name "world".
|
||||
<p>
|
||||
Py_cpp uses C++'s template argument deduction mechanism to determine the
|
||||
types of arguments to functions (except constructors, for which we must
|
||||
<a href="example1.html#Constructor_example">provide an argument list</a>
|
||||
because they can't be named in C++). Then, it calls the appropriate
|
||||
overloaded functions <code>PyObject*
|
||||
to_python(</code><em>S</em><code>)</code> and <em>
|
||||
S'</em><code>from_python(PyObject*,
|
||||
Type<</code><em>S</em><code>>)</code> which convert between any C++
|
||||
type <em>S</em> and a <code>PyObject*</code>, the type which represents a
|
||||
reference to any Python object in its 'C' API. The <a href=
|
||||
"example1.html#world_class"><code>ExtensionClass<T></code></a>
|
||||
template defines a whole raft of these conversions (for <code>T, T*,
|
||||
T&, std::auto_ptr<T></code>, etc.), using the same inline
|
||||
friend function technique employed by <a href=
|
||||
"http://www.boost.org/libs/utility/operators.htm">the boost operators
|
||||
library</a>.
|
||||
<p>
|
||||
Because the <code>to_python</code> and <code>from_python</code> functions
|
||||
for a user-defined class are defined by <code>
|
||||
ExtensionClass<T></code>, it is important that an instantiation of
|
||||
<code> ExtensionClass<T></code> is visible to any code which wraps
|
||||
a C++ function with a <code>T, T*, const T&</code>, etc. parameter or
|
||||
return value. In particular, you may want to create all of the classes at
|
||||
the top of your module's init function, then <code>def</code> the member
|
||||
functions later to avoid problems with inter-class dependencies.
|
||||
<p>
|
||||
Previous: <a href="overriding.html">Function Overloading</a> Up: <a
|
||||
href="py_cpp.html">Top</a>
|
||||
<p>
|
||||
© Copyright David Abrahams 2000. Permission to copy, use, modify,
|
||||
sell and distribute this document is granted provided this copyright
|
||||
notice appears in all copies. This document is provided "as is" without
|
||||
express or implied warranty, and with no claim as to its suitability for
|
||||
any purpose.
|
||||
<p>
|
||||
Updated: Sept 30, 2000
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
========================================================================
|
||||
DYNAMIC LINK LIBRARY : vc6_prj
|
||||
========================================================================
|
||||
|
||||
|
||||
AppWizard has created this vc6_prj DLL for you.
|
||||
|
||||
This file contains a summary of what you will find in each of the files that
|
||||
make up your vc6_prj application.
|
||||
|
||||
vc6_prj.dsp
|
||||
This file (the project file) contains information at the project level and
|
||||
is used to build a single project or subproject. Other users can share the
|
||||
project (.dsp) file, but they should export the makefiles locally.
|
||||
|
||||
vc6_prj.cpp
|
||||
This is the main DLL source file.
|
||||
|
||||
When created, this DLL does not export any symbols. As a result, it
|
||||
will not produce a .lib file when it is built. If you wish this project
|
||||
to be a project dependency of some other project, you will either need to
|
||||
add code to export some symbols from the DLL so that an export library
|
||||
will be produced, or you can check the "doesn't produce lib" checkbox in
|
||||
the Linker settings page for this project.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Other standard files:
|
||||
|
||||
StdAfx.h, StdAfx.cpp
|
||||
These files are used to build a precompiled header (PCH) file
|
||||
named vc6_prj.pch and a precompiled types file named StdAfx.obj.
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Other notes:
|
||||
|
||||
AppWizard uses "TODO:" to indicate parts of the source code you
|
||||
should add to or customize.
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1,8 +0,0 @@
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// vc6_prj.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
@@ -1,24 +0,0 @@
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#if !defined(AFX_STDAFX_H__A41B37EF_17F7_406A_95AC_FE67BDB05B1E__INCLUDED_)
|
||||
#define AFX_STDAFX_H__A41B37EF_17F7_406A_95AC_FE67BDB05B1E__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
|
||||
// Insert your headers here
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_STDAFX_H__A41B37EF_17F7_406A_95AC_FE67BDB05B1E__INCLUDED_)
|
||||
@@ -1,13 +0,0 @@
|
||||
// vc6_prj.cpp : Defines the entry point for the DLL application.
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
BOOL APIENTRY DllMain( HANDLE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user