diff --git a/newtypes.cpp b/newtypes.cpp index 3855791f..39cff270 100644 --- a/newtypes.cpp +++ b/newtypes.cpp @@ -13,79 +13,12 @@ #include #include #include +#include #include #include "objects.h" namespace py { -namespace detail { -UniquePodSet& UniquePodSet::instance() -{ - static UniquePodSet me; - return me; -} - -struct UniquePodSet::Compare -{ - bool operator()(const std::pair& x1, - const std::pair& x2) const - { - const std::ptrdiff_t n1 = x1.second - x1.first; - const std::ptrdiff_t n2 = x2.second - x2.first; - return n1 < n2 || n1 == n2 && PY_CSTD_::memcmp(x1.first, x2.first, n1) < 0; - } -}; - -const void* UniquePodSet::get_element(const void* buffer, std::size_t size) -{ - const Holder element(static_cast(buffer), - static_cast(buffer) + size); - - const Storage::iterator found - = std::lower_bound(m_storage.begin(), m_storage.end(), element, Compare()); - - if (found != m_storage.end() && !Compare()(element, *found)) - return found->first; - - std::size_t length = element.second - element.first; - char* base_address = new char[length]; - try { - PY_CSTD_::memcpy(base_address, element.first, length); - Holder new_element(base_address, base_address + length); - m_storage.insert(found, new_element); - } - catch(...) { - delete[] base_address; - throw; - } - return base_address; -} - -UniquePodSet::~UniquePodSet() -{ - for (Storage::const_iterator p = m_storage.begin(), finish = m_storage.end(); - p != finish; ++p) - { - delete[] const_cast(p->first); - } -} -} // namespace detail - -template -static MethodStruct* enable_method(const MethodStruct* base, MemberPtr p, Fn f) -{ - MethodStruct new_value; - - if (base != 0) - new_value = *base; - else - PY_CSTD_::memset(&new_value, 0, sizeof(PyMappingMethods)); - - new_value.*p = f; - - return const_cast(detail::UniquePodSet::instance().get(new_value)); -} - namespace { PyObject* call(PyObject* instance, PyObject* (TypeObjectBase::*f)(PyObject*) const) @@ -214,7 +147,7 @@ int call_length_function(PyObject* instance, int (TypeObjectBase::*f)(PyObject*) } } -} +} // anonymous namespace extern "C" { @@ -457,151 +390,173 @@ static PyObject* do_instance_nb_hex(PyObject* instance) return call(instance, &TypeObjectBase::instance_number_hex); } +} // extern "C" + +namespace +{ + +#define ENABLE_GENERAL_CAPABILITY(field) \ + case TypeObjectBase::field: \ + dest->tp_##field = &do_instance_##field; \ + break + +bool add_capability_general(TypeObjectBase::Capability capability, PyTypeObject* dest) +{ + assert(dest != 0); + + switch(capability) + { + ENABLE_GENERAL_CAPABILITY(hash); + ENABLE_GENERAL_CAPABILITY(call); + ENABLE_GENERAL_CAPABILITY(str); + ENABLE_GENERAL_CAPABILITY(getattr); + ENABLE_GENERAL_CAPABILITY(setattr); + ENABLE_GENERAL_CAPABILITY(compare); + ENABLE_GENERAL_CAPABILITY(repr); + default: + return false; + } + return true; } +template +void create_method_table_if_null(T *& table) +{ + if(table == 0) + { + table = new T; + memset(table, 0, sizeof(T)); + } +} + +#define ENABLE_MAPPING_CAPABILITY(field) \ + case TypeObjectBase::mapping_##field: \ + create_method_table_if_null(dest); \ + dest->mp_##field = &do_instance_mp_##field; \ + break + +bool add_capability_mapping(TypeObjectBase::Capability capability, PyMappingMethods*& dest) +{ + switch(capability) + { + ENABLE_MAPPING_CAPABILITY(length); + ENABLE_MAPPING_CAPABILITY(subscript); + ENABLE_MAPPING_CAPABILITY(ass_subscript); + default: + return false; + } + return true; +} + +#define ENABLE_SEQUENCE_CAPABILITY(field) \ + case TypeObjectBase::sequence_##field: \ + create_method_table_if_null(dest); \ + dest->sq_##field = &do_instance_sq_##field; \ + break + +bool add_capability_sequence(TypeObjectBase::Capability capability, PySequenceMethods*& dest) +{ + switch(capability) + { + ENABLE_SEQUENCE_CAPABILITY(length); + ENABLE_SEQUENCE_CAPABILITY(item); + ENABLE_SEQUENCE_CAPABILITY(ass_item); + ENABLE_SEQUENCE_CAPABILITY(concat); + ENABLE_SEQUENCE_CAPABILITY(repeat); + ENABLE_SEQUENCE_CAPABILITY(slice); + ENABLE_SEQUENCE_CAPABILITY(ass_slice); + default: + return false; + } + return true; +} + +#define ENABLE_NUMBER_CAPABILITY(field) \ + case TypeObjectBase::number_##field: \ + create_method_table_if_null(dest); \ + dest->nb_##field = &do_instance_nb_##field; \ + break + +bool add_capability_number(TypeObjectBase::Capability capability, PyNumberMethods*& dest) +{ + switch(capability) + { + ENABLE_NUMBER_CAPABILITY(add); + ENABLE_NUMBER_CAPABILITY(subtract); + ENABLE_NUMBER_CAPABILITY(multiply); + ENABLE_NUMBER_CAPABILITY(divide); + ENABLE_NUMBER_CAPABILITY(remainder); + ENABLE_NUMBER_CAPABILITY(divmod); + ENABLE_NUMBER_CAPABILITY(power); + ENABLE_NUMBER_CAPABILITY(negative); + ENABLE_NUMBER_CAPABILITY(positive); + ENABLE_NUMBER_CAPABILITY(absolute); + ENABLE_NUMBER_CAPABILITY(nonzero); + ENABLE_NUMBER_CAPABILITY(invert); + ENABLE_NUMBER_CAPABILITY(lshift); + ENABLE_NUMBER_CAPABILITY(rshift); + ENABLE_NUMBER_CAPABILITY(and); + ENABLE_NUMBER_CAPABILITY(xor); + ENABLE_NUMBER_CAPABILITY(or); + ENABLE_NUMBER_CAPABILITY(coerce); + ENABLE_NUMBER_CAPABILITY(int); + ENABLE_NUMBER_CAPABILITY(long); + ENABLE_NUMBER_CAPABILITY(float); + ENABLE_NUMBER_CAPABILITY(oct); + ENABLE_NUMBER_CAPABILITY(hex); + default: + return false; + } + return true; +} + +#define ENABLE_BUFFER_CAPABILITY(field) \ + case TypeObjectBase::buffer_##field: \ + create_method_table_if_null(dest); \ + dest->bf_##field = &do_instance_bf_##field; \ + break + +bool add_capability_buffer(TypeObjectBase::Capability capability, PyBufferProcs*& dest) +{ + switch(capability) + { + // nothing defined yet + + default: + return false; + } + return true; +} + +} // anonymous namespace + namespace detail { -template struct category_type; - -#define DECLARE_CAPABILITY_TYPE(field, sub_structure) \ - template <> \ - struct category_type<(PY_OFFSETOF(PyTypeObject, tp_as_##field))> \ - { \ - typedef sub_structure type; \ - } - -#define CAPABILITY(field) \ - { PY_OFFSETOF(PyTypeObject, tp_##field), 0, Dispatch(do_instance_##field), 0, -1 } - -#define CAPABILITY2(category, field) \ - { PY_OFFSETOF(PyTypeObject, tp_as_##category), \ - PY_OFFSETOF(category_type::type, field), \ - Dispatch(do_instance_##field), \ - sizeof(category_type::type), \ - PY_OFFSETOF(AllMethods, category) \ - } - -DECLARE_CAPABILITY_TYPE(mapping, PyMappingMethods); -DECLARE_CAPABILITY_TYPE(sequence, PySequenceMethods); -DECLARE_CAPABILITY_TYPE(number, PyNumberMethods); - -const CapabilityEntry capabilities[] = { - CAPABILITY(hash), - CAPABILITY(call), - CAPABILITY(str), - CAPABILITY(getattr), - CAPABILITY(setattr), - CAPABILITY(compare), - CAPABILITY(repr), - - CAPABILITY2(mapping, mp_length), - CAPABILITY2(mapping, mp_subscript), - CAPABILITY2(mapping, mp_ass_subscript), - - CAPABILITY2(sequence, sq_length), - CAPABILITY2(sequence, sq_item), - CAPABILITY2(sequence, sq_ass_item), - CAPABILITY2(sequence, sq_concat), - CAPABILITY2(sequence, sq_repeat), - CAPABILITY2(sequence, sq_slice), - CAPABILITY2(sequence, sq_ass_slice), - - 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); void add_capability( - std::size_t n, - PyTypeObject* dest_, - AllMethods& all_methods) + TypeObjectBase::Capability capability, + PyTypeObject* dest_) { - assert(n < PY_ARRAY_LENGTH(capabilities)); - const CapabilityEntry& c = capabilities[n]; + if(add_capability_general(capability, dest_)) + return; + if(add_capability_mapping(capability, dest_->tp_as_mapping)) + return; + if(add_capability_sequence(capability, dest_->tp_as_sequence)) + return; + if(add_capability_number(capability, dest_->tp_as_number)) + return; + if(add_capability_buffer(capability, dest_->tp_as_buffer)) + return; - char** const dest = reinterpret_cast( - reinterpret_cast(dest_) + c.offset1); - - if (c.substructure_size == 0) - { - *reinterpret_cast(dest) = c.dispatch; - } - else - { - *dest = reinterpret_cast(&all_methods) + c.allmethods_offset; - *reinterpret_cast(*dest + c.offset2) = c.dispatch; - } + // no one recognized the capability + throw std::runtime_error("py::detail::add_capability(): unknown capability"); } } // namespace detail -namespace { - union SubStructures { - PyMappingMethods mapping; - PySequenceMethods sequence; - PyNumberMethods number; - PyBufferProcs buffer; - }; -} - void TypeObjectBase::enable(TypeObjectBase::Capability capability) { - using detail::capabilities; - using detail::CapabilityEntry; - using detail::Dispatch; - - assert((std::size_t)capability < PY_ARRAY_LENGTH(capabilities)); - const CapabilityEntry& c = capabilities[capability]; - - PyTypeObject* const me = this; - char* const base_address = reinterpret_cast(me); - - if (c.substructure_size == 0) - { - // Stuff the dispatch function directly into the PyTypeObject - *reinterpret_cast(base_address + c.offset1) = c.dispatch; - return; - } - - const char*& sub_structure = *reinterpret_cast(base_address + c.offset1); - - // Initialize this POD union with the current state-of-the-world - SubStructures sub; - if (sub_structure == 0) - PY_CSTD_::memset(&sub, 0, c.substructure_size); - else - PY_CSTD_::memcpy(&sub, sub_structure, c.substructure_size); - - // Stuff the dispatch function into the sub-structure - *reinterpret_cast(reinterpret_cast(&sub) + c.offset2) = c.dispatch; - - // Retrieve the unique dynamically-allocated substructure and stuff it into - // the PyTypeObject. - sub_structure = static_cast( - detail::UniquePodSet::instance().get_element(&sub, c.substructure_size)); + detail::add_capability(capability, this); } - TypeObjectBase::TypeObjectBase(PyTypeObject* t) : PythonType(t) { @@ -826,6 +781,10 @@ PyObject* TypeObjectBase::instance_number_hex(PyObject*) const TypeObjectBase::~TypeObjectBase() { + delete tp_as_mapping; + delete tp_as_sequence; + delete tp_as_number; + delete tp_as_buffer; } } diff --git a/newtypes.h b/newtypes.h index 6e757a64..82eaf53a 100644 --- a/newtypes.h +++ b/newtypes.h @@ -41,13 +41,14 @@ class TypeObjectBase : public PythonType virtual ~TypeObjectBase(); public: - enum Capability - { + enum Capability { hash, call, str, getattr, setattr, compare, repr, + mapping_length, mapping_subscript, mapping_ass_subscript, + sequence_length, sequence_item, sequence_ass_item, sequence_concat, sequence_repeat, sequence_slice, sequence_ass_slice, - + number_add, number_subtract, number_multiply, number_divide, number_remainder, number_divmod, number_power, number_negative, number_positive, number_absolute, number_nonzero, number_invert, @@ -296,57 +297,8 @@ PyObject* Reprable::instance_repr(PyObject* instance) const namespace detail { - struct AllMethods { - PyMappingMethods mapping; - PySequenceMethods sequence; - PyNumberMethods number; - PyBufferProcs buffer; - }; - - typedef void (*Dispatch)(); - struct CapabilityEntry - { - std::size_t offset1; - std::size_t offset2; - Dispatch dispatch; - std::size_t substructure_size; - int allmethods_offset; - }; - - extern const CapabilityEntry capabilities[]; - extern const std::size_t num_capabilities; - - void add_capability(std::size_t index, PyTypeObject* dest, AllMethods&); - - class UniquePodSet - { - typedef std::pair Holder; - typedef std::vector Storage; - public: - static UniquePodSet& instance(); - ~UniquePodSet(); - - template - T* get(const T& x) - { - char* base = const_cast( - reinterpret_cast(&x)); - return const_cast( - static_cast( - get_element(base, sizeof(T)))); - } - - const void* get_element(const void* buffer, std::size_t size); - - private: - struct Compare; - - private: - UniquePodSet() {} // singleton - - private: - Storage m_storage; - }; + void add_capability(TypeObjectBase::Capability capability, + PyTypeObject* dest); # define PY_ARRAY_LENGTH(a) \ (sizeof(::py::detail::countof_validate(a, &(a))) ? sizeof(a) / sizeof((a)[0]) : 0) diff --git a/subclass.cpp b/subclass.cpp index 28a4a1cf..172cd467 100644 --- a/subclass.cpp +++ b/subclass.cpp @@ -728,9 +728,6 @@ namespace { // Enable any special methods which are enabled in the base class. void enable_special_methods(py::detail::ClassBase* derived, const Tuple& bases, const Dict& name_space) { - detail::AllMethods all_methods; - PY_CSTD_::memset(&all_methods, 0, sizeof(all_methods)); - for (std::size_t i = 0; i < bases.size(); ++i) { PyObject* base = bases[i].get(); @@ -742,7 +739,7 @@ namespace { Ptr::null_ok); PyErr_Clear(); if (attribute.get() != 0 && PyCallable_Check(attribute.get())) - detail::add_capability(n, derived, all_methods); + detail::add_capability(enablers[n].capability, derived); } } @@ -759,24 +756,10 @@ namespace { { if (is_prefix(enablers[i].name + 2, name + 2)) { - detail::add_capability(enablers[i].capability, derived, all_methods); + detail::add_capability(enablers[i].capability, derived); } } } - - // Now replace those pointers with a persistent copy - using detail::UniquePodSet; - if (derived->tp_as_buffer) - derived->tp_as_buffer = UniquePodSet::instance().get(*derived->tp_as_buffer); - - if (derived->tp_as_number) - derived->tp_as_number = UniquePodSet::instance().get(*derived->tp_as_number); - - if (derived->tp_as_sequence) - derived->tp_as_sequence = UniquePodSet::instance().get(*derived->tp_as_sequence); - - if (derived->tp_as_mapping) - derived->tp_as_mapping = UniquePodSet::instance().get(*derived->tp_as_mapping); } void add_current_module_name(Dict& name_space) diff --git a/test_extclass.py b/test_extclass.py index 9304f9d3..1cf64382 100644 --- a/test_extclass.py +++ b/test_extclass.py @@ -963,17 +963,15 @@ test inheritB2 TypeError: bad operand type(s) for pow() Test operator export to a subclass - >>> class Foo(Int): + >>> class IntDerived(Int): ... def __init__(self, i): ... Int.__init__(self, i) ... def __str__(self): - ... return 'Foo: ' + str(self.i()) - ... def __coerce__(self, other): - ... return Int.__coerce__(self, other) + ... return 'IntDerived: ' + str(self.i()) ... - >>> f = Foo(3) + >>> f = IntDerived(3) >>> str(f) - 'Foo: 3' + 'IntDerived: 3' >>> j = f * f >>> j.i() 9