Передача строк wchar_t из / в родной плагин — работает только в Windows?

Что я в основном хочу сделать, это передать string возражать против родного плагина, а также получить string объект назад (не обязательно из / в ту же функцию, но это не имеет значения в конце концов).

После некоторой работы я остановился на чем-то вроде этого (упрощенный пример):

EXPORT const wchar_t* myNativeFunction(const wchar_t* param)
{
// Allocate the memory for the return value (to be freed by the Mono Runtime)
#ifdef WIN32
wchar_t *result = static_cast<wchar_t *>(CoTaskMemAlloc((result_length + 1) * sizeof(wchar_t)));
#else
wchar_t *result = static_cast<wchar_t *>(malloc((result_length + 1) * sizeof(wchar_t)));
#endif

// Here I'd copy the actual results with a length of 'result_length'
return result;
}

Бэкенд C # в сценарии Unity выглядит следующим образом:

[DllImport(nativeLibrary, EntryPoint = "myNativeFunction", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string myNativeFunction([MarshalAs(UnmanagedType.LPWStr)] string param);

При компиляции и запуске под Windows это работает просто отлично. Я получаю ожидаемую строку в нативную библиотеку и получаю правильную строку обратно, независимо от того, есть там не-ANSI символы или нет.

Однако, пробуя тот же код в MacOS Sierra сейчас, похоже, это не работает должным образом Для упрощения я сократил тестовую функцию, чтобы она возвращала только L"Test" скопировать в выходной буфер (result в приведенном выше примере). Однако в Unity я получаю строку "T" только.

В целом, для меня это выглядит странной проблемой с кодировкой, но я не могу ее решить. Я что-то пропустил?

Обновить: Я был в состоянии идентифицировать проблему, и это действительно проблема кодирования. Моно будет всегда принять ширину wchar_t составляет 2 байта (что в Windows), но это сломается в Unix и MacOS, так как они используют wchar_t, что составляет 4 байта в ширину. Ищете аккуратное решение, которое не требует слишком много накладных расходов.

1

Решение

После публикации вопроса я потратил довольно много часов, пытаясь реализовать правильное (и динамическое) преобразование на основе codecvt заголовок в сочетании с std::wstring_convert, Хотя это прекрасно работало, у него был один существенный недостаток: на момент написания статьи std::wstring_convert похоже, отсутствует во всех (иначе совместимых с C ++ 11) компиляторах, кроме Visual Studio и Clang (использующих libc ++). Так что это могло бы быть стандартным совместимым решением, но оно не очень практично, если его практически невозможно использовать, и все же требует от вас прыжков (лично я отказался от попытки кросс-компиляции кода x86 в этой настройке под Debian).

Так что вместо этого я закончил свою собственную и очень простую повторную реализацию преобразования. Под Windows он ничего не делает и просто передает строки напрямую. Это также намного удобнее в использовании по сравнению с std::wstring_convert,

#if WIN32 // wchar_t *is* UTF-16
#define allocate(x) CoTaskMemAlloc(x)

std::wstring convert(const char16_t* text) {
return reinterpret_cast<const wchar_t*>(text);
}

std::u16string convert(const wchar_t* text) {
return reinterpret_cast<const char16_t*>(text);
}

#else
#define allocate(x) malloc(x)

std::wstring convert(const char16_t* text) {
std::wstring ret;
const std::size_t maxlen = std::char_traits<char16_t>::length(text);
if (maxlen == 0)
return ret;

ret.reserve(maxlen);

for (char16_t s = *text; s; s = *(++text)) {
if (s < 0xd800) {
ret += s;
}
else {
wchar_t v = (s - 0xd800) * 0x400;
s = *(++text);
v += (s - 0xdc00) + 0x10000;
ret += v;
}
}

return ret;
}

std::u16string convert(const wchar_t* text) {
std::u16string ret;
const std::size_t minlen = std::char_traits<wchar_t>::length(text);
if (minlen == 0)
return ret;

ret.reserve(minlen);

for (wchar_t v = *text; v; v = *(++text)) {
if (v < 0x10000) {
ret += v;
}
else {
ret += (v - 0x10000) / 0x400 + 0xd800;
ret += (v - 0x10000) % 0x400 + 0xdc00;
}
}

return ret;
}

#endif

Использование довольно просто:

void StringFromCSharp(const char16_t* text) {
const std::wstring wtext(convert(text));
SomeWideCharStuff(wtext.c_str());
}

const char16_t* StringToCSharp() {
const std::wstring(SomeWideCharReturningStuff());
const std::u16string converted(convert(buffer));
char16_t *result = static_cast<char16_t*>(allocate((converted.length() + 1) * sizeof(char16_t)));
memcpy(result, converted.c_str(), (converted.length() + 1) * sizeof(char16_t));
return result;
}

Конечно, есть место для оптимизации, и в зависимости от фактического варианта использования могут быть более эффективные способы сделать это с пустым перераспределением и т. Д.

Несмотря на это, не стесняйтесь использовать это в своих собственных проектах, но имейте в виду, что я мог где-то упустить какой-то важный момент, хотя пока он работает нормально для меня.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]