youR.Fate
youR.Fate

Reputation: 826

python ctypes, pass double pointer by reference

The Problem

I'm trying to use a function in a c library with the following prototype:
int glip_get_backends(const char ***name, size_t *count);
The name argument here is the problem. It is a 2 dimmensional char array passed by reference. In C the function is used as follows:

const char** name;
size_t count;
glip_get_backends(&name, &count);
for (size_t i = 0; i < count; i++) {
    printf("- %s\n", name[i]);
}

Now I want to use this function from python using ctypes.


What I've tried

The most logical approach to me was doing this in python:

lglip = CDLL("libglip.so")
count = c_int(0)
backends = POINTER(POINTER(c_char))
lglip.glip_get_backends(byref(backends), byref(count))

which resulted in the error message

TypeError: byref() argument must be a ctypes instance, not '_ctypes.PyCPointerType'


The next approach was to use the POINTER() function three times and omitting the byref(), but that results in the following error:

ctypes.ArgumentError: argument 1: : Don't know how to convert parameter 1


Then I took inspiration from this question and came up with the following:

lglip = CDLL("libglip.so")
count = c_int(0)
backends = POINTER(POINTER(c_char))()
lglip.glip_get_backends(byref(backends), byref(count))
for i in range(0, count.value):
    print backends[i]  

Adding () after the definition of backends for some reason I don't fully comprehend has fixed the call, but the output I get is:

< ctypes.LP_c_char object at 0x7f4592af8710 >
< ctypes.LP_c_char object at 0x7f4592af8710 >

It seems I can't itterate over a 2 dimensional c array with one itterator in python, as it does not properly dereference it.

Upvotes: 5

Views: 11023

Answers (2)

youR.Fate
youR.Fate

Reputation: 826

After realizing that in the C implementation %s is used to itterate over the substrings I came up with the following working solution:

lglip = CDLL("libglip.so")
count = c_int(0)
backends_c = POINTER(c_char_p)()
lglip.glip_get_backends(byref(backends_c), byref(count))
backends = []
for i in range(0, count.value):
    backends.append(backends_c[i])
print backends

Update: changed POINTER(POINTER(c_char))() to POINTER(c_char_p)(), as eryksun suggested. This way I can easily access the strings.

Upvotes: 6

Nebril
Nebril

Reputation: 3273

If backends members can use index method, you can use this code:

lglip = CDLL("libglip.so")
count = c_int(0)
backends = POINTER(POINTER(c_char))()
lglip.glip_get_backends(byref(backends), byref(count))
for i in range(0, count.value):
    print ''.join(backends[i][:backends[i].index("0")])

Upvotes: -1

Related Questions