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 ofuint8_t
, so:
or if you're in C++17, use the span implementation from the C++ Guidelines Support library:Result decode(std::span<const std::byte> buffer);
#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 aCodec
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.