使用ctypes为C函数传递和获取数组的问题 [英] Problems with passing and getting arrays for a C function using ctypes

查看:68
本文介绍了使用ctypes为C函数传递和获取数组的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用ctypes从我的python代码中将二维数组和一维数组传递给C函数,然后此函数将一维数组返回到python终端。

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-我需要从python传递数组,正如您将在下面看到的(我尝试如何做)。
2-可能我没有正确设置参数类型和返回类型。我一直在搜索很多东西,但没有什么能解决我的问题。

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.

我的C代码名为Thomas_PYTHON_DLL.c:

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| \n",i,solution[i]);}  

return solution;
//free(solution);
}

}

我的C代码为Thomas.h :

My C code named Thomas.h:

#define EXPORT __declspec(dllexport)

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

最后是我的Python代码,名为Thomas_Python.py:

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])

在上面的代码中,我希望将数组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.

推荐答案

提及 [Python 3.Docs]:ctypes-Python的外部函数库

您的代码有很多问题。以下是一些:

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


  • 我不知道像这样的函数头 double * Thomas(int Dimensions ,双重MatrizTridiagonal [dimension] [dimension],double vec_b [dimension])进行编译(因为 dimension )。但是,我没有使用 gcc

  • 您的 C Python 函数标头进行测试(返回值)不同: double * vs。 ctypes.POINTER(ctypes.c_double * 5)

  • 您永远不会取消分配返回的数组,从而导致内存泄漏

  • 代码样式(包括命名)可能非常

  • 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. 使用最大常量作为维数。限制很明显

  2. 使用指针代替。缺点是函数头不是很清楚,通常人们通常会避开指针,但是尤其是当它们具有不止一级的间接寻址( 2星指针 :)时)

  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 :) )

但是,我选择了后一种方法。我创建了一个虚拟的 .dll ,其中包含一个函数,该函数可计算2个数组的乘积(将 1D 数组视为 2D 数组

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.")

这里唯一棘手的事情是 DoublePtrArr 强制转换,因为 2D 数组不能强制转换为双精度( ** ,而不是类型)指针(我的意思是可以的,但是这2种内存布局不同,因此它会生成 未定义行为 >,并且很可能该程序将 segfault 访问冲突)),因此每个内部数组都将在中间对象中分别进行强制转换,然后将其强制转换为double( ** )指针(函数期望)。

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).

输出


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.


这篇关于使用ctypes为C函数传递和获取数组的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆