Copy a streambuf's contents to a string
I mostly don't like answers that say "You don't want X, you want Y instead and here's how to do Y" but in this instance I'm pretty sure I know what tstenner wanted.
In Boost 1.66, the dynamic string buffer type was added so async_read
can directly resize and write to a string buffer.
How copy or reuse boost::asio::streambuf?
One can use boost::asio::buffer_copy()
to copy the contents of Asio buffers. This can be convenient if, for example, one wishes to copy the contents of one streambuf
to another streambuf
.
boost::asio::streambuf source, target;
...
std::size_t bytes_copied = buffer_copy(
target.prepare(source.size()), // target's output sequence
source.data()); // source's input sequence
// Explicitly move target's output sequence to its input sequence.
target.commit(bytes_copied);
A similar approach can be used to copy from a streambuf
into any type for which Asio supports mutable buffers. For example, copying content into a std::vector<char>
:
boost::asio::streambuf source;
...
std::vector<char> target(source.size());
buffer_copy(boost::asio::buffer(target), source.data());
One notable exception is that Asio does not support returning a mutable buffer for std::string
. However, one can still accomplish copying into std::string
via iterators:
boost::asio::streambuf source;
...
std::string target{
buffers_begin(source.data()),
buffers_end(source.data())
};
Here is an example demonstrating copying contents from boost::asio::streambuf
into various other types:
#include <iostream>
#include <string>
#include <vector>
#include <boost/asio.hpp>
int main()
{
const std::string expected = "This is a demo";
// Populate source's input sequence.
boost::asio::streambuf source;
std::ostream ostream(&source);
ostream << expected;
// streambuf example
{
boost::asio::streambuf target;
target.commit(buffer_copy(
target.prepare(source.size()), // target's output sequence
source.data())); // source's input sequence
// Verify the contents are equal.
assert(std::equal(
buffers_begin(target.data()),
buffers_end(target.data()),
begin(expected)
));
}
// std::vector example
{
std::vector<char> target(source.size());
buffer_copy(boost::asio::buffer(target), source.data());
// Verify the contents are equal.
assert(std::equal(begin(target), end(target), begin(expected)));
}
// std::string example
{
// Copy directly into std::string. Asio does not support
// returning a mutable buffer for std::string.
std::string target{
buffers_begin(source.data()),
buffers_end(source.data())
};
// Verify the contents are equal.
assert(std::equal(begin(target), end(target), begin(expected)));
}
}
efficient copy of data from boost::asio::streambuf to std::string
reserve
doesn't mean what you think it means. You can work out for sure what it means by reading its documentation. It is not the same as resize
; reserving space does not affect the container's size.
You need to actually insert elements into the string. Do this:
#include <algorithm> // for copy
#include <iterator> // for back_inserter
_msg.reserve(_nBytesInMsg); // about to insert _nBytesInMsg elements
std::copy(
boost::asio::buffers_begin(buf),
boost::asio::buffers_begin(buf) + _nBytesInMsg,
std::back_inserter(_msg)
);
How to get part of a std::string into a streambuf without copying?
istream::operator>>() mangles the contents of my streambufs (as you would expect given that it's 'formatted').
Open your input stream with
std::ios::binary
flag and manipulate it withis >> std::noskipws
For example, if I want to get a substring of a string into a streambuf, how do I do that without creating a copy of the string? A basic all-in-all-out transfer can be accomplished like
Try like
outstream.write(s.begin()+start, length);
Or use
boost::string_ref
:outstream << boost::string_ref(s).instr(start, length);
And there doesn't seem to be any way of directly using string::iterators to dump part of a string into a streambuf:
std::copy(it1, it2, ostreambuf_iterator<char>(os));
Re. parsing the message lines:
You can split into iterator ranges with
iter_split
.You can parse an embedded grammar on the fly with
boost::spirit::istream_iterator
Converting boost streambuf to string_ref
I think this is, indeed, not correct, because the streambuf may have several non-contiguous regions.
So you need to copy anyways. Alternatively, just read into a fixed buffer instead. Of course, this requires you to know the maximum size ahead of time, or read in several steps.
By the way, by taking a
const&
you risk creating astring_ref
that refers to a temporary. Always try to be explicit about life time expectations.
Convert std::ostream to std::string
If you have a a
stringstream
or aostreamstring
, object or reference, just use theostringstream::str()
method that does exactly what you want: extract astring
. But you know that and you apparently have aostream
object, not astringstream
nor aostreamstring
.If your ostream reference is actually a
ostringstream
or astringstream
, useostream::rdbuf
to get astreambuf
, then use this post to see how to extract astring
.
Example:
#include <iostream>
#include <sstream>
std::string toString( std::ostream& str )
{
std::ostringstream ss;
ss << str.rdbuf();
return ss.str();
}
int main() {
std::stringstream str;
str << "Hello";
std::string converted = toString( str );
std::cout << converted;
return 0;
}
Live demo: http://cpp.sh/6z6c
- Finally, as commented by M.M, if your
ostream
is aofstream
or aniostream
, most likely it clears its buffer so you may not be able to extract the full content as proposed above.
Stream from std::string without making a copy?
The reason you're having these problem is that std::string isn't really suited to what you're doing. A better idea is to use vector of char when passing around raw data. If its possible, I would just change everything to use vector, using vector::swap and references to vectors as appropriatte to eliminate all your copying. If you like the iostreams/streambuf api, or if you have to deal with something that takes a streambuf, it would be trivial to create your own streambuf that uses a vector, like yours. It would effectively do the same thing that you do with the same issues as listed in the other answers, but you wouldn't be violating the class's contract.
Otherwise, I think what you've got is probably the best way forward short of passing around an istringstream everywhere.
Convert std::string to boost::asio::streambuf
The following code shows how to convert between std::string
and boost::asio::streambuf
, see it online at ideone:
#include <iostream>
#include <boost/asio/streambuf.hpp>
#include <boost/asio/buffer.hpp>
int main()
{
/* Convert std::string --> boost::asio::streambuf */
boost::asio::streambuf sbuf;
std::iostream os(&sbuf);
std::string message("Teststring");
os << message;
/* Convert boost::asio::streambuf --> std::string */
std::string str((std::istreambuf_iterator<char>(&sbuf)),
std::istreambuf_iterator<char>());
std::cout << str << std::endl;
}
Related Topics
Is There Any Way a C/C++ Program Can Crash Before Main()
Get Current Time in Milliseconds Using C++ and Boost
Is "Inline" Implicit in C++ Member Functions Defined in Class Definition
What Is an Iterator in General
Boost::Asio - How to Interrupt a Blocked Tcp Server Thread
Compile-Time or Runtime Detection Within a Constexpr Function
Conflict Between Dynamic Linking Priority in Osx
Subtle C++ Inheritance Error with Protected Fields
Is Sizeof(*Ptr) Undefined Behavior When Pointing to Invalid Memory
G++ How to Get Warning on Ignoring Function Return Value
Valgrind Memory Leak Errors When Using Pthread_Create
Avoiding Denormal Values in C++