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
decodefunction, passing the iterators for their vector.If the returned
Result'snew_buffer_beginis identical to thebuffer_beginthat 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 theMetadataorPacketobject that was decoded, and goes back to step 2, usingnew_buffer_beginfor the next pass.
The things I dislike about this interface and need help improving:
Using
vector<uint8_t>::const_iteratorseems 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
metadataorpacketwill have a value. I thinkstd::variantor 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?