使用CTYPE将数组从Python传递到C ,无法完全处理

Passing array from Python to C++ using ctypes, unable to process it entirely

本文关键字:处理 CTYPE 数组 Python 使用      更新时间:2023-10-16

我已经编写了以下代码,以使用ctypes库修改我的自定义Python类Point,然后在本教程中找到的批准之后。wrap_function只是易于使用的小助手,因为这是一个更大项目的MWE。

在Python方面:

import ctypes
import numpy as np 
libc = ctypes.WinDLL(r'C:Pathlib.dll')
 
def wrap_function(lib, funcname, restype, argtypes): 
    func = lib.__getattr__(funcname) 
    func.restype = restype 
    func.argtypes = argtypes 
    return func 
 
class Point(ctypes.Structure): 
    _fields_ = [('x', ctypes.c_int), ('xdata', ctypes.c_void_p)]
     
list_of_points = []                                #unused for now 
xdata = np.zeros((40000,), dtype=np.double)
a = Point(1,xdata.ctypes.data) 
b = Point(3,xdata.ctypes.data) 
change_data_for_point = wrap_function(libc,'change_data_for_point', None, [ctypes.POINTER(Point)])
change_data_for_point(a)

和C侧:

---header: 
const int N = 40000;
typedef struct {
    double x; 
    double xdata[N];
} Point;
extern "C" LIB_API void change_data_for_point(Point* p);

---source:
void change_data_for_point(Point* p) {
    p->x++; 
    for (int i = 0; i < 40000; i++) {
        p->xdata[i] = 2.0*i;
        if (i % 1000 == 0) printf("xdata at index %d is %fn", i, p->xdata[i]);
    }
}

在Windows 7 cmd中执行Python文件时,它会打印以下输出:

xdata at index 0 is 0.000000
xdata at index 1000 is 2000.000000
 // ... some more ... 
xdata at index 17000 is 34000.000000
xdata at index 18000 is 36000.000000
Traceback (most recent call last):
   File "test.py", line 40, in <module>

为什么它停在18.000?我尝试了几次,有时循环达到19或20k,但永远不会比这更高。它与C侧的数组初始化有关吗?我是否弄乱了Python侧传递的参数?


奖金问题:如何将这些点的列表通过CTYPE将其传递给C端?

尽管 numpy 添加了附加的复杂性级别,但可以在[Python 3]上找到每个信息:CTYPES- python的外国功能库。p>(主要)问题是 point 结构在 c python 中的定义不同。
此外,该功能期望Point*,因此必须使用 byref (它也没有它,我也不知道这是否是 undectine canducy '快乐的案例或 ctypes 默默地这样做 - 由于 argtypes )。

我已经调整了您的代码以工作。

dll.c

#include <stdio.h>
#if defined(_WIN32)
#  define DLL_EXPORT __declspec(dllexport)
#else
#  define DLL_EXPORT
#endif

const int N = 40000;
typedef struct {
    double x;
    double xdata[N];
} Point;

#if defined(__cplusplus)
extern "C" {
#endif
    DLL_EXPORT void change_data_for_point(Point *p);
#if defined(__cplusplus)
}
#endif

void change_data_for_point(Point *p) {
    p->x++; 
    for (int i = 0; i < 40000; i++) {
        p->xdata[i] = 2.0 * i;
        if (i % 10000 == 9999)
            printf("xdata at index %d is %fn", i, p->xdata[i]);
    }
}

code.py

#!/usr/bin/env python3
import sys
import ctypes
import numpy as np

DLL_NAME = "./dll.dll"
xdata_dim = 40000  # !!! Must match N (from C) !!!
DoubleArr = ctypes.c_double * xdata_dim
class Point(ctypes.Structure): 
    _fields_ = [
        ("x", ctypes.c_int),
        ("xdata", DoubleArr),
    ]

def wrap_function(lib, funcname, restype, argtypes):
    func = lib.__getattr__(funcname)
    func.restype = restype
    func.argtypes = argtypes
    return func

def main():
    dll = ctypes.CDLL(DLL_NAME)
    #xdata_dim = ctypes.c_int.in_dll(dll, "N")
    xdata = np.zeros((xdata_dim,), dtype=np.double)
    a = Point(1, DoubleArr.from_address(xdata.ctypes.data))
    b = Point(3, DoubleArr.from_address(xdata.ctypes.data))
    change_data_for_point = wrap_function(dll,"change_data_for_point", None, [ctypes.POINTER(Point)])
    change_data_for_point(ctypes.byref(a))
    print(a.xdata[30000])

if __name__ == "__main__":
    print("Python {:s} on {:s}n".format(sys.version, sys.platform))
    main()

输出

[cfati@CFATI-5510-0:e:WorkDevStackOverflowq055124400]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[prompt]> "c:Installx86MicrosoftVisual Studio Community2015vcvcvarsall.bat" x64
[prompt]> dir /b
code.py
dll.c
[prompt]> cl /nologo /DDLL /MD /Tp dll.c  /link /NOLOGO /DLL /OUT:dll.dll
dll.c
   Creating library dll.lib and object dll.exp
[prompt]> dir /b
code.py
dll.c
dll.dll
dll.exp
dll.lib
dll.obj
[prompt]> "e:WorkDevVEnvspy_064_03.06.08_test0Scriptspython.exe" code.py
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32
xdata at index 9999 is 19998.000000
xdata at index 19999 is 39998.000000
xdata at index 29999 is 59998.000000
xdata at index 39999 is 79998.000000
60000.0

@Edit0

如果要处理 point s的列表,则可以使用数组。类似:

PointArr = Point * len(list_of_points)
point_arr = PointArr(*list_od_points)