Motivation for this question: the C++ API to Google's Protobuf serializes its messages into ostream
:
bool SerializeToOstream(ostream* output) const;
: writes the message to the given C++ ostream.
That's fine and good, but now I need to retrieve the serialized message as std::vector<uint_8>
and transmit it (I'm talking to some embedded device via UART, but it doesn't really matter).
My question: how to create a class which
ostream
interface for accepting data as required by Protobuf API, butstd::vector<uint_8>
which I can later retrieve.
This is the class I've naively written, thinking "how hard could it be?":
class ByteStreamBuf : public std::basic_streambuf<uint8_t>, public std::ostream {
public:
const std::vector<uint8_t>& getByteBuf() const {
return _byteBuf;
}
private:
std::basic_streambuf<uint8_t>::int_type overflow(std::basic_streambuf<uint8_t>::int_type c) override {
printf("Got byte 0x%02hhX\n", c);
_byteBuf.push_back(static_cast<uint8_t>(c));
return std::basic_streambuf<uint8_t>::overflow(c);
}
std::vector<uint8_t> _byteBuf;
};
Expected usage would be:
my_protocol::MyMessage msg; // Create Protobuf message
msg.set_someint(123); // Fill in some data in Protobuf message
ByteStreamBuf buf; // Create the byte buffer
if (msg.SerializeToOstream(&buf)) { // Serialize Protobuf message into byte buffer
transmitMessage(buf); // Transmit serialized bytes
} else {
printf("SerializeToOstream() failed!\n");
}
This compiles and runs using gcc 10, however Protobuf library fails to serialize its message: SerializeToOstream()
returns false
. Also I don't see my overridden ByteStreamBuf::overflow()
function called, because it never outputs its message "Got byte 0x..".
I failed to find the code in Protobuf C++ library which actually writes into this ostream (there are many, many layers and generated code involved) so I can only hope that it uses ostream::put()
method instead of ostream::operator<<
for writing binary, but I can't tell. Maybe this changes the regular flow of data in streambuf?
What am I doing wrong?
Edit: I'm aware that I can feed Protobuf a std::stringstream
, then request from it an std::string
and convert that to std::vector<uint8_t>
. In fact, that's what I'm doing right now and wish to replace with a more elegant and less error prone conversion. I would assume it's not difficult, I just can't see the solution right now.
It turns out the solution was very simple. I needed to construct my ostream
with a pointer to streambuf
(as explained here) and everything started to work.
This is the answer in my case (slightly redacted from original code):
class ByteStream : private std::streambuf, public std::ostream {
public:
ByteStream()
: std::streambuf()
, std::ostream(this)
{}
const std::vector<uint8_t>& getBytes() const {
return _bytes;
}
private:
std::streambuf::int_type overflow(std::streambuf::int_type c) override {
_bytes.push_back(static_cast<uint8_t>(c));
return 0;
}
std::vector<uint8_t> _bytes;
};