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
Some Clarification Needed About Synchronous Versus Asynchronous Asio Operations
Qobject: Cannot Create Children for a Parent That Is in a Different Thread
Get Size of Terminal Window (Rows/Columns)
How to Track Memory Allocations in C++ (Especially New/Delete)
Cc1Plus: Error: Unrecognized Command Line Option "-Std=C++11" with G++
C++ Opencv Image Sending Through Socket
Creating Array of Objects on the Stack and Heap
C++ Memory Barriers for Atomics
How to Include the String Header
How to Do an If Else Depending Type of Type in C++ Template
How to Resize a 2D Vector of Objects Given the Width and Height
Why Is Rvo Disallowed When Returning a Parameter
Performance Difference of "If If" VS "If Else If"
Pointer to Array of Unspecified Size "(*P)[]" Illegal in C++ But Legal in C
Understanding Region of Interest in Opencv 2.4
How to Implement No-Op MACro (Or Template) in C++
Is an Iterator in C++ a Pointer
Visual Studio 2010 & 2008 Can't Handle Source Files with Identical Names in Different Folders