Преобразование массива строк C ++ / CLI в собственный символ C ++ ** - PullRequest
4 голосов
/ 25 августа 2011

В C ++ / CLI, какой самый эффективный способ преобразовать массив строк в собственный символ **?

Я делаю это:

array<String^>^ tokenArray = gcnew array<String^> {"TokenONE", "TokenTWO"};
int numTokens = tokenArray->Length;
char** ptr = new char* [numTokens];
for(int i = 0; i < numTokens; i++)
    {
        // See: http://stackoverflow.com/questions/6596242/
        array<Byte>^ encodedBytes = Text::Encoding::UTF8->GetBytes(tokenArray[i]);
        pin_ptr<Byte> pinnedBytes = &encodedBytes[0];
        ptr[i] = reinterpret_cast<char*>(pinnedBytes);
    }
int myResult = someNativeFunction(ptr, numTokens);
delete ptr;
// ...

Что, если что-то должно быть улучшено? Это нормально с точки зрения управления памятью? Я могу изменить параметры someNativeFunction при необходимости.

Спасибо.

1 Ответ

4 голосов
/ 25 августа 2011

Помимо проблемы, связанной с тем, что закрепленные указатели выходят из области видимости перед передачей на someNativeFunction(), код можно упростить для большей наглядности, особенно если вы используете MSVC2008 или новее.См. на этой странице для получения информации о том, как преобразовать одну строку (расширение до массива должно быть тривиальным).

Отредактировано:

Если вынужны строки ANSI const char*, тогда создание копии неизбежно, поскольку строки .NET имеют Unicode (UTF-16).На MSVC2008 и новее ваш код может выглядеть следующим образом:

#include <msclr/marshal.h>
using namespace msclr::interop;

marshal_context context;
array<String^>^ tokenArray = gcnew array<String^> {"TokenONE", "TokenTWO"};
char** tokensAsAnsi = new char* [tokenArray->Length];

for(int i = 0; i < tokenArray->Length; i++)
{
    tokensAsAnsi[i] = context.marshal_as<const char*>(tokenArray[i]);
}
int myResult = someNativeFunction(ptr, tokensAsAnsi);

// The marshalled results are freed when context goes out of scope
delete[] tokensAsAnsi;    // Please note you must use delete[] here!

Это похоже на ваш пример кода, но без необходимости закрепления указателя и reinterpret_cast -ing.

Есливы готовы работать с широкой строкой const wchar_t* в someNativeFunction(), вы можете напрямую использовать (закрепленные) внутренние данные. Однако вам нужно будет убедиться, что указатели остаются закрепленными до тех пор, пока не вернется someNativeFunction(), что, как указано вкомментарии, могут негативно повлиять на производительность GC.

Если вы собираетесь использовать marshall много строк и производительность крайне важна, вы можете разделить marshalling на несколько потоков, прежде чем передать все в someNativeFunction().Прежде чем сделать это, я бы рекомендовал профилировать ваше приложение, чтобы увидеть, является ли преобразование узким местом или лучше сосредоточить усилия в другом месте.

Отредактировано # 2:

Чтобы получить собственную строку в кодировке UTF-8, вы можете сделать с измененной версией вашего кода:

array<String^>^ tokenArray = gcnew array<String^> {"TokenONE", "TokenTWO"};
char** tokensAsUtf8 = new char* [tokenArray->Length];

for(int i = 0; i < tokenArray->Length; i++)
{
    array<Byte>^ encodedBytes = Text::Encoding::UTF8->GetBytes(tokenArray[i]);

    // Probably just using [0] is fine here
    pin_ptr<Byte> pinnedBytes = &encodedBytes[encodedBytes->GetLowerBound(0)];

    tokensAsUtf8[i] = new char[encodedBytes->Length + 1]; 
    memcpy(
        tokensAsUtf8[i], 
        reinterpret_cast<char*>(pinnedBytes),
        encodedBytes->Length
        );

    // NULL-terminate the native string
    tokensAsUtf8[i][encodedBytes->Length] = '\0'; 

}
int myResult = someNativeFunction(ptr, tokensAsAnsi);

for(int i = 0; i < tokenArray->Length; i++) delete[] tokensAsUtf8[i];
delete[] tokensAsUtf8;    

Если вас беспокоит скорость, вы можете предварительно выделить большой буфер для собственных строк (если вы знаете, что их будет только ограниченное количество) или использовать пул хранилища.

Отредактировано# 3: (О.Г. Чувак) Просто исправлены некоторые мелкие опечатки.

...