Passing a list of strings to from python/ctypes to C function expecting char **
I have a C function which expects a list \0 terminated strings as input:
void external_C( int length , const char ** string_list) {
// Inspect the content of string_list - but not modify it.
}
From python (with ctypes) I would like to call this function based on a list of python strings:
def call_c( string_list ):
lib.external_C( ?? )
call_c( ["String1" , "String2" , "The last string"])
Any tips on how to build up the datastructure on the python side? Observe that I开发者_Go百科 guarantee that the C function will NOT alter content of the strings in string_list.
Regards
joakim
def call_c(L):
arr = (ctypes.c_char_p * len(L))()
arr[:] = L
lib.external_C(len(L), arr)
Thank you very much; that worked like charm. I also did an alternative variation like this:
def call_c( L ):
arr = (ctypes.c_char_p * (len(L) + 1))()
arr[:-1] = L
arr[ len(L) ] = None
lib.external_C( arr )
And then in C-function I iterated through the (char **) list until I found a NULL.
Using ctypes
create list of expiries( strings)
expiries = ["1M", "2M", "3M", "6M","9M", "1Y", "2Y", "3Y","4Y", "5Y", "6Y", "7Y","8Y", "9Y", "10Y", "11Y","12Y", "15Y", "20Y", "25Y", "30Y"]
Logic to send string array
convert strings array to bytes array by looping in the array
expiries_bytes = []
for i in range(len(expiries)):
expiries_bytes.append(bytes(expiries[i], 'utf-8'))
Logic ctypes to initiate a pointer with a length of array
expiries_array = (ctypes.c_char_p * (len(expiries_bytes)+1))()
assigning the byte array into the pointer
expiries_array[:-1] = expiries_bytes
I just make it using SWIG typemap
1.write customized typemap in demo.i
interface file.
%module demo
/* tell SWIG to treat char ** as a list of strings */
%typemap(in) char ** {
// check if is a list
if(PyList_Check($input))
{
int size = PyList_Size($input);
int i = 0;
$1 = (char **)malloc((size + 1)*sizeof(char *));
for(i = 0; i < size; i++)
{
PyObject * o = PyList_GetItem($input, i);
if(PyString_Check(o))
$1[i] = PyString_AsString(o);
else
{
PyErr_SetString(PyExc_TypeError, "list must contain strings");
free($1);
return NULL;
}
}
}
else
{
PyErr_SetString(PyExc_TypeError, "not a list");
return NULL;
}
}
// clean up the char ** array
%typemap(freearg) char ** {
free((char *) $1);
}
2.generate extension
$ swig -python demo.i // generate wrap code
$ gcc -c -fpic demo.c demo_wrap.c
$ gcc -shared demo.o demo_wrap.o -o _demo.so
3.import the module in python.
>>> from demo import yourfunction
This is a pretty old question, but I think worth to add if people still search for similar question.
using numpy, would be probably easiest way for handling all the low level manipulations and linking with with libraries.
example = ["String1" , "String2" , "The last string"]
example_np = m = np.array(example, dtype=np.chararray)
when you build you numpy array of char*, you could just get the pointer to array (with ctypes and directly give it to the lib expecting char**.
example_ptr = ctypes.cast(example_np.ctypes.data, ctypes.POINTER(ctypes.c_char_p)) # this is char**
and you could just call
lib.external_C(len(example), example_ptr)
精彩评论