Установка внутреннего буфера, используемого стандартным потоком (pubsetbuf)

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

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

#include <sstream>
#include <algorithm>

void FillBuffer(char* buffer, unsigned int size)
{
    std::stringstream message;
    message << "Hello" << std::endl;
    message << "World!" << std::endl;

    std::string messageText(message.str());
    std::copy(messageText.begin(), messageText.end(), buffer);
}

Это когда я обнаружил метод streambuf::pubsetbuf() и просто переписал вышеуказанный код следующим образом.

#include <sstream>

void FillBuffer(char* buffer, unsigned int size)
{
    std::stringstream message;
    message.rdbuf()->pubsetbuf(buffer, size);

    message << "Hello" << std::endl;
    message << "World!" << std::endl;
}

К сожалению, это не работает при реализации стандартной библиотеки С++, которая поставляется с Visual Studio 2008; buffer остается неизменным.

Я посмотрел на реализацию pubsetbuf, и получается, что он буквально "ничего не делает".

virtual _Myt *__CLR_OR_THIS_CALL setbuf(_Elem *, streamsize)
{   // offer buffer to external agent (do nothing)
    return (this);
}

Это, по-видимому, является ограничением данной реализации стандартной библиотеки С++. Каков рекомендуемый способ настройки потока для записи его содержимого в заданный буфер?

Ответ 1

После еще нескольких исследований этой проблемы и проверки моего кода я наткнулся на сообщение, предлагая использовать ручную кодировку std::streambuf. Идея этого кода состоит в том, чтобы создать streambuf, который инициализирует его внутренности, чтобы ссылаться на данный буфер. Код выглядит следующим образом.

#include <streambuf>

template <typename char_type>
struct ostreambuf : public std::basic_streambuf<char_type, std::char_traits<char_type> >
{
    ostreambuf(char_type* buffer, std::streamsize bufferLength)
    {
        // set the "put" pointer the start of the buffer and record it length.
        setp(buffer, buffer + bufferLength);
    }
};

Теперь, если вы посмотрите на мой исходный код, вы заметите, что для начала мне не нужен stringstream. Все, что мне действительно нужно, это способ записи во внешний буфер с помощью библиотеки IOStream и std::ostream - гораздо лучший тип для решения этой проблемы. Кстати, я подозреваю, что это array_sink типа из Boost.IOStreams.

Вот модифицированный код, который использует мой тип ostreambuf.

#include <ostream>
#include "ostreambuf.h"  // file including ostreambuf struct from above.

void FillBuffer(char* buffer, unsigned int size)
{
    ostreambuf<char> ostreamBuffer(buffer, size);
    std::ostream messageStream(&ostreamBuffer);

    messageStream << "Hello" << std::endl;
    messageStream << "World!" << std::endl;
}

Ответ 3

Как говорится в опубликованной вами ссылке: "конкретные реализации могут отличаться".

Не можете ли вы просто вернуть объект std::string, а затем использовать std::string:: c_str() или std::string:: data() в точке, где требуется буфер char?

Альтернативно использовать sprintf() из библиотеки C, тогда вся операция может быть завершена в переданном буфере. Так как это может привести к переполнению потенциального буфера, и вы используете Visual С++, вы можете рассмотреть sprintf_s