Hi,
Motivated by an enquiry by a Numba user (
https://github.com/numba/numba/issues/1443 ), I found that it's not
presently possible to pass an object supporting the buffer protocol
directly to a pointer parameter in CFFI, though I did find this mention
that eventually support for this may be added:
https://groups.google.com/forum/#!msg/python-cffi/9uc6aqX7z0Q/Q8cb8BVtt-IJI tried experimenting a little with this to see if I could make steps
towards its implementation, using the following code as a test:
from numba import cffi_support, jit
import numpy as np
import cffi
# Create the vectormaths module
defs = "void vsSin(int n, float* x, float* y);"
source = """\
void vsSin(int n, float* x, float* y) {
int i;
for (i=0; i<n; i++)
y[i] = sin(x[i]);
}"""
ffi = cffi.FFI()
ffi.set_source('vectormaths', source)
ffi.cdef(defs, override=True)
ffi.compile()
# Import the compiled module
import vectormaths
cffi_support.register_module(vectormaths)
vsSin = vectormaths.lib.vsSin
# Pass an object supporting the buffer protocol to a float*
# Not currently supported
def pass_buffer_to_ptr(x):
y = np.empty_like(x)
vsSin(len(x), x, y)
return y
x = np.arange(10).astype(np.float32)
pass_buf_y = pass_buffer_to_ptr(x)
# Use ffi.from_buffer. Currently supported
def from_buffer_to_ptr(x):
y = np.empty_like(x)
vsSin(len(x), ffi.from_buffer(x), ffi.from_buffer(y))
return y
x = np.arange(10).astype(np.float32)
from_buf_y = from_buffer_to_ptr(x)
# These two versions do produce the same thing
assert np.all(pass_buf_y == from_buf_y)
print(pass_buf_y)
print(from_buf_y)Presently calling the pass_buffer_to_ptr function fails, but I was able
to get it to work on CPython with a small modification to the
_prepare_pointer_call_argument function:
https://bitbucket.org/gmarkall/cffi/commits/8aa7cd36813529838d4001c666650dea38447117Aside from some validation to check the format of the buffer is suitable
for the type of the parameter being passed to, I see also a couple of
other difficulties with proceeding:
* The
Py_buffer view that is now created gets leaked, as I can't see a
clean way to "remember" it through the interface of
_prepare_pointer_call_argument so that it can be cleaned up after the
call to the actual C function. One possibility I could see would be to
return a particular sentinel value (e.g. -1 for general failure, -2 for
"failure, but supports buffer protocol", as opposed to the present
situation where any negative number seems to indicate failure) so that
the code to create the
Py_buffer can be inside the calling function (in
my example,
_cffi_f_vsSin), and therefore it can also clean up more
easily after the call to the C function. Would this be a suitable way to
go?
* I don't have any experience with PyPy, so I'd expect to struggle with
making any progress on support for this feature with PyPy.
Any guidance on how to proceed in terms of the right way to
structure/implement changes to make this into an acceptable contribution
to cffi would be appreciated. Alternately, if pursuing this path is
unlikely to result in a worthwhile contribution, please let me know :-)
Best regards,
Graham Markall.