Reputation: 13
I am trying to write a python wrapper for the following functions and structures in a dynamic library, but I cannot get it right.
typedef struct
{
const char* name;
void* value;
} XY_Param ;
XY_DLL_API int XY_SetParam(XY_Param parameter);
XY_DLL_API int XY_GetParam(XY_Param* parameter);
On the python side, I defined the corresponding structure and function below.
class Parameter(Structure):
_fields_ = [
("name", c_char_p),
("value", c_void_p)
]
def setDoubleParameter(self, pname, pvalue):
param = Parameter()
param.name = c_char_p(pname.encode())
param.value = c_void_p(c_double(pvalue))
ret = self.se.XY_SetParam(ptmtr)
That results in the following error message:
param.value = c_void_p(c_double(pvalue))
TypeError: cannot be converted to pointer
Upvotes: 1
Views: 719
Reputation: 177800
c_double(pvalue)
is an value equivalent to C double
. A pointer is needed to cast to a c_void_p
(C void*
), so the address must be taken. A downside to this design, however, is the object has to exist for the lifetime the pointer is valid, which may not be convenient.
In the example below, I create the ctypes
object and store it as an instance variable in the class instance, then cast its address to a c_void_p
to pass to C. As long as the class instance exists, the pointer will be valid:
test.c
#define XY_DLL_API __declspec(dllexport)
typedef struct
{
const char* name;
void* value;
} XY_Param ;
XY_Param global;
XY_DLL_API void XY_SetParam(XY_Param parameter) {
global = parameter;
}
XY_DLL_API void XY_GetParam(XY_Param* parameter) {
*parameter = global;
}
test.py
from ctypes import *
class Parameter(Structure):
_fields_ = [("name", c_char_p),
("value", c_void_p)]
dll = CDLL('./test')
dll.XY_SetParam.argtypes = Parameter,
dll.XY_SetParam.restype = None
dll.XY_GetParam.argtypes = POINTER(Parameter),
dll.XY_GetParam.restype = None
class Stash:
def set_double(self, name, value):
param = Parameter()
param.name = name.encode()
self.obj = c_double(value) # save the object to be stashed as instance variable
param.value = cast(byref(self.obj),c_void_p) # take address and cast to void*
dll.XY_SetParam(param)
def get_double(self):
param = Parameter()
dll.XY_GetParam(byref(param))
data = cast(param.value,POINTER(c_double)).contents # cast back to double* and dereference
return param.name.decode(),data.value
stash = Stash()
stash.set_double('double',1.234)
print(stash.get_double())
# demo the object being freed
del stash.obj
print(stash.get_double()) # could crash, could print garbage
('double', 1.234) # retrieved correctly
('\x12', 1.465443742036e-311) # garbage after deletion
Upvotes: 1