错误的 ctypes 分配

Wrong ctypes assignation

我制作了一个 CPP DLL,我试图从 python 调用其中的函数。 我已经为其他功能实现了多次,但是这个,我就是找不到我的错误。

dll_name = "..\src\x64\Debug\2019-3A-IBD-MLDLL.dll"
dllabspath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + dll_name
myDll = CDLL(dllabspath)

#fit_reg_RBF_naive
myDll.fit_reg_RBF_naive.argtypes = [ct.c_void_p, ct.c_double,  ct.c_void_p, ct.c_int, ct.c_int]
myDll.fit_reg_RBF_naive.restypes = ct.c_void_p

#predict_reg_RBF_naive
myDll.predict_reg_RBF_naive.argtypes = [ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_int, ct.c_double, ct.c_int]
myDll.predict_reg_RBF_naive.restypes = ct.c_double

def fit_reg_RBF_naive(pyXTrain, pyGamma, pyYTrain, pySampleCount, pyInputCountPerSample):
    XTrain = (ct.c_double * len(pyXTrain))(*pyXTrain)
    YTrain = (ct.c_double * len(pyYTrain))(*pyYTrain)
    inputCountPerSample = ct.c_int(pyInputCountPerSample)
    sampleCount = ct.c_int(pySampleCount)
    gamma = ct.c_double(pyGamma)
    return myDll.fit_reg_RBF_naive(XTrain, gamma, YTrain, sampleCount, inputCountPerSample)

def predict_reg_RBF_naive(pyW, pyXTrain, pyXpredict ,pyInputCountPerSample, pyGamma, pySampleCount):
    XTrain = (ct.c_double * len(pyXTrain))(*pyXTrain)
    inputCountPerSample = ct.c_int(pyInputCountPerSample)
    sampleCount = ct.c_int(pySampleCount)
    gamma = ct.c_double(pyGamma)
    Xpredict = (ct.c_double * len(pyXpredict))(*pyXpredict)
    return myDll.predict_reg_RBF_naive(W, XTrain, Xpredict, inputCountPerSample, gamma, sampleCount)

基本上我加载我的 DLL,为我的两个函数设置参数和结果的 Ctypes。然后我做了一个 python 包装器,这样用户就不必重新输入从 python 到 cpp.

的每个转换

我在 cpp 方面的类型似乎也不错:

extern "C" {

    SUPEREXPORT double predict_reg_RBF_naive(double* W, double* X, double* Xpredict, int inputCountPerSample, double gamma, int N);
    SUPEREXPORT double* fit_reg_RBF_naive(double* XTrain, double gamma, double* YTrain, int sampleCount, int inputCountPerSample);
}

编译器没有针对 cpp 部分发出警告,我已经在 cpp 的 fit_reg_RBF_naive 中的 return 和 [=48 中的 W 之前打印了内存地址=] 并且它们是相同的。

000002B358384980 // cpp address of W before return
0x58384980       # Python address of W after function call

对我来说似乎是同一个地址。也许我错了。

所以当我尝试调用我的第二个 cpp 函数时它说

myDll.predict_reg_RBF_naive(W, XTrain, Xpredict,inputCountPerSample, gamma, sampleCount) OSError: exception: access violation reading 0x000000007C7380A0

它在尝试读取 W 时在 cpp 中崩溃。它们在 cpp 中没有 free 或 'delete' 并且变量已正确分配:double* W = new double[2];

此外,当我打印 W 输入 python 时,我得到 <class 'int'>.

我的W怎么好像语言地址一样,但是类型不对?将 fit_reg_RBF_naive 的结果类型更改为 POINTER(ct.c_double * 2) 不会发生任何变化。

编辑:

下面是我调用函数的方式:

from dll_load import predict_reg_RBF_naive, fit_reg_RBF_naive

gamma = 50
sampleCount = 2
inputCountPerSample = 3
XTrain = [1.0, 1.0, 1.0, 3.0, 3.0, 3.0]
YTrain = [-1.0, 1.0]
Xpredict = [1.0, 1.0, 1.0]

W = fit_reg_RBF_naive(XTrain, gamma, YTrain, sampleCount, inputCountPerSample)

print(predict_reg_RBF_naive(W, XTrain, Xpredict, inputCountPerSample, gamma, sampleCount))

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

你拼错了restypes(应该是restype).通过这样做,restype 没有被初始化,并且默认为 int(这在 32bit),而你 运行 变成:

除此之外,代码中还有几个问题:

  • 如果C函数指定了一个指针(在本例中为double*),不要使用ctypes.c_void_p(在argtypes restype) 来映射它,因为它可能太宽了,使用(对于这种情况)ctypes.POINTER(ctypes.c_double) 而不是
  • 对我来说,这甚至不能编译(我想知道你是如何能够 运行 那个代码的)。我将仅在 XTrain 上举例说明,但 适用于 YTrainXpredict 还有ctypes 不知道将 Python 列表转换为 ctypes.POINTER(ctypes.c_double)(或 ctypes.c_void_p),并且必须手动进行转换(到 ctypes.c_double 数组):

    XTrain = [1.0, 1.0, 1.0, 3.0, 3.0, 3.0]
    xtrain_ctypes = (ctypes.c_double * len(XTrain))(*XTrain)
    

    并将xtrain_ctypes传递给函数。