EDIT: originally this was a feature request to add weak reference support to the LowLevelView type. After discussion it seems like adding support for a __dict__ is a better solution.
I have a specific use case for this I will explain below. However I'm open to suggestions for alternative solutions. I looked into using special variables and couldn't come up with a solution, though I must admit I'm not very confident about my understanding of what these variables do.
Suppose I have C++ functions that return std::vector. The goal is to turn these into numpy arrays without copying any data. The challenge is I want to be able to keep the underlying C++ objects alive until no python object is referencing the LowLevelView anymore.
To illustrate the issue, consider this code:
import cppyy
import numpy as np
cppyy.cppdef("""
#include <numeric>
static std::vector<int> VectorFunc(size_t len)
{
std::vector<int> ret(len);
std::iota(ret.begin(), ret.end(), 0);
return ret;
}
""")
VectorFunc = cppyy.gbl.VectorFunc
def vector_func(size):
vec = VectorFunc(size)
arr = np.frombuffer(vec.data(), dtype=np.int32)
# at this point, arr is completely valid because we still have a reference to vec
# arr.base is a LowLevelView object.
print(arr)
return arr
arr = vector_func(5)
print(arr)
Output:
[0 1 2 3 4]
[-1689064896 41504 499 0 4]
We know we need to keep the C++ vector alive for as long as anything is referencing the LowLevelView object pointing to vec.data(). One way to do this is to wrap the LowLevelView object with a small wrapper class that also stores a reference to vec and make sure arr.base points to an instance of this class. The issue is this wrapper class needs to "forward" the buffer protocol, i.e. it needs to present the buffer protocol from its LowLevelView member to its callers. I don't think this is currently possible. PEP688 may change this starting in 3.12.
Another way to achieve the same behavior would be to keep a reference to vec until the last reference to the LowLevelView goes out of scope. To that end, we could keep a dictionary where the keys are weak references to LowLevelViews and the values are the corresponding std::vectors. Continuing from the previous code:
import weakref
cpp_objects = {}
def vector_func2(size):
def callback(x):
del cpp_objects[x]
vec = VectorFunc(size)
llv = vec.data()
wr = weakref.ref(llv, callback)
cpp_objects[wr] = vec
return np.frombuffer(llv, dtype=np.int32)
vector_func2(5)
I think this would do the trick, but it requires LowLevelView to support weak referencing.
File "/Users/tomas/repo/oct100/python/cpycppyy_issue1.py", line 46, in <module>
vector_func2(5)
File "/Users/tomas/repo/oct100/python/cpycppyy_issue1.py", line 42, in vector_func2
wr = weakref.ref(llv, callback)
TypeError: cannot create weak reference to 'cppyy.LowLevelView' object
EDIT: originally this was a feature request to add weak reference support to the LowLevelView type. After discussion it seems like adding support for a
__dict__is a better solution.I have a specific use case for this I will explain below. However I'm open to suggestions for alternative solutions. I looked into using special variables and couldn't come up with a solution, though I must admit I'm not very confident about my understanding of what these variables do.
Suppose I have C++ functions that return
std::vector. The goal is to turn these into numpy arrays without copying any data. The challenge is I want to be able to keep the underlying C++ objects alive until no python object is referencing the LowLevelView anymore.To illustrate the issue, consider this code:
Output:
We know we need to keep the C++ vector alive for as long as anything is referencing the LowLevelView object pointing to
vec.data(). One way to do this is to wrap the LowLevelView object with a small wrapper class that also stores a reference to vec and make surearr.basepoints to an instance of this class. The issue is this wrapper class needs to "forward" the buffer protocol, i.e. it needs to present the buffer protocol from its LowLevelView member to its callers. I don't think this is currently possible. PEP688 may change this starting in 3.12.Another way to achieve the same behavior would be to keep a reference to vec until the last reference to the LowLevelView goes out of scope. To that end, we could keep a dictionary where the keys are weak references to LowLevelViews and the values are the corresponding std::vectors. Continuing from the previous code:
I think this would do the trick, but it requires LowLevelView to support weak referencing.