diff --git a/class_wrapper.h b/class_wrapper.h index dbb64e4a..b383d263 100644 --- a/class_wrapper.h +++ b/class_wrapper.h @@ -65,6 +65,45 @@ class ClassWrapper template 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 + void declare_base(ClassWrapper const & base) + { + m_class->declare_base(base.get_extension_class()); + } + + // declare the given class a base class of this one and register + // upcast conversion function + template + void declare_base(ClassWrapper 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 + void declare_base(ExtensionClass * base) + { + m_class->declare_base(base); + } + + // declare the given class a base class of this one and register + // upcast conversion function + template + void declare_base(ExtensionClass * base, WithoutDowncast) + { + m_class->declare_base(base, without_downcast); + } + + // get the embedded ExtensioClass object + ExtensionClass * get_extension_class() const + { + return m_class.get(); + } + private: PyPtr > m_class; }; diff --git a/comparisons.html b/comparisons.html index 565e8bad..d2cabc62 100644 --- a/comparisons.html +++ b/comparisons.html @@ -149,9 +149,8 @@ Acquisition.
  • Zope's ComputedAttribute support is designed to be used from Python. - The analogous feature of py_cp is designed to be used from C++ (I - make no claims that the py_cpp mechanism is more useful than the Zope - mechanism in this case) + The analogous feature of py_cpp can be used from C++ or Python. The + feature is arguably easier to use in py_cpp.

    Also, the Zope docs say: "The first superclass listed in the class diff --git a/extclass.cpp b/extclass.cpp index a1734760..c654afa3 100644 --- a/extclass.cpp +++ b/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 *>(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(method), name); diff --git a/extclass.h b/extclass.h index 3374c0ae..9b5ff64b 100644 --- a/extclass.h +++ b/extclass.h @@ -27,6 +27,7 @@ namespace py { // forward declarations class ExtensionInstance; +class ExtensionClassBase; template class InstanceHolder; template class InstanceValueHolder; template class InstancePtrHolder; @@ -53,10 +54,39 @@ T* check_non_null(T* p) template 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 { 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 const& base_classes() const = 0; + virtual std::vector const& derived_classes() const = 0; + protected: void add_method(PyPtr method, const char* name); void add_default_method(PyPtr method, const char* name); @@ -72,12 +102,24 @@ template class ClassRegistry { public: - static Class* class_object() + static ExtensionClassBase* class_object() { return static_class_object; } - static void register_class(py::Class*); - static void unregister_class(py::Class*); + + // 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 const& base_classes(); + static std::vector const& derived_classes(); private: - static py::Class* static_class_object; + static ExtensionClassBase* static_class_object; + static std::vector static_base_class_info; + static std::vector static_derived_class_info; }; #ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back to global namespace for this GCC bug @@ -123,6 +165,11 @@ class PyExtensionClassConverters py::InstanceHolder* held = dynamic_cast*>(*p); if (held != 0) return held->target(); + + // see extclass.cpp for an explanation of try_class_conversions() + void * target = py::ClassRegistry::class_object()->try_class_conversions(*p); + if(target) + return static_cast(target); } py::report_missing_instance_data(self, py::ClassRegistry::class_object(), typeid(T)); throw py::ArgumentError(); @@ -171,7 +218,6 @@ class PyExtensionClassConverters return py::PyPtr(new py::ExtensionInstance(class_)); } - // Convert to const T* friend const T* from_python(PyObject* p, py::Type) { return from_python(p, py::Type()); } @@ -250,6 +296,28 @@ class ReadOnlySetattrFunction : public Function String m_name; }; +namespace detail +{ + +template +struct DefineConversion +{ + static void * upcast_ptr(void * v) + { + return static_cast(static_cast(v)); + } + + static void * downcast_ptr(void * v) + { + return dynamic_cast(static_cast(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 objects. // @@ -344,10 +412,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 + void declare_base(ExtensionClass * base) + { + // see extclass.cpp for an explanation of why we need to register + // conversion functions + detail::BaseClassInfo baseInfo(base, + &detail::DefineConversion::downcast_ptr); + ClassRegistry::register_base_class(baseInfo); + add_base(Ptr(as_object(base), Ptr::new_ref)); + + detail::DerivedClassInfo derivedInfo(this, + &detail::DefineConversion::upcast_ptr); + ClassRegistry::register_derived_class(derivedInfo); + } + + // declare the given class a base class of this one and register + // only up conversion function + template + void declare_base(ExtensionClass * base, WithoutDowncast) + { + // see extclass.cpp for an explanation of why we need to register + // conversion functions + detail::BaseClassInfo baseInfo(base, 0); + ClassRegistry::register_base_class(baseInfo); + add_base(Ptr(as_object(base), Ptr::new_ref)); + + detail::DerivedClassInfo derivedInfo(this, + &detail::DefineConversion::upcast_ptr); + ClassRegistry::register_derived_class(derivedInfo); + } - private: + private: // types typedef InstanceValueHolder Holder; - + + private: // ExtensionClassBase virtual function implementations + std::vector const& base_classes() const; + std::vector const& derived_classes() const; + void* extract_object_from_holder(InstanceHolderBase* v) const; + + private: // Utility functions template void add_constructor(Signature sig) { @@ -425,7 +532,7 @@ class InstancePtrHolder : public InstanceHolder public: HeldType* target() { return &*m_ptr; } PtrType& ptr() { return m_ptr; } - + InstancePtrHolder(PtrType ptr) : m_ptr(ptr) {} private: PtrType m_ptr; @@ -464,6 +571,30 @@ ExtensionClass::ExtensionClass(const char* name) ClassRegistry::register_class(this); } +template +inline +std::vector const & +ExtensionClass::base_classes() const +{ + return ClassRegistry::base_classes(); +} + +template +inline +std::vector const & +ExtensionClass::derived_classes() const +{ + return ClassRegistry::derived_classes(); +} + +template +void* ExtensionClass::extract_object_from_holder(InstanceHolderBase* v) const +{ + py::InstanceHolder* held = dynamic_cast*>(v); + if(held) return held->target(); + return 0; +} + template ExtensionClass::~ExtensionClass() { @@ -471,7 +602,7 @@ ExtensionClass::~ExtensionClass() } template -inline void ClassRegistry::register_class(Class* p) +inline void ClassRegistry::register_class(ExtensionClassBase* p) { // You're not expected to create more than one of these! assert(static_class_object == 0); @@ -479,7 +610,7 @@ inline void ClassRegistry::register_class(Class* p) } template -inline void ClassRegistry::unregister_class(Class* p) +inline void ClassRegistry::unregister_class(ExtensionClassBase* p) { // The user should be destroying the same object they created. assert(static_class_object == p); @@ -487,11 +618,39 @@ inline void ClassRegistry::unregister_class(Class* p) static_class_object = 0; } +template +void ClassRegistry::register_base_class(py::detail::BaseClassInfo const & i) +{ + static_base_class_info.push_back(i); +} + +template +void ClassRegistry::register_derived_class(py::detail::DerivedClassInfo const & i) +{ + static_derived_class_info.push_back(i); +} + +template +std::vector const& ClassRegistry::base_classes() +{ + return static_base_class_info; +} + +template +std::vector const& ClassRegistry::derived_classes() +{ + return static_derived_class_info; +} + // // Static data member declaration. // template -Class* ClassRegistry::static_class_object; +ExtensionClassBase* ClassRegistry::static_class_object; +template +std::vector ClassRegistry::static_base_class_info; +template +std::vector ClassRegistry::static_derived_class_info; } // namespace py diff --git a/extclass_demo.cpp b/extclass_demo.cpp index dd6c13f5..0334e0cd 100644 --- a/extclass_demo.cpp +++ b/extclass_demo.cpp @@ -8,6 +8,7 @@ #include "extclass_demo.h" #include "class_wrapper.h" #include // used for portability on broken compilers +#include 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 derived1Factory(int i) +{ + return std::auto_ptr(new Derived1(i)); +} + +static std::auto_ptr derived2Factory(int i) +{ + return std::auto_ptr(new Derived2(i)); +} + +static int testDowncast1(Derived1 * d) +{ + return d->x(); +} + +static int testDowncast2(Derived2 * d) +{ + return d->x(); +} + +/************************************************************/ +/* */ +/* test classes for interaction of overloading, */ +/* base declarations, and callbacks */ +/* */ +/************************************************************/ + +struct CallbackTestBase +{ + virtual int testCallback(int i) { return callback(i); } + virtual int callback(int i) = 0; + virtual ~CallbackTestBase() {} +}; + +struct CallbackTest : public CallbackTestBase +{ + virtual int callback(int i) { return i + 1; } + virtual std::string callbackString(std::string const & i) { return i + " 1"; } +}; + +struct CallbackTestCallback : public CallbackTest +{ + CallbackTestCallback(PyObject* self) + : m_self(self) + {} + + int callback(int x) + { + return py::Callback::call_method(m_self, "callback", x); + } + std::string callbackString(std::string const & x) + { + return py::Callback::call_method(m_self, "callback", x); + } + + static int default_callback(CallbackTest * self, int x) + { + return self->CallbackTest::callback(x); + } + static std::string default_callbackString(CallbackTest * self, std::string x) + { + return self->CallbackTest::callbackString(x); + } + + PyObject * m_self; +}; + +int testCallback(CallbackTestBase * b, int i) +{ + return b->testCallback(i); +} + +typedef boost::rational 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 +struct StandardOps +{ + static T add(const T& x, const T& y) { return x + y; } + static T sub(const T& x, const T& y) { return x - y; } + static T mul(const T& x, const T& y) { return x * y; } + static T div(const T& x, const T& y) { return x / y; } + static T cmp(const T& x, const T& y) { return std::less()(x, y) ? -1 : std::less()(y, x) ? 1 : 0; } +}; + +/************************************************************/ +/* */ +/* 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 > rational(m, "Rational"); + rational.def(py::Constructor()); + rational.def(py::Constructor()); + rational.def(py::Constructor<>()); + rational.def(StandardOps::add, "__add__"); + rational.def(StandardOps::sub, "__sub__"); + rational.def(StandardOps::mul, "__mul__"); + rational.def(StandardOps::div, "__div__"); + rational.def(StandardOps::cmp, "__cmp__"); + rational.def(ratio_coerce, "__coerce__"); + rational.def(ratio_str, "__str__"); + rational.def(ratio_repr, "__repr__"); + rational.def(ratio_abs, "__abs__"); + py::ClassWrapper range(m, "Range"); range.def(py::Constructor()); range.def(py::Constructor()); @@ -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 over(m, "OverloadTest"); + over.def(py::Constructor()); + over.def(py::Constructor()); + over.def(py::Constructor()); + over.def(py::Constructor()); + over.def(py::Constructor()); + over.def(py::Constructor()); + over.def(py::Constructor()); + over.def(&getX, "getX"); + over.def(&OverloadTest::setX, "setX"); + over.def(&OverloadTest::x, "overloaded"); + over.def(&OverloadTest::p1, "overloaded"); + over.def(&OverloadTest::p2, "overloaded"); + over.def(&OverloadTest::p3, "overloaded"); + over.def(&OverloadTest::p4, "overloaded"); + over.def(&OverloadTest::p5, "overloaded"); + + py::ClassWrapper base(m, "Base"); + base.def(&Base::x, "x"); + + py::ClassWrapper 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()); + + py::ClassWrapper derived2(m, "Derived2"); + // don't enable downcast from Base to Derived2 + derived2.declare_base(base, py::without_downcast); + derived2.def(py::Constructor()); + + m.def(&testUpcast, "testUpcast"); + m.def(&derived1Factory, "derived1Factory"); + m.def(&derived2Factory, "derived2Factory"); + m.def(&testDowncast1, "testDowncast1"); + m.def(&testDowncast2, "testDowncast2"); + + py::ClassWrapper callbackTestBase(m, "CallbackTestBase"); + callbackTestBase.def(&CallbackTestBase::testCallback, "testCallback"); + m.def(&testCallback, "testCallback"); + + py::ClassWrapper callbackTest(m, "CallbackTest"); + callbackTest.def(py::Constructor()); + callbackTest.def(&CallbackTest::callback, "callback", + &CallbackTestCallback::default_callback); + callbackTest.def(&CallbackTest::callbackString, "callback", + &CallbackTestCallback::default_callbackString); + + callbackTest.declare_base(callbackTestBase); } void init_module() diff --git a/gcc.mak b/gcc.mak index 00cea9e5..cec872cf 100644 --- a/gcc.mak +++ b/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 diff --git a/newtypes.cpp b/newtypes.cpp index e0b4223f..8c75795b 100644 --- a/newtypes.cpp +++ b/newtypes.cpp @@ -86,21 +86,13 @@ static MethodStruct* enable_method(const MethodStruct* base, MemberPtr p, Fn f) return const_cast(detail::UniquePodSet::instance().get(new_value)); } -// TODO: is there a problem with calling convention here, or can I really pass a -// pointer to a C++ linkage function as a C-linkage function pointer? The -// compilers seem to swallow it, but is it legal? Symantec C++ for Mac didn't -// behave this way, FWIW. -// Using C++ linkage allows us to keep the virtual function members of -// TypeObjectBase private and use friendship to get them called. +namespace { -extern "C" { - -static PyObject* do_instance_repr(PyObject* instance) +PyObject* call(PyObject* instance, PyObject* (TypeObjectBase::*f)(PyObject*) const) { try { - return static_cast(instance->ob_type) - ->instance_repr(instance); + return (static_cast(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 +R int_call(PyObject* instance, R (TypeObjectBase::*f)(PyObject*) const) { try { - return static_cast(instance->ob_type) - ->instance_compare(instance, other); + return (static_cast(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 +PyObject* call(PyObject* instance, PyObject* (TypeObjectBase::*f)(PyObject*, A1) const, A1 a1) { try { - return static_cast(instance->ob_type) - ->instance_str(instance); + return (static_cast(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 +int call(PyObject* instance, int (TypeObjectBase::*f)(PyObject*, A1) const, A1 a1) { try { - return static_cast(instance->ob_type) - ->instance_hash(instance); + return (static_cast(instance->ob_type)->*f)(instance, a1); + } + catch(...) + { + handle_exception(); + return -1; + } +} + +template +PyObject* call(PyObject* instance, PyObject* (TypeObjectBase::*f)(PyObject*, A1, A2) const, A1 a1, A2 a2) +{ + try + { + return (static_cast(instance->ob_type)->*f)(instance, a1, a2); + } + catch(...) + { + handle_exception(); + return 0; + } +} + +template +int call(PyObject* instance, int (TypeObjectBase::*f)(PyObject*, A1, A2) const, A1 a1, A2 a2) +{ + try + { + return (static_cast(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 +int call(PyObject* instance, int (TypeObjectBase::*f)(PyObject*, A1, A2, A3) const, A1 a1, A2 a2, A3 a3) { try { - return static_cast(instance->ob_type) - ->instance_call(instance, args, keywords); + return (static_cast(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(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(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(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(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(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(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(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(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(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(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(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(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); } } @@ -402,6 +482,7 @@ template struct category_type; 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); @@ -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() { } diff --git a/newtypes.h b/newtypes.h index 94382232..ec8839a8 100644 --- a/newtypes.h +++ b/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 diff --git a/objects.h b/objects.h index c2d1eabe..4f54befc 100644 --- a/objects.h +++ b/objects.h @@ -13,6 +13,7 @@ # include "pyconfig.h" # include "pyptr.h" # include "boost/operators.hpp" +# include namespace py { @@ -36,8 +37,31 @@ class Tuple : public Object public: Tuple(std::size_t n = 0); explicit Tuple(Ptr p); + + template + Tuple(const std::pair& 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 + 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 + 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); diff --git a/py.cpp b/py.cpp index a64e27c5..9ac2490d 100644 --- a/py.cpp +++ b/py.cpp @@ -7,7 +7,11 @@ // producing this work. #include "py.h" -#include +#include +#include +#ifndef BOOST_NO_LIMITS +# include +#endif namespace py { @@ -82,11 +86,19 @@ T integer_from_python(PyObject* p, py::Type) { const long long_result = from_python(p, py::Type()); +#ifndef BOOST_NO_LIMITS try { return boost::numeric_cast(long_result); } catch(const boost::bad_numeric_cast&) +#else + if (static_cast(long_result) == long_result) + { + return static_cast(long_result); + } + else +#endif { char buffer[256]; const char message[] = "%ld out of range for %s"; @@ -103,12 +115,17 @@ template PyObject* integer_to_python(T value) { long value_as_long; - + +#ifndef BOOST_NO_LIMITS try { - value_as_long = boost::numeric_cast(value); + value_as_long = boost::numeric_cast(value); } catch(const boost::bad_numeric_cast&) +#else + value_as_long = static_cast(value); + if (value_as_long != value) +#endif { const char message[] = "value out of range for Python int"; PyErr_SetString(PyExc_ValueError, message); diff --git a/py.h b/py.h index 2cf89807..06365764 100644 --- a/py.h +++ b/py.h @@ -165,6 +165,11 @@ PyObject* to_python(boost::shared_ptr p) // inline implementations // +inline PyObject* to_python(double d) +{ + return PyFloat_FromDouble(d); +} + inline PyObject* to_python(long l) { return PyInt_FromLong(l); diff --git a/py_cpp.html b/py_cpp.html index c65f4a1b..03ff2677 100644 --- a/py_cpp.html +++ b/py_cpp.html @@ -18,12 +18,12 @@ Python.

    The source code for py_cpp, including a MSVC demo project is available - here. It has been tested against Python + here. 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 STLport standard library implementation and the library implementation which ships with the compiler. It has also been tested - against Python 2.0c1 with an unknown version of MSVC++ by Alex Martelli. + against Python 2.0c1 with MSVC++ 6sp4 by Alex Martelli. Py_cpp requires the Boost libraries, and is currently under formal review on the boost mailing list for @@ -116,5 +116,5 @@ express or implied warranty, and with no claim as to its suitability for any purpose.

    - Updated: Oct 15, 2000 + Updated: Oct 18, 2000 diff --git a/py_cpp_20001013.zip b/py_cpp_20001013.zip deleted file mode 100644 index 420d2bc4..00000000 Binary files a/py_cpp_20001013.zip and /dev/null differ diff --git a/subclass.cpp b/subclass.cpp index 3f1d25ec..3a133be0 100644 --- a/subclass.cpp +++ b/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 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::call_method(this, "__setslice__", start, finish, value); } +PyObject* Instance::add(PyObject* other) +{ + return Callback::call_method(this, "__add__", other); +} + +PyObject* Instance::subtract(PyObject* other) +{ + return Callback::call_method(this, "__sub__", other); +} + +PyObject* Instance::multiply(PyObject* other) +{ + return Callback::call_method(this, "__mul__", other); +} + +PyObject* Instance::divide(PyObject* other) +{ + return Callback::call_method(this, "__div__", other); +} + +PyObject* Instance::remainder(PyObject* other) +{ + return Callback::call_method(this, "__mod__", other); +} + +PyObject* Instance::divmod(PyObject* other) +{ + return Callback::call_method(this, "__divmod__", other); +} + +PyObject* Instance::power(PyObject* exponent, PyObject* modulus) +{ + if (as_object(modulus->ob_type) == Py_None) + return Callback::call_method(this, "__pow__", exponent); + else + return Callback::call_method(this, "__pow__", exponent, modulus); +} + +PyObject* Instance::negative() +{ + return Callback::call_method(this, "__neg__"); +} + +PyObject* Instance::positive() +{ + return Callback::call_method(this, "__pos__"); +} + +PyObject* Instance::absolute() +{ + return Callback::call_method(this, "__abs__"); +} + +int Instance::nonzero() +{ + return Callback::call_method(this, "__nonzero__"); +} + +PyObject* Instance::invert() +{ + return Callback::call_method(this, "__invert__"); +} + +PyObject* Instance::lshift(PyObject* other) +{ + return Callback::call_method(this, "__lshift__", other); +} + +PyObject* Instance::rshift(PyObject* other) +{ + return Callback::call_method(this, "__rshift__", other); +} + +PyObject* Instance::do_and(PyObject* other) +{ + return Callback::call_method(this, "__and__", other); +} + +PyObject* Instance::do_xor(PyObject* other) +{ + return Callback::call_method(this, "__xor__", other); +} + +PyObject* Instance::do_or(PyObject* other) +{ + return Callback::call_method(this, "__or__", other); +} + +int Instance::coerce(PyObject** x, PyObject** y) +{ + assert(this == *x); + + // Coerce must return a tuple + Tuple result(Callback::call_method(this, "__coerce__", *y)); + + *x = result[0].release(); + *y = result[1].release(); + return 0; +} + +PyObject* Instance::as_int() +{ + return Callback::call_method(this, "__int__"); +} + +PyObject* Instance::as_long() +{ + return Callback::call_method(this, "__long__"); +} + +PyObject* Instance::as_float() +{ + return Callback::call_method(this, "__float__"); +} + +PyObject* Instance::oct() +{ + return Callback::call_method(this, "__oct__"); +} + +PyObject* Instance::hex() +{ + return Callback::call_method(this, "__hex__"); +} + namespace { struct 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) diff --git a/subclass.h b/subclass.h index 4d745bc0..a442ca30 100644 --- a/subclass.h +++ b/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::instance_call(PyObject* instance, PyObject* args, PyObject* return Downcast(instance)->call(args, keywords); } +template +PyObject* Class::instance_number_add(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->add(other); +} + +template +PyObject* Class::instance_number_subtract(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->subtract(other); +} + +template +PyObject* Class::instance_number_multiply(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->multiply(other); +} + +template +PyObject* Class::instance_number_divide(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->divide(other); +} + +template +PyObject* Class::instance_number_remainder(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->remainder(other); +} + +template +PyObject* Class::instance_number_divmod(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->divmod(other); +} + +template +PyObject* Class::instance_number_power(PyObject* instance, PyObject* exponent, PyObject* modulus) const +{ + return Downcast(instance)->power(exponent, modulus); +} + +template +PyObject* Class::instance_number_negative(PyObject* instance) const +{ + return Downcast(instance)->negative(); +} + +template +PyObject* Class::instance_number_positive(PyObject* instance) const +{ + return Downcast(instance)->positive(); +} + +template +PyObject* Class::instance_number_absolute(PyObject* instance) const +{ + return Downcast(instance)->absolute(); +} + +template +int Class::instance_number_nonzero(PyObject* instance) const +{ + return Downcast(instance)->nonzero(); +} + +template +PyObject* Class::instance_number_invert(PyObject* instance) const +{ + return Downcast(instance)->invert(); +} + +template +PyObject* Class::instance_number_lshift(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->lshift(other); +} + +template +PyObject* Class::instance_number_rshift(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->rshift(other); +} + +template +PyObject* Class::instance_number_and(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->do_and(other); +} + +template +PyObject* Class::instance_number_xor(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->do_xor(other); +} + +template +PyObject* Class::instance_number_or(PyObject* instance, PyObject* other) const +{ + return Downcast(instance)->do_or(other); +} + +template +int Class::instance_number_coerce(PyObject* instance, PyObject** x, PyObject** y) const +{ + return Downcast(instance)->coerce(x, y); +} + +template +PyObject* Class::instance_number_int(PyObject* instance) const +{ + return Downcast(instance)->as_int(); +} + +template +PyObject* Class::instance_number_long(PyObject* instance) const +{ + return Downcast(instance)->as_long(); +} + +template +PyObject* Class::instance_number_float(PyObject* instance) const +{ + return Downcast(instance)->as_float(); +} + +template +PyObject* Class::instance_number_oct(PyObject* instance) const +{ + return Downcast(instance)->oct(); +} + +template +PyObject* Class::instance_number_hex(PyObject* instance) const +{ + return Downcast(instance)->hex(); +} + template Dict& Class::dict() { diff --git a/test_extclass.py b/test_extclass.py index a458dbb0..8861aa06 100644 --- a/test_extclass.py +++ b/test_extclass.py @@ -130,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. @@ -264,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): @@ -412,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 "", line 1, in ? + TypeError: illegal argument type for built-in operation + >>> c.callback(1) + 2 + >>> c.callback('foo') + 'foo 1' + + >>> import types + >>> class RedefineCallback(CallbackTest): + ... def callback(self, x): + ... if type(x) is types.IntType: + ... return x - 2 + ... else: + ... return CallbackTest.callback(self,x) + ... + >>> r = RedefineCallback() + >>> r.callback(1) + -1 + >>> r.callback('foo') + 'foo 1' + >>> r.testCallback('foo') + Traceback (innermost last): + File "", line 1, in ? + TypeError: illegal argument type for built-in operation + >>> r.testCallback(1) + -1 + >>> testCallback(r, 1) + -1 ''' from demo import * diff --git a/todo.txt b/todo.txt index 80514798..51967244 100644 --- a/todo.txt +++ b/todo.txt @@ -7,14 +7,15 @@ 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 - I don't think this is possible Handle polymorphism (passing a Wrapped 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 +Make abstract classes non-instantiable (?) +Much more testing, especially of things in objects.h +Support for Python LONG types in Objects.h Documentation: building @@ -31,6 +32,7 @@ Documentation: differences between Python classes and ExtensionClasses additional capabilities of ExtensionClasses + slice adjustment exception handling diff --git a/vc6_prj/vc6_prj.opt b/vc6_prj/vc6_prj.opt new file mode 100644 index 00000000..543c06ad Binary files /dev/null and b/vc6_prj/vc6_prj.opt differ