3

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?

2

2 Answers 2

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)
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. I arrived at another option, which I have posted, but I am marking your answer as the solution since you got there first and since your answer changes my original code less than my own. I wonder if one is preferable over the other for whatever reason, though? Performance matters.
P.S. i am new to StackOverflow. Is there something I have to do to get email notifications?
0

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

You were better off using 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.