I am trying to use Python's ctypes library to access some methods in the scanning library SANE. This is my first experience with ctypes and the first time I have had to deal with C datatypes in over a year so there is a fair learning curve here, but I think even without that this particular declaration would be troublesome:
extern SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only);
First of all, I've successfully dealt with SANE_Status
(an enum) and SANE_Bool
(a typedef to c_int
). Those were both simple. That first parameter, on the other hand, is causing me all sorts of grief. I'm unfamiliar with the "***
" notation to begin with and my tracer bullets so far have yielded nothing more than garbage data. How do I format the input to this function such that I can read back a list of my Python structure-objects? For reference, the C structure being referenced is:
typedef struct
{
SANE_String_Const name; /* unique device name */
SANE_String_Const vendor; /* device vendor string */
SANE_String_Const model; /* device model name */
SANE_String_Const type; /* device type (e.g., "flatbed scanner") */
}
SANE_Device;
Where SANE_String_Const
is defined as a c_char_p
.
My Python/ctypes version of this object is:
class SANE_Device(Structure):
_fields_ = [
("name", c_char_p),
("vendor", c_char_p),
("model", c_char_p),
("type", c_char_p)]
Suggestions on what I should pass in such that I can get the expected behavior (a list of structure-objects) out of this? All responses appreciated.
Update 1:
Using the following, I was able to retrieve a correct SANE_Device Python structure:
devices = pointer(pointer(pointer(SANE_Device())))
status = libsane.sane_get_devices(devices, c_int(0))
print status, devices, devices.contents.contents.contents.name
However, 1) yuck and 2) that seems like it would only work if there is a single result. I can't len() on devices.contents.contents
or devices.contents.contents.contents
. How am I to determine the number of results? The SANE docs specify that "If the function executes successfully, it stores a pointer to a NULL terminated array of pointers to SANE_Device structures in *device_list". Suggestions?
Update 2:
I was able to pass an ten-item array and then access the first element using:
devices = pointer(pointer(pointer((SANE_Device * 10)())))
status = libsane.sane_get_devices(devices, c_int(0))
print status, devices, devices.contents.contents.contents[0].name
However, ten is obviously an arbitrary number and I have no way of determining the actual number of results. Trying to access devices.contents.contents.contents[1].name
when only one device is connected causes a segmentation fault. There must be a proper way of dealing with variable-length constructs like these in ctypes.