You can do what you are asking with ctypes, however your C
code as is has some deficiencies. The big problem is this:
double A[2];
This creates an array of 2 doubles on the stack. When you use return p;
this effectively ends up returning a pointer to an array on the stack. Since the stack will unwind when your function exits you can no longer rely on the pointer being valid. If you want an array of 2 doubles use malloc
to create them and then return a pointer to that array. So this code would work:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
double * Make_A(){
/* Use Malloc to create an array of 2 doubles */
double *A = malloc(2 * sizeof(double));
A[0] = 0.00001;
A[1] = 42.0;
return A;
}
As well you should create a function that you can call from Python to free any pointers you create on the C side:
/* Create a function that can free our pointers */
void freeptr(void *ptr)
{
free(ptr);
}
With our C code created we can use ctypes to load our shared object and call our functions.
import ctypes
lib_cpp = ctypes.CDLL('./test.so')
# Make_A returns a pointer to two doubles
lib_cpp.Make_A.restype = ctypes.POINTER(ctypes.c_double * 2)
# freeptr takes one argument that is a void pointer
lib_cpp.freeptr.argtype = ctypes.c_void_p
# freeptr return void (no parameters)
lib_cpp.freeptr.restype = None
# Call Make_A and retrieve a double array pointer
darrayptr = lib_cpp.Make_A()
# Convert the array pointer contents to a Python list
floatlist = [x for x in darrayptr.contents]
# We need to free our pointer since Python won't know to
# do it for us. Similar to C where we must free anything we
# malloc.
lib_cpp.freeptr(darrayptr)
You can find material on ctypes in the Python documentation; some sample code to give you some ideas; and this tutorial.
If you don't want to create a full Python module you also have the ability to create a shared object that utilizes Python data types and return a PyObject that contains a list of floats. Information on PyList can be found in this documentation and documentation on the PyFloat type can be found in this documentation:
PyObject* PyList_New(Py_ssize_t len)
Return value: New reference.
Return a new list of length len on success, or NULL on failure.
You can then add this example to your C code before all other includes:
#include <Python.h>
And then you can add this code:
PyObject* getList(void)
{
PyObject *dlist = PyList_New(2);
PyList_SetItem(dlist, 0, PyFloat_FromDouble(0.00001));
PyList_SetItem(dlist, 1, PyFloat_FromDouble(42.0));
return dlist;
}
The shared object can be built with a command like this on most *nix type systems where the package python-dev (or python-devel) is installed:
gcc -c -Wall -Werror -fPIC `python-config --cflags` test.c
gcc -shared -o test.so test.o `python-config --ldflags`
Now in Python can you can do code similar to what we had earlier, but we can now use Python lists more directly:
import ctypes
lib_cpp = ctypes.CDLL('./test.so')
# getList returns a PyObject
lib_cpp.getList.restype = ctypes.py_object
# Now call it and do something with the list
mylist = lib_cpp.getList()
print(mylist)
The output would look something like:
[1e-05, 42.0]