Установка внутреннего буфера, используемого стандартным потоком (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;
}
Ответ 2
Похож на работу (официально устаревшая, но все же стандартная) std :: strstream. Вы также можете посмотреть библиотеку Boost.IOStreams, array_sink, в частности.
Ответ 3
Как говорится в опубликованной вами ссылке: "конкретные реализации могут отличаться".
Не можете ли вы просто вернуть объект std::string, а затем использовать std::string:: c_str() или std::string:: data() в точке, где требуется буфер char?
Альтернативно использовать sprintf() из библиотеки C, тогда вся операция может быть завершена в переданном буфере. Так как это может привести к переполнению потенциального буфера, и вы используете Visual С++, вы можете рассмотреть sprintf_s