Wrapping C++ classes that contain wxString with Cython
Question: I'm working on a Python extension to tie in with a C++ application written
using wxWidgets for the GUI. I'm using Cython, and have the basic system
(build tools, plus a starter extension with appropriate version details etc)
happily working.

I'm only interested in making backend (non-GUI) functionality available, such
as file parsing and processing. However, all classes - not just the GUI ones -
use `wxString` for string data, such as in the following minimal example:

    
    
    #include <wx/string.h>
    
    class MyClass{
        wxString name;
        wxString get_name(){
            return this->name;
        }
    };
    

My question is what is the best way to go about wrapping such a class? Is
there a simple way to interface between a Python string and a `wxString`
instance? Or will I need to wrap the `wxString` class as well? Am I able to
tie in somehow with the wxPython port to avoid re-inventing the wheel?


Answer: I got it to work by using the static `wxString::FromUTF8()` function to
convert from Python to wxString, and the `wxString.ToUTF8()` to go in the
other direction. The following is the code I came up with:

    
    
    # Import the parts of wxString we want to use.
    cdef extern from "wx/string.h":
        cdef cppclass wxString:
            char* ToUTF8()
    
    
    # Import useful static functions from the class.
    cdef extern from "wx/string.h" namespace "wxString":
       wxString FromUTF8(char*)
    
    
    # Function to convert from Python string to wxString. This can be given either
    # a unicode string, or a UTF-8 encoded byte string. Results with other encodings
    # are undefined and will probably lead to errors.
    cdef inline wxString from_python(python_string):
        # If it is a Python unicode string, encode it to a UTF-8 byte string as this
        # is how we will pass it to wxString.
        if isinstance(python_string, unicode):
            byte_string = python_string.encode('UTF-8')
    
        # It is already a byte string, and we have no choice but to assume its valid
        # UTF-8 as theres no (sane/efficient) way to detect the encoding.
        else:
            byte_string = python_string
    
        # Turn the byte string (which is still a Python object) into a C-level char*
        # string.
        cdef char* c_string = byte_string
    
        # Use the static wxString::FromUTF8() function to get us a wxString.
        return FromUTF8(c_string)
    
    
    # Function to convert a wxString to a UTF-8 encoded Python byte string.
    cdef inline object to_python_utf8(wxString wx_string):
        return wx_string.ToUTF8()
    
    
    # Function to convert a wxString to a Python unicode string.
    cdef inline object to_python_unicode(wxString wx_string):
        # Since the wxString.ToUTF8() method returns a const char*, we'd have to try
        # and cast it if we wanted to do it all in here. I've tried this and can't
        # seem to get it to work. But calling the to_python_utf8() function
        # means Cython handles the conversions and it all just works. Plus, since
        # they are defined as inline functions this may well be simplified down when
        # compiled.
        byte_string = to_python_utf8(wx_string)
    
        # Decode it to a unicode string and we're done.
        return byte_string.decode('UTF-8')
    

Simply put this in a .pxd file (personally, I put it in a subdirectory as
`wx/string.pxd` \- make sure you also create an `wx/__init__.pdx` if you
choose to do the same). Then `cimport` it and call the functions as
appropriate:

    
    
    cimport wx.string
    
    wx_string = wx.string.from_python(python_string)
    python_string = wx.string.to_python_unicode(wx_string)
    

