I made a CPP DLL and I'm trying to call function inside it from python. I've achieved this multiple times for other functions, but this one, I just can't find my mistake.
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)
Basically I load my DLL, set Ctypes for arguments and result for both of my fonctions. Then I make a python wrapper so that the user does not have to retype every cast from python to cpp.
My types on the cpp side seems good too:
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);
}
I have no warning from the compiler for the cpp part, I've printed the memory adresse before the return inside fit_reg_RBF_naive
from cpp and the W
in python and they are the same.
000002B358384980 // cpp address of W before return
0x58384980 # Python address of W after function call
For me it seems the same address. Maybe I'm wrong.
So when I try to call my second cpp function it said
myDll.predict_reg_RBF_naive(W, XTrain, Xpredict,inputCountPerSample, gamma, sampleCount) OSError: exception: access violation reading 0x000000007C7380A0
It crashed in the cpp when it tries to read W
. They are no free
or 'delete' in the cpp and the variable is properly allocated : double* W = new double[2];
Also, when I print W
type in python I get <class 'int'>
.
How comes my W
seems to have the same address regarding the language, but has not the good type? Changing the result type of fit_reg_RBF_naive
to POINTER(ct.c_double * 2)
makes no change.
EDIT:
Here is how I call my functions:
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.
You misspelled restypes (it should be restype). By doing so, restype is not initialized, and defaults to int (this wouldn't be a problem on 32bit), and you ran into:
Besides that, there are several problems in the code:
double*
in this case), don't use ctypes.c_void_p
(in argtypes or restype) to map it, as it might be too wide, use (for this case) ctypes.POINTER(ctypes.c_double)
insteadFor me this doesn't even compile (I wonder how were you able to run that code). I'm going to exemplify on XTrain only, but applies to YTrain and Xpredict as well. ctypes doesn't know to convert a Python list to a ctypes.POINTER(ctypes.c_double)
(or ctypes.c_void_p
), and the conversion must be made manually (to a ctypes.c_double
array):
XTrain = [1.0, 1.0, 1.0, 3.0, 3.0, 3.0]
xtrain_ctypes = (ctypes.c_double * len(XTrain))(*XTrain)
and pass xtrain_ctypes to the functions.