How to Return Array from C++ Function to Python Using Ctypes

How to return array from C++ function to Python using ctypes

function.cpp returns an int array, while wrapper.py tries to interpret them as doubles. Change ArrayType to ctypes.c_int * 10 and it should work.


It's probably easier to just use np.ctypeslib instead of frombuffer yourself. This should look something like

import ctypes
from numpy.ctypeslib import ndpointer

lib = ctypes.CDLL('./library.so')
lib.function.restype = ndpointer(dtype=ctypes.c_int, shape=(10,))

res = lib.function()

Reading array returned by c function in ctypes

Specify restypes as [POINTER][1](c_float):

import ctypes

libfoo = ctypes.cdll.LoadLibrary('./foo.so')
foo = libfoo.foo
foo.argtypes = ()
foo.restype = ctypes.POINTER(ctypes.c_float)
result = foo()
print(result[0], result[1])

Returning a C array from a C function to Python using ctypes

The pointer you're returning is actually the same that you're passing. I.e. you don't actually need to return the array pointer.

You are handing over a pointer to the memory area backing the list from Python to C, the C function can then change that memory. Instead of returning the pointer, you can return an integer status code to flag whether everything went as expected.

int addOne(int array[4])
{
int i;
for(i = 0; i < 5; i++)
{
array[i] = array[i] + 1; //This modifies the underlying memory
}
return 0; //Return 0 for OK, 1 for problem.
}

From the Python side, you can view the results by inspecting arr.

from ctypes import *
libCalc = CDLL("libcalci.so")
pyarr = [65, 66, 67, 68] #Create List with underlying memory
arr = (ctypes.c_int * len(pyarr))(*pyarr) #Create ctypes pointer to underlying memory
res = libCalc.addOne(arr) #Hands over pointer to underlying memory

if res==0:
print(', '.join(arr)) #Output array

pass and return an array of doubles through c function inside Python

It sounds like b is an array, but testfun1 does not get an indication of the size of the array and it wasn't mentioned in the question. Here's an example implementation of testfun1 that assumes the array is three elements:

test.c

#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif

#include <stdio.h>

API double testfun1(double x, double* y, char* str)
{
printf("%lf %p %s\n",x,y,str);
y[0] = x;
y[1] = x * 2;
y[2] = x * 3;
return x * 4;
}

Here's the Python code to call it:

test.py

from ctypes import *

dll = CDLL('test')
test1 = dll.testfun1
test1.argtypes = c_double,POINTER(c_double),c_char_p
test1.restype = c_double

a = 2.5
b = (c_double * 3)() # create an array of three doubles
s = b'test123'
d = test1(a,b,s)
print(d,list(b))

Output

2.500000 000001CA3E31B330 test123
10.0 [2.5, 5.0, 7.5]

Passing a python list to c DLL function which returns array data using ctypes

The given C code doesn't match the Python wrapper. The function name doesn't match and the types don't match. Here's a working example for your learning:

test.c

#include <string.h>

#ifdef _WIN32
# define API __declspec(dllexport) // Windows-specific export
#else
# define API
#endif

/* This function takes pre-allocated inputs of an array of byte strings
* and an array of integers of the same length. The integers will be
* updated in-place with the lengths of the byte strings.
*/
API void epicsData(char** in_data, int* out_data, int size)
{
for(int i = 0; i < size; ++i)
out_data[i] = (int)strlen(in_data[i]);
}

test.py

from ctypes import *

dll = CDLL('test')
dll.epicsData.argtypes = POINTER(c_char_p),POINTER(c_int),c_int
dll.epicsData.restype = None

data = [b'A',b'BC',b'DEF'] # Must be byte strings.

# Create the Python equivalent of C 'char* in_data[3]' and 'int out_data[3]'.
in_data = (c_char_p * len(data))(*data)
out_data = (c_int * len(data))()

dll.epicsData(in_data,out_data,len(data))
print(list(out_data))

Output:

[1, 2, 3]

Python Ctypes: Convert returned C array to python list, WITHOUT numpy

I succeeded solving it using pointers

The solution:

Define the function return type as POINTER(double_c):

getWeights_function_handler.restype = POINTER(double_c)

When the function returns, you can use the [] operator to access the C array elements (the same operator used in C):

weights = getWeights_function_handler()
mylist = [weights[i] for i in xrange(ARRAY_SIZE_I_KNOW_IN_ADVANCE)]

How do you pass an array by reference from python to a C++ function using CTypes?

You are returning array_type. That’s a type, not an instance of the type. The instance is your array_type(*numbers) passed to the function, but it is not preserved. Assign it to a variable and return that, or better yet convert it back to a Python list as shown below:

test.cpp

#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif

extern "C" API void our_function(double * numbers) {
numbers[0] += 1;
numbers[1] += 1;
numbers[2] += 1;
}

test.py

import ctypes

_sum = ctypes.CDLL('./test')
_sum.our_function.argtypes = ctypes.POINTER(ctypes.c_double),

def our_function(numbers):
array_type = ctypes.c_double * 3 # equiv. to C double[3] type
arr = array_type(*numbers) # equiv. to double arr[3] = {...} instance
_sum.our_function(arr) # pointer to array passed to function and modified
return list(arr) # extract Python floats from ctypes-wrapped array

z = our_function([0, 1, 2])
print(z)

Output:

[1.0, 2.0, 3.0]

Create an array of pointers in Python using ctypes

To convert a list some type into a ctypes array of that type, the straightforward idiom is:

(element_type * num_elements)(*list_of_elements)

In this case:

(c_char_p * len(array))(*array)

Note that (*array) expands the array as if each individual element was passed as a parameter, which is required to initialize the array.

Full example:

test.c - To verify the parameters are passed as expected.

#include <stdio.h>

#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif

API int foo(int numOfProp, const char** propName, const char** propValue) {
for(int i = 0; i < numOfProp; i++)
printf("name = %s value = %s\n", propName[i], propValue[i]);
return 1;
}

test.py

import ctypes as ct

dll = ct.CDLL('./test')
# Always define .argtypes and .restype to help ctypes error checking
dll.foo.argtypes = ct.c_int, ct.POINTER(ct.c_char_p), ct.POINTER(ct.c_char_p)
dll.foo.restype = ct.c_int

# helper function to build ctypes arrays
def make_charpp(arr):
return (ct.c_char_p * len(arr))(*(s.encode() for s in arr))

def foo(arr1, arr2):
if len(arr1) != len(arr2):
raise ValueError('arrays must be same length')
return dll.foo(len(arr1) ,make_charpp(arr1), make_charpp(arr2))

foo(['PropName1', 'PropName2'], ['10', '20'])

Output:

name = PropName1    value = 10
name = PropName2 value = 20


Related Topics



Leave a reply



Submit