I need to call a C function from Python, using ctypes and to have that function provide one or more arrays back to Python. The arrays will always be of simple types like long, bool, double.
I would very much prefer if the arrays could be dynamically sized. I will know the needed before each call, but different call should use different sizes.
I suppose I should allocate the arrays in Python and let the C code overwrite the contents, so that Python can eventually de-allocate the memory it allocated.
I control both the Python and the C code.
I have this now which does not work:
C:
FOO_API long Foo(long* batch, long bufferSize)
{
for (size_t i = 0; i < bufferSize; i++)
{
batch[i] = i;
}
return 0;
}
Python:
print "start test"
FooFunction = Bar.LoadedDll.Foo
longPtrType = ctypes.POINTER(ctypes.c_long)
FooFunction.argtypes = [longPtrType, ctypes.c_long]
FooFunction.restype = ctypes.c_long
arrayType = ctypes.c_long * 7
pyArray = [1] * 7
print pyArray
errorCode = FooFunction(arrayType(*pyArray), 7)
print pyArray
print "test finished"
Produces:
start test
[1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1]
test finished
Should Produce:
start test
[1, 1, 1, 1, 1, 1, 1]
[0, 1, 2, 3, 4, 5, 6]
test finished
Why does this not work? Or do I need to do this in a different way?
-
Maybe try Cython + numpy, see here kbarbary.github.io/blog/cython-and-multiple-numpy-dtypesmirosval– mirosval2015年12月10日 15:08:28 +00:00Commented Dec 10, 2015 at 15:08
-
@mirosval: No thanks. The solution is complicated enough as it is.ThomasI– ThomasI2015年12月10日 16:26:44 +00:00Commented Dec 10, 2015 at 16:26
2 Answers 2
The C array is built using the python list; both are different objects. And the code is print the python list, which is not affected by Foo call.
You need to build the C array, pass it, then use it after the call:
arrayType = ctypes.c_long * 7
array = arrayType(*[1] * 7)
print list(array)
errorCode = FooFunction(array, len(array))
print list(array)
2 Comments
Thanks to falsetru for the extremely quick answer. I did no notice it right away and in the mean time I arrived at this, which also seems to work. I wonder if one is preferable to the other?
print "start test"
FooFunction = GpGlobals.LoadedDll.Foo
longArrayType = ctypes.c_long * (7)
FooFunction.argtypes = [longArrayType, ctypes.c_long]
FooFunction.restype = ctypes.c_long
pyArray = longArrayType()
for l in pyArray:
print l
errorCode = FooFunction(pyArray, 7)
for l in pyArray:
print l
print "test finished"
I did not initially think that this would work with dynamically sized arrays, but all that I had to do was redefine the argtypes before each call.
1 Comment
ctypes.POINTER(ctypes.c_long) in argtypes since it accepts any long * pointer, including long arrays of any size since they're passed as a pointer. As to iterating the result instead of creating a list, that's preferred if you don't actually need to create a list. There's no need to duplicate the data in two objects.