0

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

Ivan Mishalkin
  • 1,049
  • 9
  • 25
  • This is not a valid strategy to delete a question and to repost it: https://stackoverflow.com/questions/55865245/how-to-convert-array-of-chars-from-python-to-c-in-cython. Given that in the old version you was given feedback what to improve in the question to make it understandable and even got an educated guess by DavidW for a possible reason for your problem, we have to start from the scratch now. You are supposed to improve your question and not to discard the given feedback. – ead May 18 '19 at 08:46
  • I vote to close this question as too broad, because it consist of multiple subquestions: If you don't know how to get/pass an `wchar_t` from unicode-objectresearch it on SO/enywhere else and ask a **specific** and **precise** question(see also [mcve] ) about what stays unclear. If you don't know what `wchar_t**` is and how to handle it/to populate it with values - use the same approach as above: do research and ask a **specific** question. If you don't know how to manage memory - ask specific question about it (after having done research). – ead May 18 '19 at 08:53

2 Answers2

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.

DavidW
  • 29,336
  • 6
  • 55
  • 86
  • 1
    it is probably safer/easier to allocate memory with `calloc` - this way `strings` are filled with `NULL`s and it is easier to decide whether `PyMem_Free` should be called. – ead May 21 '19 at 09:09
-1

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!')
Ivan Mishalkin
  • 1,049
  • 9
  • 25