inheritance, multiple inheritance and the weaklist and instance dictionaries

Rouslan Korneychuk rouslank at msn.com
Wed Feb 9 18:11:26 EST 2011


On 02/09/2011 04:58 PM, Carl Banks wrote:
> On Feb 9, 1:14 pm, Rouslan Korneychuk<rousl... at msn.com>  wrote:
>> On 02/09/2011 02:42 PM, Carl Banks wrote:
>>> This is the only case I can think of where the
>>> layout conflict would be caused by a type setting tp_dictoffset.
>>
>> No, actually I have code that is roughly equivalent to the following
>> pseudocode:
>>
>> class _internal_class(object):
>>       __slots__ = ()
>>
>> class BaseA(_internal_class):
>>       __slots__ = (some_data,...,__weaklist__,__dict__)
>>
>> class BaseB(BaseA):
>>       __slots__ = (some_data,...,__weaklist__,__dict__)
>>
>> class BaseC(_internal_class):
>>       __slots__ = (some_other_data,...,__weaklist__,__dict__)
>>
>> class Derived(BaseB,BaseC):
>>       __slots__ = (combined_data,...,__weaklist__,__dict__)
>
> Ah, ok.  So BaseA sticks weaklist and dict into a certain layout
> location.  BaseB gets the same location because it has the same
> layout.
> BaseC sticks weaklist and dict into a different location.  Derived
> then can't reconcile it because dict and weakref are in different
> locations.

That doesn't explain why changing _internal_class to:
     class _internal_class(object):
         __slots__ = (__weaklist__,__dict__)
makes it work. Also, why does Derived need to reconcile anything if I'm 
telling it where the dictionaries are in the new class? Doesn't CPython 
calculate where, for example, weaklist is with what is essentially:
     obj_instance + obj_instance->ob_type->tp_weaklistoffset
(at least when tp_weaklistoffset is non-negative)? If so, then it 
wouldn't matter what the base classes look like.


> "some_data" a proper subset of "some_other_data", right?  (If it isn't
> you have worse problems than dict and weakreflist.)

Not at all. The point of this, is to allow C++ multiple inheritance to 
be mapped to Python multiple inheritance. I solve this issue as follows: 
All attributes of base classes of multiply-inheriting classes are turned 
into properties. Any time the wrapped C++ object needs to be accessed, 
the PyObject self variable's type is compared to every derived type that 
also inherits from another type. If it matches a derived type, a 
reinterpret_cast is performed on the C++ object to the derived C++ type, 
and then an implicit cast is performed to the intended type, which 
allows the proper pointer fix-up to happen. Here is the actual function 
that does that for BaseA:

BaseA &get_base_BaseA(PyObject *x,bool safe = true) {
 
if(reinterpret_cast<long>(static_cast<BaseA*>(reinterpret_cast<Derived*>(1))) 
!= 1 &&
 
PyObject_IsInstance(x,reinterpret_cast<PyObject*>(get_obj_DerivedType())))
         return cast_base_Derived(x);

     if(UNLIKELY(safe && 
!PyObject_IsInstance(x,reinterpret_cast<PyObject*>(get_obj_BaseAType())))) {
         PyErr_SetString(PyExc_TypeError,"object is not an instance of 
BaseA");
         throw py_error_set();
     }
 
assert(PyObject_IsInstance(x,reinterpret_cast<PyObject*>(get_obj_BaseAType())));
     return cast_base_BaseA(x);
}

(cast_base_X checks how the C++ object is stored and retrieves a 
reference to it. The 
"reinterpret_cast<long>(static_cast<BaseA*>(reinterpret_cast<Derived*>(1))) 
!= 1" part is an optimization trick. It checks to see if a pointer 
fix-up is even necessary. If not, that 'if' statement will be subject to 
dead code removal.)

Of course, if this was hand-written code, such a method would be very 
brittle and would be a nightmare to maintain, but it's generated by a 
Python program and only needs to be correct.


> Don't set tp_dictoffset and tp_weakrefoffset in any of the bases.  Set
> them only in Derived.  If you need to instantiate a Base, make a
> trivial dervied class for it.

That would mean calling isinstance(derived_instance,BaseType) would 
return false. In any case, I'm writing a general-purpose tool and not 
trying to solve a specific problem. If I can't do what I'm trying to do 
here, I can just make the program not allow multiply-inheriting classes 
to derive (directly or indirectly) from classes that have a different 
combination of weaklist and dict (having both will be the default 
anyway, so this is a special case).




More information about the Python-list mailing list