Search code examples
c++protocol-buffersostreamstreambuf

Accept binary data via ostream interface and buffer into vector<uint_8>


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

  1. implements the ostream interface for accepting data as required by Protobuf API, but
  2. saves a verbatim copy into std::vector<uint_8> which I can later retrieve.
    • In future I might want to write the serialized bytes directly into UART as they come

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.


Solution

  • 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;
    };