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 28651eb8..5c8b44b5 100644 --- a/extclass.cpp +++ b/extclass.cpp @@ -239,43 +239,44 @@ ExtensionClassBase::ExtensionClassBase(const char* name) 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 * ExtensionClassBase::try_class_conversions(InstanceHolderBase* object) const { - void * result = try_derived_class_conversions(object); + void* result = try_derived_class_conversions(object); if(result) return result; - result = try_base_class_conversions(object); - return result; + + return try_base_class_conversions(object); } -void * ExtensionClassBase::try_base_class_conversions(InstanceHolderBase * object) const +void* ExtensionClassBase::try_base_class_conversions(InstanceHolderBase* object) const { - void * result = 0; - for(int i=0; iconvert_from_holder(object); - if(result) - return (*base_classes()[i].convert)(result); - result = base_classes()[i].class_object->try_base_class_conversions(object); - if(result) - return (*base_classes()[i].convert)(result); + + void* result1 = base_classes()[i].class_object->convert_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 +void* ExtensionClassBase::try_derived_class_conversions(InstanceHolderBase* object) const { - void * result = 0; - for(int i=0; iconvert_from_holder(object); - if(result) - return (*derived_classes()[i].convert)(result); - result = derived_classes()[i].class_object->try_derived_class_conversions(object); - if(result) - return (*derived_classes()[i].convert)(result); + void* result1 = derived_classes()[i].class_object->convert_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; } diff --git a/extclass_demo.cpp b/extclass_demo.cpp index 59e8099f..18050f7d 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 { @@ -476,6 +477,50 @@ 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 */ @@ -501,6 +546,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()); 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 5458bcf6..ec8839a8 100644 --- a/newtypes.h +++ b/newtypes.h @@ -44,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); @@ -77,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.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/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 0f2366b5..778f8519 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&); @@ -98,6 +123,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; @@ -322,6 +372,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 1ef74a7c..648b3e81 100644 --- a/test_extclass.py +++ b/test_extclass.py @@ -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): 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