Skip to content
Advertisement

Wrapping a C++ library using msl-loadlib in python

I am currently writing a wrapper for a C++ library. The library is a 32-bits dll file and I’m using 64-bits so I’m using msl-loadlib. I have a problem wrapping a function that has pointer parameters.

Here is the header of the function in C++

int CUSB::GetMeasurement(int Group, int StartPoint, int* NumberOfPoints, double* XData, double** YData, eFilter Filter)

and here the wrapper I wrote

from msl.loadlib import Server32
import ctypes

from client import Client

class Server(Server32):

    def __init__(self, host: str, port: int):
        super().__init__("USBLib.dll", "cdll", host, port)

    ...

    def getMeasurement(self, group: int, startPoint: int, nbrOfPoints: int, filter: Client.Filter):
        self.lib.GetMeasurement.restype = int
        xData = (ctypes.c_double * 65536)()
        yData = ((ctypes.c_double * 3) * 65536)()
        self.lib.GetMeasurement(
            ctypes.c_int(int(group)),
            ctypes.c_int(int(startPoint)),
            ctypes.pointer(ctypes.c_int(int(nbrOfPoints))),
            ctypes.byref(xData),
            ctypes.byref(yData),
            ctypes.c_int(int(filter.value[0]))
        )
        return xData, yData

from enum import Enum, unique
from msl.loadlib import Client64

class Client(Client64):

    @unique
    class Filter(Enum):
        NONE = 0,
        LOWPASS = 1

    ...

    def getMeasurement(self, group: int, startPoint: int, nbrOfPoints: int, filter: Filter):
        return self.request32(
            'getMeasurement',
            group,
            startPoint,
            nbrOfPoints,
            filter
        )

When I call Client.getMeasurement(parameters), I get the following error

File 'C:\Users\DELL\Documents\python\USBlib\server.py', line 77, in getMeasurement
    ctypes.c_int(int(filter.value[0]))
OSError: exception: access violation writing 0x00000000

Edit :

I tried to use ctypes argtypes.

xData = (ctypes.c_double * 65536)()
yData = ((ctypes.c_double * 3) * 65536)()
nbrOfPoints = ctypes.c_int()

self.lib.GetMeasurement.argtypes = [
    ctypes.c_int,
    ctypes.c_int,
    ctypes.POINTER(ctypes.c_int),
    ctypes.POINTER(ctypes.c_double),
    ctypes.POINTER(ctypes.POINTER(ctypes.c_double)),
    ctypes.c_int
]

dataSize = ctypes.c_int(int(inputDataSize - previousPoint))

self.lib.GetMeasurement(1, previousPoint, ctypes.byref(dataSize), xData, yData, Client.Filter.NONE)        

I have a C++ sample code that is using the GetMeasurement function :

int PreviousPoint = 0;
double* ppYData1[3];
double* pXData1;

int UpdateSize = 65536;

for (int channel=0; channel<3; channel++)
{
     ppYData1[channel] = new double[UpdateSize];
}
pXData1 = new double[UpdateSize];

int DataSize = Input1DataSize-PreviousPoint;

GetMeasurement(INPUT1, PreviousPoint, &DataSize, pXData1, ppYData1, (eFilter)(TC->SC->m_ScanConfiguration.m_nFilter));

Advertisement

Answer

I’m ignoring msl-loadlib as extraneous to the problem of calling ctypes correctly.

Here’s an example of calling the function shown. The YData needs to be an array of 3 double* and then each of those pointers needs to be initialized with the next dimension of the array. Note this parallels the C++ example of calling the function.

test.cpp – sample implementation to fill out the arrays.

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef int eFilter;

extern "C" {

API int GetMeasurement(int Group, int StartPoint, int* NumberOfPoints, double* XData, double** YData, eFilter Filter) {
    for(int i = 0; i < 65536; ++i) {
            XData[i] = i;
            YData[0][i] = i + .25;
            YData[1][i] = i + .5;
            YData[2][i] = i + .75;
    }
    return 0;
}

}

test.py – ctypes example to call the function

import ctypes as ct

pdouble = ct.POINTER(ct.c_double)
ppdouble = ct.POINTER(pdouble)

dll = ct.CDLL('./test')
dll.GetMeasurement.argtypes = ct.c_int, ct.c_int, ct.POINTER(ct.c_int), pdouble, ppdouble, ct.c_int
dll.GetMeasurement.restype = ct.c_int

xData = (ct.c_double * 65536)()
yData = (pdouble * 3)()
for channel in range(3):
    yData[channel] = (ct.c_double * 65536)()
nbrOfPoints = ct.c_int()

dll.GetMeasurement(1, 0, ct.byref(nbrOfPoints), xData, yData, 0)
print(xData[0],xData[65535])
print(yData[0][0],yData[1][0],yData[2][0])
print(yData[0][65535],yData[1][65535],yData[2][65535])

Output:

0.0 65535.0
0.25 0.5 0.75
65535.25 65535.5 65535.75

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement