ctypes and string

Bdfy picture Bdfy · Dec 9, 2011 · Viewed 23.3k times · Source

I have a simple C-file:

char* initializetest() {
    char * test = malloc(1000);
    return test;
}

int searchtest( char* test )
{
   strcpy(test,"test");
   return 0;
}

main()
 {
    char *test = initializetest();
    searchtest(test);
    printf("%s\n", test );
}

and python file:

from ctypes import *

class Test(object):
    def __init__(self):
        self.test_library=CDLL("test.so")
        self.test_initialize = self.test_library.initializetest
        self.test_search = self.test_library.searchtest
        self.test_search.restype=c_int
        self.m = self.test_initialize()

    def search(self):
        self.test_search(self.m)
        print self.m

r = Test()
print r.search()

How to get "test" string in python ?

Answer

yak picture yak · Dec 9, 2011
from ctypes import *

charptr = POINTER(c_char)

test = CDLL('test.so')
test.initializetest.argtypes = []
test.initializetest.restype = charptr
test.searchtest.argtypes = [charptr]
test.searchtest.restype = c_int

buf = test.initializetest()
test.searchtest(buf)
print cast(buf, c_char_p).value
# TODO Release the "buf" memory or it will leak.

EDIT

Initially I used c_char_p to pass the buffer between the functions but c_char_p is like a const pointer. If used as a restype, you will actually get a Python str back. So for initializetest it will create a string from the allocated memory (by copying data) and throw the pointer away.

Now we're creating a new type, a POINTER to c_char. This is then used in both functions.

For Python, this type points to a single char so we have to cast it to get the whole string after searchtest is done. We cast to c_char_p because we just want to read the value so a const pointer is OK.

As a side note, this illustrates the disastrous effect of using c_char_p with functions that modify the array (as searchtest above does):

>>> libc.memset.argtypes = [c_char_p, c_int, c_int]
>>> foo = 'Python'
>>> foo
'Python'
>>> libc.memset(foo, ord('x'), 3)
44808532
>>> foo
'xxxhon'
>>>

Note how we've managed to change an immutable Python string!

The argtypes setup line isn't even needed because ctypes assumes c_char_p if Python str is used as argument.