Closed lukevalenty closed 1 month ago
Yes, Python ref-counting rules work differently for deeply nested statements than C++ rules for temporaries. In the case of C++, temporaries are guaranteed to live for the duration of the statement, so you can write things like the above, but in Python this doesn't (always) work: the temporary reaches a ref-count of 0 before all C++ (!) references to its internals are gone.
Note that for methods, cppyy captures the common case where the address of the return value falls within the memory extend of the object it is called on, by putting a hardref from the result back onto the object. Here, however, get
is a global function and so that case isn't caught. It would be simple to write a pythonization specifically for get
to fix that, though, as a back-reference is always warranted for non-builtin types.
This is more detailed explanation of what's going on with the temporaries: https://pybind11.readthedocs.io/en/stable/advanced/functions.html#return-value-policies
Bummer! What would the pythonization for std::get
look like?
The following probably isn't very efficient (as is done in Python, with so many indirections), but then again, get<>
isn't all that efficient to begin with. It would probably warrant its own specialized class at some point, with reusable calls based on memory offsets instead of wrapper functions (that would also solve the problems I have with it on Windows). In any case, this would be one way to handle the life times:
import cppyy
import cppyy.types
class SafeGet:
def __init__(self, get):
self._get = get
def __call__(self, tup):
res = self._get.__call__(tup)
if isinstance(res, cppyy.types.Instance):
res.__get_keepalive = tup
return res
def __getitem__(self, *args):
res = self._get.__getitem__(*args)
return SafeGet(res)
cppyy.gbl.std.get = SafeGet(cppyy.gbl.std.get)
Thanks!
I found a strange data corruption bug that seems to depend on whether a newly constructed cpp object is passed directly into a cpp function or if it is stored locally in a python variable first. I have both a passing and failing test case below. I don't think this is specific to
std::tuple
because it affected our own tuple implementation instdx
.I've left in my own debugging print statements that show the values contained within the inner tuples in both python and cpp. As you can see in the test output, one of the values gets corrupted: