How should I replace vector<uint8_t>::const_iterator in an API?

I agree that mandating vector is inappropriate, and applaud your attempts to make the interface more useful.

If decode expects a contiguous sequence of uint8_t, the tried-and-tested (and most flexible) solution is just to take a const uint8_t* and a std::size_t (or alternatively two pointers, but pointer and length is more idiomatic).

From C++20 you can do this with one argument of type std::span<const uint8_t>. Or going back to pointers, if you really want to use modern library tools for the sake of it, you can confuse people with std::experimental::observer_ptr.

You may also consider making decode a template that accepts any iterator pair, and (if contiguity is needed) mandates, even if only by documentation, that the iterators reflect a contiguous sequence. But making everything a template isn't always what you want, and it isn't always useful.


In addition to @Justin's valid suggestion of spans:

  • You might also want to consider using std::byte instead of uint8_t, so:
    Result decode(std::span<const std::byte> buffer);
    
    or if you're in C++17, use the span implementation from the C++ Guidelines Support library:
    #include <gsl/span>
    // etc.
    Result decode(gsl::span<const std::byte> buffer);
    
  • If you want to support decoding from containers other than raw memory, use arbitrary iterators (in C++17 and earlier) or possibly ranges (in C++20). The iterator version:

    template <typename InputIt>
    Result decode(InputIt start, InputIt end) { /* etc. */ }
    
  • It's fishy that a Decoder inherits from a Codec rather than the other way around.

  • The question of whether callbacks are a good choice or not is something that's difficult (for me) to answer without seeing the code. But do indeed use an std::variant to express the fact you have either a Packet or Metadata; you could also "combine" alternatives if instead of callbacks you use variants' std::visit.

C++20 will have std::span, which does what you want:

    Result decode(std::span<uint8_t const> buffer);

std::span<T> is semantically equivalent to a T* buffer, size_t size.


In C++17, there are some implementations of a span type which are equivalent to std::span, such as the GSL's gsl::span. See What is a "span" and when should I use one? .

If you can't use any external libraries, consider writing your own span type, else uint8_t const* buffer_begin, uint8_t const* buffer_end can work.