如何用C#中的Swig包装UTF-8编码的C ++ std :: strings?

时间:2013-11-05 06:53:07

标签: c++ swig

我的问题几乎与this question相同,只是链接的问题处理char *,而我在代码中使用std :: string。就像链接的问题一样,我也使用C#作为我的目标语言。

我有一个用C ++编写的类:

class MyClass
{
public:
    const std::string get_value() const; // returns utf8-string
    void set_value(const std::string &value); // sets utf8-string
private:
    // ...
};

这个由SWIG在C#中包含如下:

public class MyClass
{
    public string get_value();
    public void set_value(string value);
}

SWIG为我做了一切,除了它在调用MyClass期间没有进行utf8到utf16字符串转换。如果它们在ASCII中可以表示,我的字符串会很好,但是如果我尝试通过“set_value”和“get_value”在往返中传递带有非ascii字符的字符串,我最终会得到难以理解的字符。

如何在C#中使用SWIG包装UTF-8编码的C ++字符串?注:我正在使用std :: string,而不是std :: wstring,而不是char *。

SWIG sourceforge site有部分解决方案,但它处理char *而不是std :: string,它使用(可配置的)固定长度缓冲区。

1 个答案:

答案 0 :(得分:3)

在链接的Code Project文章中David Jeske的帮助(阅读:天才!),我终于能够回答这个问题了。

您需要在C#库中使用此课程(来自David Jeske的代码)。

public class UTF8Marshaler : ICustomMarshaler {
    static UTF8Marshaler static_instance;

    public IntPtr MarshalManagedToNative(object managedObj) {
        if (managedObj == null)
            return IntPtr.Zero;
        if (!(managedObj is string))
            throw new MarshalDirectiveException(
                   "UTF8Marshaler must be used on a string.");

        // not null terminated
        byte[] strbuf = Encoding.UTF8.GetBytes((string)managedObj); 
        IntPtr buffer = Marshal.AllocHGlobal(strbuf.Length + 1);
        Marshal.Copy(strbuf, 0, buffer, strbuf.Length);

        // write the terminating null
        Marshal.WriteByte(buffer + strbuf.Length, 0); 
        return buffer;
    }

    public unsafe object MarshalNativeToManaged(IntPtr pNativeData) {
        byte* walk = (byte*)pNativeData;

        // find the end of the string
        while (*walk != 0) {
            walk++;
        }
        int length = (int)(walk - (byte*)pNativeData);

        // should not be null terminated
        byte[] strbuf = new byte[length];  
        // skip the trailing null
        Marshal.Copy((IntPtr)pNativeData, strbuf, 0, length); 
        string data = Encoding.UTF8.GetString(strbuf);
        return data;
    }

    public void CleanUpNativeData(IntPtr pNativeData) {
        Marshal.FreeHGlobal(pNativeData);            
    }

    public void CleanUpManagedData(object managedObj) {
    }

    public int GetNativeDataSize() {
        return -1;
    }

    public static ICustomMarshaler GetInstance(string cookie) {
        if (static_instance == null) {
            return static_instance = new UTF8Marshaler();
        }
        return static_instance;
    }
}

然后,在第24行的Swig" std_string.i"中替换此行:

%typemap(imtype) string "string"

这一行:

%typemap(imtype, inattributes="[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]", outattributes="[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]") string "string"

并在第61行上,替换此行:

%typemap(imtype) const string & "string"

这一行:

%typemap(imtype, inattributes="[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]", outattributes="[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]") string & "string"

瞧,一切正常。阅读链接的文章,以便更好地了解其工作原理。