I\'m trying to use ctypes for passing a bidimensional array and an unidimensional array to a C function from my python code then this function return an unidimensional array to
Mentioning [Python.Docs]: ctypes - A foreign function library for Python.
There are a number of problems with your code. Here are a few:
double *Thomas(int dimension, double MatrizTridiagonal[dimension][dimension], double vec_b[dimension])
compiles (because of dimension). However, I didn't test it with gccdouble*
vs. ctypes.POINTER(ctypes.c_double * 5)
When dealing with arrays (especially multidimensional - since dimension needs to be known at compile time) as function arguments, meaning that they are passed from outside, there are a couple of ways to handle things:
However, I chose the latter approach. I created a dummy .dll, which contains a function that calculates the product of the 2 arrays (thinking of the 1D array as a 2D array that only has one column).
dll0.c:
#include <stdlib.h>
#if defined(_WIN32)
# define DLL0_EXPORT_API __declspec(dllexport)
#else
# define DLL0_EXPORT_API
#endif
DLL0_EXPORT_API double *dll0Func0(int dimension, double **arr2D, double *arr1D) {
double *solution = (double*)calloc(dimension, sizeof(double));
for (int i = 0; i < dimension; i++) {
for (int j = 0; j < dimension; j++) {
solution[i] += arr2D[i][j] * arr1D[j];
}
}
return solution;
}
DLL0_EXPORT_API void dealloc(double *ptr) {
free(ptr);
}
script0.py:
#!/usr/bin/env python3
import sys
import ctypes
DLL_NAME = "./dll0.dll"
def main():
dim = 5
DoubleArr = ctypes.c_double * dim
DoubleArrArr = DoubleArr * dim
DoublePtr = ctypes.POINTER(ctypes.c_double)
DoublePtrPtr = ctypes.POINTER(DoublePtr)
DoublePtrArr = DoublePtr * dim
dll0 = ctypes.CDLL(DLL_NAME)
dll0Func0 = dll0.dll0Func0
dll0Func0.argtypes = [ctypes.c_int, DoublePtrPtr, DoublePtr]
dll0Func0.restype = DoublePtr
dealloc = dll0.dealloc
dealloc.argtypes = [DoublePtr]
mat = DoubleArrArr(
(2, -1, 0, 0, 0),
(-1, 2, -1, 0, 0),
(0, -1, 2, -1, 0),
(0, 0, -1, 2, -1),
(0, 0, 0, -1, 2),
)
vec = DoubleArr(4, 2, 2, 2, 4)
res = dll0Func0(dim, ctypes.cast(DoublePtrArr(*(ctypes.cast(row, DoublePtr) for row in mat)), DoublePtrPtr), ctypes.cast(vec, DoublePtr))
print("{0:s} returned {1:}".format(dll0Func0.__name__, res))
for i in range(dim):
print("{0:d} - {1:.3f}".format(i, res[i]))
dealloc(res)
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main()
print("\nDone.")
The only tricky thing here is the DoublePtrArr cast, as the 2D array can't be cast to double (**, not the type) pointer directly (I mean it can be, but the 2 memory layouts differ, so it would generate Undefined Behavior, and most likely the program will segfault (Access Violation)), so each inner array is cast separately in the intermediary object, which will be then cast to a double (**) pointer (that the function expects).
Output:
cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q057295045]> sopr.bat *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages *** [prompt]> "c:\Install\x86\Microsoft\Visual Studio Community\2017\VC\Auxiliary\Build\vcvarsall.bat" x64 ********************************************************************** ** Visual Studio 2017 Developer Command Prompt v15.9.14 ** Copyright (c) 2017 Microsoft Corporation ********************************************************************** [vcvarsall.bat] Environment initialized for: 'x64' [prompt]> dir /b dll0.c script0.py thomas.c Thomas.h [prompt]> cl /nologo /DDLL dll0.c /link /NOLOGO /DLL /OUT:dll0.dll dll0.c Creating library dll0.lib and object dll0.exp [prompt]> dir /b *.dll dll0.dll [prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" script0.py Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32 dll0Func0 returned <__main__.LP_c_double object at 0x0000026CD4BEC4C8> 0 - 6.000 1 - -2.000 2 - 0.000 3 - -2.000 4 - 6.000 Done.