How to convert Python list of strings into C array of wchar_t?

自作多情 提交于 2019-12-11 15:28:22

问题


The source code in C looks like:

typedef wchar_t            char_t;
typedef const char_t*  const_string_t;
static const_string_t g_symbols[] = { {L"IBM"}, {L"MSFT"}, {L"YHOO"}, {L"C"} };
...
some_c_func(g_symbols)
...

some_c_func is declared somewhere earlier with something like:

int some_c_func(const_string_t* symbols)

It is important that g_symbols is passed to some_c_func function, so I have to write wrapper over it, that should look somehow like:

ctypedef wchar_t char_t
ctypedef const char_t*  const_string_t

def some_py_func(py_list=['a', 'b', 'c']):
    g_symbols = ... # some transformation from py_list to g_symbols
    some_c_func(g_symbols)
    print('Done!')

I will appreciate any help


回答1:


The easiest way to get a wchar* from a unicode object is probably PyUnicode_AsWideCharString. Cython doesn't provide a definition so you need to do a suitable cdef extern yourself:

 from libc.stddef cimport wchar_t
 from cpython.mem cimport PyMem_Free

 cdef extern from "Python.h":
     wchat_t* PyUnicode_AsWideCharString(object, Py_ssize_t*) except NULL

 def f(string):
     cdef wchar_t* c_string = PyUnicode_AsWideCharString(string, NULL)
     # use the string
     PyMem_Free(<void*>c_string) # you must free it after use

Read the documentation to see if you should use the "size" argument.

To allocate space for an array of wchar_t* you should use malloc or calloc. You should free this space when you're done with it. You need to cast from malloc

from libc.stdlib cimport malloc, free

cdef wchar_t** strings = <wchar_t**>malloc(sizeof(wchar_t*)*length)
# do something
free(<void*>strings)

A common pattern from ensuring memory is cleaned up to use try and finally:

def some_py_func(py_list):
    g_symbols = malloc(...)
    try:
        # loop through py_list getting wchar_t*
        # call your C function 
    finally:
        # loop through g_symbols calling PyMem_Free
        free(g_symbols)

You need to be careful that you only call PyMem_Free on valid (or NULL) pointers in the event of an exception. Remember that memory from malloc may be filled with arbitrary values that it isn't safe to pass to PyMem_Free.




回答2:


Thanks to @DavidW, but I found, I think, simplier solution:

from cpython.mem cimport PyMem_Malloc, PyMem_Free

def some_py_func(py_list=['a', 'b', 'c']):
    cdef int number = len(symbols)  # get c int of elements
    cdef int idx # for faster loops

    # create array with dynamic memory allocation
    cdef const_string_t *g_symbols = <const_string_t *> PyMem_Malloc(number * sizeof(const_string_t))

    # create array with cycle
    for idx, sym in enumerate(py_list):
        g_symbols[idx] = PyUnicode_AsWideCharString(sym, NULL)

    # call c function
    some_c_func(g_symbols)

    # free memory
    for idx in range(number):
        PyMem_Free(g_symbols[idx])
    PyMem_Free(g_symbols)
    print('Done!')


来源:https://stackoverflow.com/questions/56185968/how-to-convert-python-list-of-strings-into-c-array-of-wchar-t

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!