6

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;
}
4
  • 1
    Do you understand this part of the documenatation? cython.readthedocs.io/en/latest/src/tutorial/…. I'm still working on it. Commented Mar 8, 2017 at 18:25
  • Thanks. Actually, I managed to pass the string following this table cython.readthedocs.io/en/latest/src/userguide/… . Is the use of libcpp.string and std::string, that I think you are proposing, better than passing bytes to char* and then converting the return value (with type bytes) to str? Commented Mar 8, 2017 at 18:45
  • The documentation page that I linked seems to have a low opinion of C char strings, especially since Py3 uses unicode. But what's best may depend on what you are doing with the strings in C++. Commented Mar 8, 2017 at 19:19
  • The fact is I am interfacing an old legacy code and I have still little experience with cython. If you may put the more suited way to do it with libcpp.string as an answer I can accept it. Commented Mar 8, 2017 at 19:26

1 Answer 1

6

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
Sign up to request clarification or add additional context in comments.

3 Comments

Is the memory that fun returns allocated using malloc? If it is then you may also need to free it too. If not then this should be fine.
In this particular case not, but it may be useful. How can I do it? Feel free to modify the answer accordingly.
I've edited it how you'd do this. If you want to structure it differently then feel free to change it too

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.