wlav / cppyy

Other
400 stars 41 forks source link

Inconsistent base class behavior in vector #67

Closed mkolin closed 2 years ago

mkolin commented 2 years ago

The behavior of a vector of base pointers may behave differently depending on how it gets called or how a base class is defined. It will sometimes return the base and sometimes return the derived type.

This falls into at least three cases:

All three of these cases will return the base if there is no vtable. This is fine, it just threw me when constructing the tests. Also, the behavior has somewhat changed since 1.6.1.

Example:

import cppyy

cppyy.cppdef('''
struct Base1 { };
struct Derived1 : Base1 { };
struct Base2 { virtual ~Base2() noexcept = default; };
struct Derived2 : Base2 { };

Derived1 d1;
Derived2 d2;
std::vector<const Base1*> vec1 { &d1 };
std::vector<const Base2*> vec2 { &d2 };
''')

from cppyy.gbl import std, Base1, Derived1, Base2, Derived2, vec1, vec2

print(f'vec1[0] is {vec1[0]}')
print(f'vec2[0] is {vec2[0]}')

print(f'list(vec1)[0] is {list(vec1)[0]}')
print(f'list(vec2)[0] is {list(vec2)[0]}')

print(f'[d for d in vec1 if isinstance(d, Derived1)] is {[d for d in vec1 if isinstance(d, Derived1)]}')
print(f'[d for d in vec2 if isinstance(d, Derived2)] is {[d for d in vec2 if isinstance(d, Derived2)]}')

In 2.3.1 we get this output:

vec1[0] is <cppyy.gbl.Base1 object at 0x7f28f23b7010>
vec2[0] is <cppyy.gbl.Derived2 object at 0x7f28f23b7048>
list(vec1)[0] is <cppyy.gbl.Base1* object at 0x7f28f23b7010>
list(vec2)[0] is <cppyy.gbl.Base2* object at 0x7f28f23b7048>
[d for d in vec1 if isinstance(d, Derived1)] is []
[d for d in vec2 if isinstance(d, Derived2)] is []

In 1.6.1 we get this output:

vec1[0] is <cppyy.gbl.Base1 object at 0x7f56d33d5010>
vec2[0] is <cppyy.gbl.Derived2 object at 0x7f56d33d5048>
list(vec1)[0] is <cppyy.gbl.Base1 object at 0x7f56d33d5010>
list(vec2)[0] is <cppyy.gbl.Derived2 object at 0x7f56d33d5048>
[d for d in vec1 if isinstance(d, Derived1)] is []
[d for d in vec2 if isinstance(d, Derived2)] is [<cppyy.gbl.Derived2 object at 0x7f56d33d5048>]

Any help would be appreciated. Thanks!

mkolin commented 2 years ago

This may be related to https://github.com/wlav/cppyy/issues/66

wlav commented 2 years ago

Yes, this is the same as #66. The point being that T* can be auto-downcasted, but const T*& (the actual return type of vector indexing) can not. After all, in the latter case, if the pointer is updated, and that change propagated through the reference, then the new instance pointed to by the base class pointer could be of a different derived type. This one, like #66, requires some thinking as clearly the correct transliteration doesn't line up with most probable expected behavior.

wlav commented 2 years ago

Fixed in repo by distinguishing the behaviors between const and non-const. It fixes this report and doesn't break any tests, but there may still be corner cases lurking.

wlav commented 2 years ago

Released with 2.4.0 and its dependencies.