Here is what I would like to do:
std::stringstream s;
s<<"Some "<<std::hex<<123<<" hex data"<<...;
Having this s, I would very much like to pass it around, and that is possible easily. However, at some point, there is a need to (conceptually) pass it to an interface that only accepts const void */size_t pair which describes the memory region.
As far as I see (would love to stand corrected), there is no way to do that with 0-copy in C++17 and below. One must use .str() which will create a string copy and then take it from there.
As a "hack", this is what I came up with:
struct stringstream_extractor_t {
// this structure will be used to get access to pbase member function which is protected
// the way we do that is to inherit from it
template <typename T>
struct accessor_t : public T {
static const char* data(const accessor_t* pT) { return pT->pbase(); }
};
// get the return type for std::stringstream::rdbuf
using bufferType = std::remove_pointer<decltype(((std::stringstream*)nullptr)->rdbuf())>::type;
// having the std::stringstream::rdbuf result type, we can now create our accessor type
// which will be able to call pbase inside it
using accessorType = accessor_t<bufferType>;
// this is where we deposit the data, in a const manner
const std::string_view _data;
// syntactic sugar: init the _data with the stuff from stream reference
stringstream_extractor_t(std::stringstream& stream) : _data{getBuffer(stream), static_cast<size_t>(stream.tellp())} {}
// syntactic sugar: init the _data with the stuff from stream pointer
stringstream_extractor_t(std::stringstream* pStream) :
_data{pStream ? getBuffer(*pStream) : nullptr, pStream ? static_cast<size_t>(pStream->tellp()) : 0} {}
// this uses the accessor type to grab access to data
static const char* getBuffer(const std::stringstream& stream) {
// we get the buffer and we cast it to our accessor type. This is safe, as we do not
// have any members inside it, just a static function
const accessorType* pBuffer = reinterpret_cast<accessorType*>(stream.rdbuf());
// grab the data now
return accessorType::data(pBuffer);
}
// convenience functionality
inline const char* data() const { return _data.data(); }
inline size_t size() const { return _data.size(); }
};
And here is how it is used, with a C-like interface
std::stringstream s;
s << "Hi there! " << std::hex << 0xdeadbeef;
const stringstream_extractor_t e(s);
write(2, e.data(), e.size());
Yes, I'm aware of the fact that the pointers must be kept alive (the std::stringstream instance), and all the life cycle implications.
Is there a more comfortable non-convoluted way to achieve this quite basic thing: get a buffer out from an output stringstream with move semantics.
I'm clearly missing something, this should not be this hard.