Проблема с передачей указателя на строку ostringstream

По совету ChatGPT писал код для работы с cURL, в котором был фрагмент

    std::ostringstream postFields;
    // Долгая запись в postFields...
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postFields.str().c_str());

Все это не работало - тело запроса никак не передавалось. Как будто его нет. Путем проб и ошибок нашел, что проблема во второй строке.

Стоило сделать так:

    std::string g = postFields.str();
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, g.c_str());

или даже

    const std::string& g = postFields.str();
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, g.c_str());

как все отлично работало.

У меня подозрения, что это связано с какими-то тонкостями передачи - получить строку, а потом из нее указатель, и что-то по дороге портится, вроде как с возвратом локальной переменной из функции.

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


Ответы (1 шт):

Автор решения: HolyBlackCat

Начинаем с очевидных вещей:

std::ostringstream::str() возвращает строку по значению, эта строка умирает в конце текущего выражения (грубо говоря, на ближайшем ;, т.е. в конце текущей строки кода).

Это было бы нормально, если бы curl_easy_setopt делал себе копию строки, но судя по всему, он ее не делает, и расчитывает на то, что строка продолжит существовать.

И действительно, если открыть мануал:

Strings passed to libcurl as char * arguments, are copied by the library; the string storage associated to the pointer argument may be discarded or reused after curl_easy_setopt returns. The only exception to this rule is really CURLOPT_POSTFIELDS, but the alternative that copies the string CURLOPT_COPYPOSTFIELDS has some usage characteristics you need to read up on.

Т.е. если не хочется хранить строку самому, то нужно использовать CURLOPT_COPYPOSTFIELDS.


Ну и мелочь: Такой вызов .str() создает лишнюю копию строки. Правильнее так: std::move(postFields).str().

→ Ссылка