Search code examples
c++python-3.xcythoncythonize

How to pass string from Python3 to cythonized C++ function


I am trying to understand how to pass string values between Python3 and cythonized C++ function. However I am unable to build the library with Cython.

In particular, I didn't understand how to declare the string return value and string parameter in the source.pyx. With the int type it works correctly.

The error I get during build with clang is the following:

candidate function not viable: no known conversion from 'PyObject *' (aka '_object *') to 'char *' for 1st argument

My source.pyx is the following:

cdef extern from "source.cpp":
  cdef str fun(str param)

def pyfun(mystring):
  return fun(mystring)

My source.cpp is:

char * fun(char *string) {
  return string;
}

Solution

  • Aside from mistakes in the original code, I managed to make it work with the following source.pyx (translation between bytes type in Python3 and char* in C++):

    cdef extern from "source.cpp":
      cdef char* fun(char* param)
    
    def pyfun(mystring):
      mystring_b = mystring.encode('utf-8')
      rvalue = fun(mystring_b).decode('utf-8')
      return rvalue
    

    If the memory is allocated using malloc within fun it will also need to be freed, otherwise there will be a memory leak (when working with C pointers it's always worth considering who owns the memory). A modified version that does this is:

    from libc.stdlib cimport free
    
    # cdef extern as before
    
    def pyfun(mystring):
        cdef char* value_from_fun
        mystring_b = mystring.encode('utf-8')
        value_from_fun = fun(mystring_b)
        try:    
            return value_from_fun.decode('utf-8')
        finally:
            free(value_from_fun)
    

    The type conversions act in the same way as before.


    Edit

    As per hpaulj's comment in the original question, here is the version with libcpp.string which maps C++ std::string defined in <string>:

    from libcpp.string cimport string
    
    cdef extern from "source.cpp":
      cdef string fun(string param)
    
    def pyfun(mystring):
      mystring_ = mystring.encode('utf-8')
      rvalue = fun(mystring_).decode('utf-8')
      return rvalue