I've been given the task of polishing the interface of a codec library. We're using C++17, and I can only use the standard library (i.e. no Boost). Currently, there's a Decoder
class that looks roughly like this:
class Decoder : public Codec {
public:
struct Result {
vector<uint8_t>::const_iterator new_buffer_begin;
optional<Metadata> metadata;
optional<Packet> packet;
};
Result decode(vector<uint8_t>::const_iterator buffer_begin,
vector<uint8_t>::const_iterator buffer_end);
private:
// irrelevant details
};
The caller instantiates a Decoder
, then feeds a stream of data to the decoder by
Reading a chunk of data from a file (but there could be other sources in the future), and appending it to a
vector<uint8_t>
.Calling the
decode
function, passing the iterators for their vector.If the returned
Result
'snew_buffer_begin
is identical to thebuffer_begin
that was passed todecode
, that means there wasn't enough data in the buffer to decode anything, and the caller should go back to step 1. Otherwise, the caller consumes theMetadata
orPacket
object that was decoded, and goes back to step 2, usingnew_buffer_begin
for the next pass.
The things I dislike about this interface and need help improving:
Using
vector<uint8_t>::const_iterator
seems overly specific. Is there a more generic approach that doesn't force the caller to usevector
? I was considering just using C-style interface; auint8_t *
and a length. Is there a C++ alternative that's fairly generic?If there was enough data to decode something, only
metadata
orpacket
will have a value. I thinkstd::variant
or 2 callbacks (one for each type) would make this code more self-documenting. I'm not sure which is more idiomatic though. What are the pros and cons of each, and is there an even better approach?