开发者

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)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜