2015-10-29 6 views
3

У меня есть следующий код в C++ на Win32. Это просто C++ перекос на некоторых Win32 API, который возвращает CHAR *:Инициировать std :: string с одной копией

wstring expandEnvironmentVariables(const wstring & str) 
{ 
    DWORD neededSize = ExpandEnvironmentStrings(str.c_str(), nullptr, 0); 
    vector<WCHAR> expandedStr(neededSize); 
    if (0 == ExpandEnvironmentStrings(str.c_str(), expandedStr.data(), static_cast<DWORD>(expandedStr.size()))) { 
     return wstring(str); 
    } 
    return wstring(expandedStr.data()); 
} 

Что беспокоит меня об этом коде, является двойной копией результата.

  1. по API в вектор WCHAR s.
  2. от вектора до std::wstring.

Есть ли способ реализовать этот код только с одной копией и без существенного изменения подписи функции. Это конкретный пример, но меня больше интересует общее решение и правильный способ работы с std::wstring/std::string, потому что этот шаблон проявляется во многих местах кода.

ответ

3

Что касается стороны C++, вы можете просто использовать wstring непосредственно как переменную результата.

Чтобы получить указатель на буфер wstring ненулевого размера, используйте только &s[0].

Точно так же, как std::vector, std::basic_string имеет гарантированный непрерывный буфер.

Для возврата он, вероятно, получит Оптимизацию возвращаемого значения (RVO), а если нет, то он будет перемещен.

Отказ от ответственности: Я не проверял документацию по функциям API. Я не знаю, правильный ли этот код или даже значимый. Я просто предполагаю это.

+0

@ Энже: Добро пожаловать. Я тоже был уверен, что однажды обнаружил упущение. Это странно. –

+0

Обычно я предпочитаю 'front' вместо' & s [0] '. Он работает даже в контейнерах типа 'list'. – edmz

+0

Но зачем вам указатель на первый узел в «списке»? –

2
wstring expandEnvironmentVariables(const wstring & str) 
{ 
    wstring expandedStr; 
    DWORD neededSize = ExpandEnvironmentStrings(str.c_str(), 
               nullptr, 0); 
    if (neededSize) 
    { 
     expandedStr.resize(neededSize); 
     if (0 == ExpandEnvironmentStrings(str.c_str(), 
             &expandedStr[0], 
             neededSize)) 
     { 
      // pathological case requires a copy 
      expandedStr = str; 
     } 
    } 
    // RVO here 
    return expandedStr; 
} 

EDIT:

Поразмыслив, так как мы используем C++ пойдём на всю катушку и поставить в исправном обнаружения ошибок и сообщения об ошибках с информативной вложенной цепи исключения:

DWORD check_not_zero(DWORD retval, const char* context) 
{ 
    if(!retval) 
     throw std::system_error(GetLastError(), 
           std::system_category(), 
           context); 
    return retval; 
} 

std::wstring expandEnvironmentVariables(const std::wstring & str) 
try 
{ 
    DWORD neededSize = check_not_zero(ExpandEnvironmentStrings(str.c_str(), 
                   nullptr, 
                   0), 
             "ExpandEnvironmentStrings1"); 

    std::wstring expandedStr(neededSize, 0); 
    check_not_zero(ExpandEnvironmentStrings(str.c_str(), 
              &expandedStr[0], 
              neededSize), 
        "ExpandEnvironmentStrings2"); 

    // RVO here 
    return expandedStr; 
} 
catch(...) 
{ 
    std::throw_with_nested(std::runtime_error("expandEnvironmentVariables() failed")); 
} 
+0

Было бы разумно проверить размер 0. Выражение '& s [0]' является UB для этого размера. –

+0

исправлено. спасибо –

+0

@ Cheersandhth.-Alf переосмыслил обработку ошибок. Лучше конвертировать ошибки os в system_error, на мой взгляд. –