添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

I have this function in C library that I want to access from python using ctypes:

static const char *names[] =     {"Backplane", "ADC", "External", "Reserved"};
int test_char_ptr(const char ***names_, int *count_)
    *names_ = names;
    *count_ = 4;
    return 0;

I can then do this in C app:

    const char **items;
    unsigned nr_items;
    result = test_char_ptr(&items, &nr_items);
    for (unsigned i = 0; i < nr_items; i++) {
        printf("string[%d]: %s\n", i, items[i]);

In python I’m doing the following so far:

    names_ = C.POINTER(C.POINTER(C.c_char))()
    count_ = C.c_int(123456789) # WORKS!
    pydemolib.test_char_ptr(names_, count_)
    print("count: %d" % count_.value)
    print("names: %s" % names_)
    print("names.contents: %s" % names_.contents)
    print("names.contents.contents: %s" % names_.contents.contents)
    print("names.contents.contents.value: %s" % names_.contents.contents.value)
count: 4
names: <pydemolib.LP_LP_c_char object at 0x7f0dd6ed7ac0>
names.contents: <ctypes.LP_c_char object at 0x7f0dd6c9f5c0>
names.contents.contents: c_char(b'B')
names.contents.contents.value: b'B'
names.contents.contents bytes: b'B'

How can I access the array of null terminated C strings in names_ at this point? It would be great if I could convert them to a list.

In ctypes, the type c_char means exactly that - a single character - and a POINTER to that really points specifically at that character and not anything subsequent. There is not a general “understanding” of how pointer decay, strings etc. work in C - that’s why, for example, the atomic types implement a * operator to give you array types. C allows arrays to decay to pointers implicitly all over the place, but Python is more strongly typed despite the dynamic typing - it maintains a strong distinction. So C.POINTER(C.c_char) doesn’t have access to any char values after the one directly pointed at; and it also doesn’t care if that char has a zero value.

To deal with the special case of C’s null-terminated strings, a separate c_char_p type is provided. (See also the full table of types).

You are actually really dealing with char**, not char*** in your ctypes code - in the C code, the third * is only being used in order to simulate pass-by-reference. (You’ll also want to check out the section on byref.) But what you really have is an array of count_ many “strings” (null-terminated C strings). That should look like c_char_p * count_ if I’m thinking clearly. (You might need to redesign the interface so that you get the count information separately, before the string data.)

For the sake of testing I’ve hardcoded the count_ in python to 4 and changed the signature as well as argument as follows:

test_char_ptr.argtypes = [POINTER(POINTER(c_char_p * 4)), POINTER(c_int)]
names_ = C.POINTER(C.c_char_p * 4)()
count_ = C.c_int(123456789) # WORKS!
pydemolib.test_char_ptr(names_, count_)
print("count: %d" % count_.value)
print("names: %s" % names_)
print("names.contents: %s" % names_.contents)
for name in names_.contents:
    print(name)

Results in this:

count: 4
names: <pydemolib.LP_c_char_p_Array_4 object at 0x7f31086f7ac0>
names.contents: <pydemolib.c_char_p_Array_4 object at 0x7f31084c35c0>
b'Backplane'
b'ADC'
b'External'
b'Reserved'

Seems to be exactly was I was looking for!

Thanks!

Since I can not change the API of the real shared object (this C code was a test code), I think I can get away with changing the signature of the ctypes function.
First it expects just one c_char_p value and after the call the count_ will be valid. Then I change the signature to expect count_ * c_char_p values and make a second call. This time I get all the C strings out.

pydemolib.test_char_ptr.restype = C.c_int pydemolib.test_char_ptr.argtypes = [C.POINTER(C.POINTER(C.c_char_p)), C.POINTER(C.c_int)] names_ = C.POINTER(C.c_char_p)() count_ = C.c_int(123456789) pydemolib.test_char_ptr(names_, count_) print("count: %d" % count_.value) pydemolib.test_char_ptr.argtypes = [C.POINTER(C.POINTER(C.c_char_p * count_.value)), C.POINTER(C.c_int)] pydemolib.test_char_ptr.restype = C.c_int names_ = C.POINTER(C.c_char_p * count_.value)() count_ = C.c_int(123456789) pydemolib.test_char_ptr(names_, count_) # print("count: %d" % count_.value) print("names: %s" % names_) print("names.contents: %s" % names_.contents) for name in names_.contents: print(name)

Output:

count: 4
names: <__main__.LP_c_char_p_Array_4 object at 0x7ff8edea35c0>
names.contents: <__main__.c_char_p_Array_4 object at 0x7ff8ee000540>
b'Backplane'
b'ADC'
b'External'
b'Reserved'