5

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.

asked May 19, 2015 at 11:47
3
  • Did you try iterating over objects that are in backends[i]? This is a 2-dimensional array, so you may need to use nested loop for this. I am not familiar with ctypes usage, just using my intuition. Commented May 19, 2015 at 11:54
  • 1
    I can try that, but I don't know the ranges of the loops. That's why in C it just uses one iterator. Oh, I've just realized in C it uses %s, therefore printing until 0円. Working on it. Commented May 19, 2015 at 11:55
  • 3
    You had to add the () to backends because POINTER(POINTER(c_char)) is a type, similar to c_int vs c_int(0)...first is type, second is instance of type. Commented May 19, 2015 at 17:10

2 Answers 2

6

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.

answered May 19, 2015 at 12:04
Sign up to request clarification or add additional context in comments.

4 Comments

c_char_p is a null-terminated string in ctypes (specifically this simple C type is a subclass of ctypes._SimpleCData defined with _type_ == 'z'). Use count = c_int(); backends = POINTER(c_char_p)() . That's a single pointer to a null-terminated string. Pass those by reference, as you're currently doing, for the function to fill in the initial pointer and array count. ctypes implements pointer arithmetic indexing, so backends[i] gets the i'th pointer in the array.
Thank you, I will try that out tomorrow and update the answer if it works as expected.
I had actually tried using c_char_p first, but then I still didn't have the parentheses at the end so I only got errors.
Yes, I had tried using backends = POINTER(c_char_p), but that didn't work since, as Mark Tolonen has also explained, that returns a type, not an instance. But I didn't know that was the problem so I switched to backends = POINTER(POINTER(c_char)) and eventually to backends = POINTER(POINTER(c_char))(). Using c_char_p again simply had not occured to me.
-1

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")])
answered May 19, 2015 at 12:31

2 Comments

Nope, that only gives AttributeError: 'LP_c_char' object has no attribute 'index'.
Then I guess you will be good with your code. I cannot think of more elegant way that would use simple array-like syntax there.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.