Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
510 views
in Technique[技术] by (71.8m points)

python - Problems with passing and getting arrays for a C function using ctypes

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 my python terminal.

1-I need to pass the array from python, as you will see below (how i tried to do). 2-Probably i don't set up argument types and return types correctly. I've been searching a lot but nothing solve my problems.

My C code named Thomas_PYTHON_DLL.c:

#include"stdio.h"
#include"stdlib.h"
#include"Thomas.h"


EXPORT double* Thomas(int dimension, double MatrizTridiagonal[dimension] 
[dimension],double vec_b[dimension]){


double* Thomas(int dimension, double MatrizTridiagonal[dimension] 
[dimension],double vec_b[dimension]){


double a[dimension];                            
double b[dimension];                            
double c[dimension];                            
double resp[dimension];                         

double *solution;
solution=(double *) malloc(dimension*sizeof(double));                       

for(int i=0;i<dimension;i++){resp[i]=vec_b[i];}


for(int i=0;i<dimension;i++){                    
        if(i==0){a[i]=0.0;}
        else{               
            a[i]=MatrizTridiagonal[i][i-1]; 
            }
}

for(int i=0;i<dimension;i++){                           
        b[i]=MatrizTridiagonal[i][i];       
    }

    for(int i=0;i<dimension;i++){               
        if(i==dimension-1){c[dimension-1]=0.0;}
        else{
            c[i]=MatrizTridiagonal[i][i+1];     
    }
}


for(int i=0;i<dimension;i++){                   
        if(i==0){
            c[i]=c[i]/b[i];                     
            resp[i]=resp[i]/b[i];               
                        }
        else{
            c[i]=c[i]/(b[i]-c[i-1]*a[i]);                           
            resp[i]=(resp[i]-a[i]*resp[i-1])/(b[i]-a[i]*c[i-1]);                
    }
}


for(int i=dimension-1;i>=0;i--){            

    if(i==dimension-1){
        solution[i]=resp[i];
    }

    else{

        solution[i]=resp[i]-c[i]*solution[i+1];
    }   

}

for(int i=0;i<dimension;i++){printf("x%d=|%0.2f| 
",i,solution[i]);}  

return solution;
//free(solution);
}

}

My C code named Thomas.h:

#define EXPORT __declspec(dllexport)

EXPORT double* Thomas(int dimension, double MatrizTridiagonal[dimension] 
[dimension],double vec_b[dimension]);

And finally my Python code, named Thomas_Python.py:

from ctypes import *

x=(c_double*5)
Tridiagonal = cdll.LoadLibrary('Thomas_dll.dll')
Tridiagonal.Thomas.restype=POINTER(x)
Tridiagonal.Thomas.argtypes=[c_int,((c_double*5)*5),(c_double*5)]

#arrays that i want to pass to C code
a=((c_double*5)*5)((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))
b=(c_double*5)(4,2,2,2,4)

r=Tridiagonal.Thomas(5,a,b)

print(r[2])

In the code above, i expected to print the value of the array r in the position "2", but the print show me:

<__main__.c_double_Array_5 object at 0x03A77350>

A great help would be beyond knowing how to read array values, getting the entire array as a list. Thank you so much for all your help and time, and I apologize for my English.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Mentioning [Python.Docs]: ctypes - A foreign function library for Python.

There are a number of problems with your code. Here are a few:

  • I don't know how a function header like this double *Thomas(int dimension, double MatrizTridiagonal[dimension][dimension], double vec_b[dimension]) compiles (because of dimension). However, I didn't test it with gcc
  • Your C and Python function header (return value) differs: double* vs. ctypes.POINTER(ctypes.c_double * 5)
  • You never deallocate the returned array, resulting in memory leaks
  • The codestyle (including naming) could be very much improved

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:

  1. Use a maximum constant value for dimension. The limitation is pretty obvious
  2. Use pointers instead. The drawback is that the function header is not that clear, and in general people tend to run away from pointers in general, but especially if they have more than one level of indirection (2 star pointer :) )

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}
".format(" ".join(item.strip() for item in sys.version.split("
")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main()
    print("
Done.")

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:WorkDevStackOverflowq057295045]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[prompt]> "c:Installx86MicrosoftVisual Studio Community2017VCAuxiliaryBuildvcvarsall.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:WorkDevVEnvspy_064_03.07.03_test0Scriptspython.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.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...