When using Boost.Asio operations that operate on streambuf or stream objects that use a streambuf, such as std::ostream and std::istream, the underlying input and output sequences will be properly managed. If a buffer is provided to an operation instead, such as passing passing prepare() to a read operation or data() to a write operation, then one must explicitly handle the commit() and consume().
The issue in the example is that it violates the API contract, causing uninitialized memory to be committed to the input sequence. The commit() documentation states:
Requires a preceding call prepare(x) where x >= n, and no intervening operations that modify the input or output sequence.
The use of the std::ostream between the prepare() and commit() violates this contract, as it will modify the input sequence:
// Prepare 1024 bytes for the output sequence. The input sequence is
// empty.
boost::asio::streambuf streambuf;
streambuf.prepare(1024);
// prepare() and write to the output sequence, then commit the written
// data to the input sequence. The API contract has been violated.
std::ostream ostream(&streambuf);
ostream << "1234567890";
// Commit 10 unspecified bytes to the input sequence. Undefined
// behavior is invoked.
streambuf.commit(10);
Here is a complete example demonstrating using streambuf with annotated comments:
#include <iostream>
#include <vector>
#include <boost/asio.hpp>
int main()
{
std::cout << "with streams:" << std::endl;
{
boost::asio::streambuf streambuf;
// prepare() and write to the output sequence, then commit the written
// data to the input sequence. The output sequence is empty and
// input sequence contains "1234567890".
std::ostream ostream(&streambuf);
ostream << "1234567890";
// Read from the input sequence and consume the read data. The string
// 'str' contains "1234567890". The input sequence is empty, the output
// sequence remains unchanged.
std::istream istream(&streambuf);
std::string str;
istream >> str;
std::cout << "str = " << str << std::endl;
// Clear EOF bit.
istream.clear();
// prepare() and write to the output sequence, then commit the written
// data to the input sequence. The output sequence is empty and
// input sequence contains "0987654321".
ostream << "0987654321";
// Read from the input sequence and consume the read data. The string
// 'str' contains "0987654321". The input sequence is empty, the output
// sequence remains unchanged.
istream >> str;
std::cout << "str = " << str << std::endl;
}
std::cout << "with streams and manual operations:" << std::endl;
{
boost::asio::streambuf streambuf;
// prepare() and write to the output sequence, then commit the written
// data to the input sequence. The output sequence is empty and
// input sequence contains "1234567890".
std::ostream ostream(&streambuf);
ostream << "1234567890";
// Copy 10 bytes from the input sequence. The string `str` contains
// "1234567890". The output sequence is empty and the input
// sequence contains "1234567890".
auto data = streambuf.data();
std::string str(boost::asio::buffers_begin(data),
boost::asio::buffers_begin(data) + 10);
std::cout << "str = " << str << std::endl;
// Consume 10 bytes from the input sequence. The input sequence is
// now empty.
streambuf.consume(10);
// prepare() and write to the output sequence, then commit the written
// data to the input sequence. The output sequence is empty and
// input sequence contains "0987654321".
ostream << "0987654321";
// Copy 10 bytes from the input sequence. The string `str` contains
// "0987654321. The output sequence is empty and the input
// sequence contains "0987654321".
data = streambuf.data();
str.assign(boost::asio::buffers_begin(data),
boost::asio::buffers_begin(data) + 10);
std::cout << "str = " << str << std::endl;
// Consume 10 bytes from the input sequence. The input sequence is
// now empty.
streambuf.consume(10);
}
}
Output:
with streams:
str = 1234567890
str = 0987654321
with streams and manual operations:
str = 1234567890
str = 0987654321
For more information on streambuf usage, consider reading this answer.