How should I replace vector::const_iterator in an API? Announcing the arrival of Valued Associate #679: Cesar Manara Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern) Data science time! April 2019 and salary with experience The Ask Question Wizard is Live!What is a “span” and when should I use one?Singleton: How should it be usedHow to find out if an item is present in a std::vector?How do I erase an element from std::vector<> by index?How to replace all occurrences of a character in string?How to convert vector to arrayHow to correctly implement custom iterators and const_iterators?Interface-based programming in C++ in combination with iterators. How too keep this simple?How to implement the factory method pattern in C++ correctlyHow to print out the contents of a vector?Vector, iterators and const_iterator

What's the meaning of "fortified infraction restraint"?

How to find all the available tools in mac terminal?

Maximum summed powersets with non-adjacent items

How to Make a Beautiful Stacked 3D Plot

How to react to hostile behavior from a senior developer?

What are the out-of-universe reasons for the references to Toby Maguire-era Spider-Man in ITSV

When the Haste spell ends on a creature, do attackers have advantage against that creature?

Integration Help

Is it cost-effective to upgrade an old-ish Giant Escape R3 commuter bike with entry-level branded parts (wheels, drivetrain)?

Would "destroying" Wurmcoil Engine prevent its tokens from being created?

Generate an RGB colour grid

First console to have temporary backward compatibility

When a candle burns, why does the top of wick glow if bottom of flame is hottest?

How to convince students of the implication truth values?

How can I use the Python library networkx from Mathematica?

Why are the trig functions versine, haversine, exsecant, etc, rarely used in modern mathematics?

Can an alien society believe that their star system is the universe?

Can a party unilaterally change candidates in preparation for a General election?

If a VARCHAR(MAX) column is included in an index, is the entire value always stored in the index page(s)?

How does the math work when buying airline miles?

Is grep documentation wrong?

How do I stop a creek from eroding my steep embankment?

Has negative voting ever been officially implemented in elections, or seriously proposed, or even studied?

How would a mousetrap for use in space work?



How should I replace vector::const_iterator in an API?



Announcing the arrival of Valued Associate #679: Cesar Manara
Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern)
Data science time! April 2019 and salary with experience
The Ask Question Wizard is Live!What is a “span” and when should I use one?Singleton: How should it be usedHow to find out if an item is present in a std::vector?How do I erase an element from std::vector<> by index?How to replace all occurrences of a character in string?How to convert vector to arrayHow to correctly implement custom iterators and const_iterators?Interface-based programming in C++ in combination with iterators. How too keep this simple?How to implement the factory method pattern in C++ correctlyHow to print out the contents of a vector?Vector, iterators and const_iterator



.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;








14















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



  1. 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>.


  2. Calling the decode function, passing the iterators for their vector.


  3. If the returned Result's new_buffer_begin is identical to the buffer_begin that was passed to decode, 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 the Metadata or Packet object that was decoded, and goes back to step 2, using new_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 use vector? I was considering just using C-style interface; a uint8_t * and a length. Is there a C++ alternative that's fairly generic?


  • If there was enough data to decode something, only metadata or packet will have a value. I think std::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?










share|improve this question

















  • 2





    Is there a C++ alternative that's fairly generic? Templates.

    – tkausl
    Apr 13 at 22:14











  • typedef vector<uint8_t>::const_iterator it_t; or using it_t= vector<uint8_t>::const_iterator; will make it cleaner.

    – Mirko
    Apr 13 at 22:18






  • 1





    I like the callback approach, passing a consumer object with a callback for each kind of result produced. When the method return you give the guaranty that at most one callback has been called. But you could also have an async variant. The API could evolve by adding more callback to the consumer. std::variant is also good but may require the user to check which one is available (doesn't really change from two optionals).

    – semako
    Apr 13 at 22:39

















14















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



  1. 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>.


  2. Calling the decode function, passing the iterators for their vector.


  3. If the returned Result's new_buffer_begin is identical to the buffer_begin that was passed to decode, 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 the Metadata or Packet object that was decoded, and goes back to step 2, using new_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 use vector? I was considering just using C-style interface; a uint8_t * and a length. Is there a C++ alternative that's fairly generic?


  • If there was enough data to decode something, only metadata or packet will have a value. I think std::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?










share|improve this question

















  • 2





    Is there a C++ alternative that's fairly generic? Templates.

    – tkausl
    Apr 13 at 22:14











  • typedef vector<uint8_t>::const_iterator it_t; or using it_t= vector<uint8_t>::const_iterator; will make it cleaner.

    – Mirko
    Apr 13 at 22:18






  • 1





    I like the callback approach, passing a consumer object with a callback for each kind of result produced. When the method return you give the guaranty that at most one callback has been called. But you could also have an async variant. The API could evolve by adding more callback to the consumer. std::variant is also good but may require the user to check which one is available (doesn't really change from two optionals).

    – semako
    Apr 13 at 22:39













14












14








14








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



  1. 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>.


  2. Calling the decode function, passing the iterators for their vector.


  3. If the returned Result's new_buffer_begin is identical to the buffer_begin that was passed to decode, 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 the Metadata or Packet object that was decoded, and goes back to step 2, using new_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 use vector? I was considering just using C-style interface; a uint8_t * and a length. Is there a C++ alternative that's fairly generic?


  • If there was enough data to decode something, only metadata or packet will have a value. I think std::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?










share|improve this question














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



  1. 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>.


  2. Calling the decode function, passing the iterators for their vector.


  3. If the returned Result's new_buffer_begin is identical to the buffer_begin that was passed to decode, 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 the Metadata or Packet object that was decoded, and goes back to step 2, using new_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 use vector? I was considering just using C-style interface; a uint8_t * and a length. Is there a C++ alternative that's fairly generic?


  • If there was enough data to decode something, only metadata or packet will have a value. I think std::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?







c++ c++17 binary-data idiomatic






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Apr 13 at 22:13









splicersplicer

3,89943546




3,89943546







  • 2





    Is there a C++ alternative that's fairly generic? Templates.

    – tkausl
    Apr 13 at 22:14











  • typedef vector<uint8_t>::const_iterator it_t; or using it_t= vector<uint8_t>::const_iterator; will make it cleaner.

    – Mirko
    Apr 13 at 22:18






  • 1





    I like the callback approach, passing a consumer object with a callback for each kind of result produced. When the method return you give the guaranty that at most one callback has been called. But you could also have an async variant. The API could evolve by adding more callback to the consumer. std::variant is also good but may require the user to check which one is available (doesn't really change from two optionals).

    – semako
    Apr 13 at 22:39












  • 2





    Is there a C++ alternative that's fairly generic? Templates.

    – tkausl
    Apr 13 at 22:14











  • typedef vector<uint8_t>::const_iterator it_t; or using it_t= vector<uint8_t>::const_iterator; will make it cleaner.

    – Mirko
    Apr 13 at 22:18






  • 1





    I like the callback approach, passing a consumer object with a callback for each kind of result produced. When the method return you give the guaranty that at most one callback has been called. But you could also have an async variant. The API could evolve by adding more callback to the consumer. std::variant is also good but may require the user to check which one is available (doesn't really change from two optionals).

    – semako
    Apr 13 at 22:39







2




2





Is there a C++ alternative that's fairly generic? Templates.

– tkausl
Apr 13 at 22:14





Is there a C++ alternative that's fairly generic? Templates.

– tkausl
Apr 13 at 22:14













typedef vector<uint8_t>::const_iterator it_t; or using it_t= vector<uint8_t>::const_iterator; will make it cleaner.

– Mirko
Apr 13 at 22:18





typedef vector<uint8_t>::const_iterator it_t; or using it_t= vector<uint8_t>::const_iterator; will make it cleaner.

– Mirko
Apr 13 at 22:18




1




1





I like the callback approach, passing a consumer object with a callback for each kind of result produced. When the method return you give the guaranty that at most one callback has been called. But you could also have an async variant. The API could evolve by adding more callback to the consumer. std::variant is also good but may require the user to check which one is available (doesn't really change from two optionals).

– semako
Apr 13 at 22:39





I like the callback approach, passing a consumer object with a callback for each kind of result produced. When the method return you give the guaranty that at most one callback has been called. But you could also have an async variant. The API could evolve by adding more callback to the consumer. std::variant is also good but may require the user to check which one is available (doesn't really change from two optionals).

– semako
Apr 13 at 22:39












3 Answers
3






active

oldest

votes


















16














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.






share|improve this answer




















  • 2





    There's many, many algorithms in the standard library that expect a contiguous sequence of some type (to name just one: std::sort). But as far as I can see, none of them takes a T* and length, they all use iterators. Seems very error prone.

    – Voo
    Apr 14 at 9:00






  • 3





    @Voo: A T* is an iterator... and the second iterator you pass is raw_pointer + length - another pointer.

    – einpoklum
    Apr 14 at 9:01






  • 1





    @einpoklum Every T* is an iterator but not every iterator is a T*, which is pretty much my point.

    – Voo
    Apr 14 at 9:01







  • 6





    @Voo, why do you think, that std::sort expects contiguous memory? It requires just random access iterators.

    – magras
    Apr 14 at 9:03






  • 3





    @Voo, if library is not header only (I doubt that codec library is) or closed source, I see no good way to use templates in public API.

    – magras
    Apr 14 at 9:11



















16














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.





share|improve this answer

























  • Decoder and another class, Encoder, inherit from Codec because Codec provides the majority of maintained state and a bunch of shared logic. Unfortunately, I'm not allowed to share the code; just the interface. Your suggestion of using std::visit looks promising. Thanks!

    – splicer
    Apr 13 at 23:06











  • @splicer: In that case, consider either renaming Encoder or perhaps taking some functionality out into a namespace.

    – einpoklum
    Apr 13 at 23:07






  • 2





    +1 for recommending span. I would even suggest implementing your own (simple) span if you're not allowed to use a library or C++20

    – Arvid
    Apr 14 at 15:49






  • 2





    @Arvid: Justin beat me to it, you should +1 his answer. Also - GSL is header-only, it's really not a big deal to use it, and if it's the fact that it's multiple files - there's always GSL-lite.

    – einpoklum
    Apr 14 at 16:15



















5














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.






share|improve this answer


















  • 1





    Rather than writing my own span, do you think basic_string_view<byte> would make a good interim solution?

    – splicer
    Apr 14 at 1:40






  • 1





    @splicer I almost suggested basic_string_view<byte>, but it's very situational. If you can guarantee that the input buffer would be null-terminated, it can work. If you can't, it's dangerous.

    – Justin
    Apr 14 at 2:06






  • 3





    When basic_string_view<byte> is instantiated with both a pointer and a size, it allows for null characters to be stored mid-string. Of course, it's still too error-prone to use in the API. However, it does turn out to be useful on the implementation side if I go with the pointer & length API style.

    – splicer
    Apr 14 at 4:00











Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55670315%2fhow-should-i-replace-vectoruint8-tconst-iterator-in-an-api%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























3 Answers
3






active

oldest

votes








3 Answers
3






active

oldest

votes









active

oldest

votes






active

oldest

votes









16














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.






share|improve this answer




















  • 2





    There's many, many algorithms in the standard library that expect a contiguous sequence of some type (to name just one: std::sort). But as far as I can see, none of them takes a T* and length, they all use iterators. Seems very error prone.

    – Voo
    Apr 14 at 9:00






  • 3





    @Voo: A T* is an iterator... and the second iterator you pass is raw_pointer + length - another pointer.

    – einpoklum
    Apr 14 at 9:01






  • 1





    @einpoklum Every T* is an iterator but not every iterator is a T*, which is pretty much my point.

    – Voo
    Apr 14 at 9:01







  • 6





    @Voo, why do you think, that std::sort expects contiguous memory? It requires just random access iterators.

    – magras
    Apr 14 at 9:03






  • 3





    @Voo, if library is not header only (I doubt that codec library is) or closed source, I see no good way to use templates in public API.

    – magras
    Apr 14 at 9:11
















16














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.






share|improve this answer




















  • 2





    There's many, many algorithms in the standard library that expect a contiguous sequence of some type (to name just one: std::sort). But as far as I can see, none of them takes a T* and length, they all use iterators. Seems very error prone.

    – Voo
    Apr 14 at 9:00






  • 3





    @Voo: A T* is an iterator... and the second iterator you pass is raw_pointer + length - another pointer.

    – einpoklum
    Apr 14 at 9:01






  • 1





    @einpoklum Every T* is an iterator but not every iterator is a T*, which is pretty much my point.

    – Voo
    Apr 14 at 9:01







  • 6





    @Voo, why do you think, that std::sort expects contiguous memory? It requires just random access iterators.

    – magras
    Apr 14 at 9:03






  • 3





    @Voo, if library is not header only (I doubt that codec library is) or closed source, I see no good way to use templates in public API.

    – magras
    Apr 14 at 9:11














16












16








16







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.






share|improve this answer















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.







share|improve this answer














share|improve this answer



share|improve this answer








edited Apr 13 at 22:31

























answered Apr 13 at 22:26









Lightness Races in OrbitLightness Races in Orbit

296k55480819




296k55480819







  • 2





    There's many, many algorithms in the standard library that expect a contiguous sequence of some type (to name just one: std::sort). But as far as I can see, none of them takes a T* and length, they all use iterators. Seems very error prone.

    – Voo
    Apr 14 at 9:00






  • 3





    @Voo: A T* is an iterator... and the second iterator you pass is raw_pointer + length - another pointer.

    – einpoklum
    Apr 14 at 9:01






  • 1





    @einpoklum Every T* is an iterator but not every iterator is a T*, which is pretty much my point.

    – Voo
    Apr 14 at 9:01







  • 6





    @Voo, why do you think, that std::sort expects contiguous memory? It requires just random access iterators.

    – magras
    Apr 14 at 9:03






  • 3





    @Voo, if library is not header only (I doubt that codec library is) or closed source, I see no good way to use templates in public API.

    – magras
    Apr 14 at 9:11













  • 2





    There's many, many algorithms in the standard library that expect a contiguous sequence of some type (to name just one: std::sort). But as far as I can see, none of them takes a T* and length, they all use iterators. Seems very error prone.

    – Voo
    Apr 14 at 9:00






  • 3





    @Voo: A T* is an iterator... and the second iterator you pass is raw_pointer + length - another pointer.

    – einpoklum
    Apr 14 at 9:01






  • 1





    @einpoklum Every T* is an iterator but not every iterator is a T*, which is pretty much my point.

    – Voo
    Apr 14 at 9:01







  • 6





    @Voo, why do you think, that std::sort expects contiguous memory? It requires just random access iterators.

    – magras
    Apr 14 at 9:03






  • 3





    @Voo, if library is not header only (I doubt that codec library is) or closed source, I see no good way to use templates in public API.

    – magras
    Apr 14 at 9:11








2




2





There's many, many algorithms in the standard library that expect a contiguous sequence of some type (to name just one: std::sort). But as far as I can see, none of them takes a T* and length, they all use iterators. Seems very error prone.

– Voo
Apr 14 at 9:00





There's many, many algorithms in the standard library that expect a contiguous sequence of some type (to name just one: std::sort). But as far as I can see, none of them takes a T* and length, they all use iterators. Seems very error prone.

– Voo
Apr 14 at 9:00




3




3





@Voo: A T* is an iterator... and the second iterator you pass is raw_pointer + length - another pointer.

– einpoklum
Apr 14 at 9:01





@Voo: A T* is an iterator... and the second iterator you pass is raw_pointer + length - another pointer.

– einpoklum
Apr 14 at 9:01




1




1





@einpoklum Every T* is an iterator but not every iterator is a T*, which is pretty much my point.

– Voo
Apr 14 at 9:01






@einpoklum Every T* is an iterator but not every iterator is a T*, which is pretty much my point.

– Voo
Apr 14 at 9:01





6




6





@Voo, why do you think, that std::sort expects contiguous memory? It requires just random access iterators.

– magras
Apr 14 at 9:03





@Voo, why do you think, that std::sort expects contiguous memory? It requires just random access iterators.

– magras
Apr 14 at 9:03




3




3





@Voo, if library is not header only (I doubt that codec library is) or closed source, I see no good way to use templates in public API.

– magras
Apr 14 at 9:11






@Voo, if library is not header only (I doubt that codec library is) or closed source, I see no good way to use templates in public API.

– magras
Apr 14 at 9:11














16














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.





share|improve this answer

























  • Decoder and another class, Encoder, inherit from Codec because Codec provides the majority of maintained state and a bunch of shared logic. Unfortunately, I'm not allowed to share the code; just the interface. Your suggestion of using std::visit looks promising. Thanks!

    – splicer
    Apr 13 at 23:06











  • @splicer: In that case, consider either renaming Encoder or perhaps taking some functionality out into a namespace.

    – einpoklum
    Apr 13 at 23:07






  • 2





    +1 for recommending span. I would even suggest implementing your own (simple) span if you're not allowed to use a library or C++20

    – Arvid
    Apr 14 at 15:49






  • 2





    @Arvid: Justin beat me to it, you should +1 his answer. Also - GSL is header-only, it's really not a big deal to use it, and if it's the fact that it's multiple files - there's always GSL-lite.

    – einpoklum
    Apr 14 at 16:15
















16














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.





share|improve this answer

























  • Decoder and another class, Encoder, inherit from Codec because Codec provides the majority of maintained state and a bunch of shared logic. Unfortunately, I'm not allowed to share the code; just the interface. Your suggestion of using std::visit looks promising. Thanks!

    – splicer
    Apr 13 at 23:06











  • @splicer: In that case, consider either renaming Encoder or perhaps taking some functionality out into a namespace.

    – einpoklum
    Apr 13 at 23:07






  • 2





    +1 for recommending span. I would even suggest implementing your own (simple) span if you're not allowed to use a library or C++20

    – Arvid
    Apr 14 at 15:49






  • 2





    @Arvid: Justin beat me to it, you should +1 his answer. Also - GSL is header-only, it's really not a big deal to use it, and if it's the fact that it's multiple files - there's always GSL-lite.

    – einpoklum
    Apr 14 at 16:15














16












16








16







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.





share|improve this answer















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.






share|improve this answer














share|improve this answer



share|improve this answer








edited 17 hours ago

























answered Apr 13 at 22:38









einpoklumeinpoklum

37.6k28135265




37.6k28135265












  • Decoder and another class, Encoder, inherit from Codec because Codec provides the majority of maintained state and a bunch of shared logic. Unfortunately, I'm not allowed to share the code; just the interface. Your suggestion of using std::visit looks promising. Thanks!

    – splicer
    Apr 13 at 23:06











  • @splicer: In that case, consider either renaming Encoder or perhaps taking some functionality out into a namespace.

    – einpoklum
    Apr 13 at 23:07






  • 2





    +1 for recommending span. I would even suggest implementing your own (simple) span if you're not allowed to use a library or C++20

    – Arvid
    Apr 14 at 15:49






  • 2





    @Arvid: Justin beat me to it, you should +1 his answer. Also - GSL is header-only, it's really not a big deal to use it, and if it's the fact that it's multiple files - there's always GSL-lite.

    – einpoklum
    Apr 14 at 16:15


















  • Decoder and another class, Encoder, inherit from Codec because Codec provides the majority of maintained state and a bunch of shared logic. Unfortunately, I'm not allowed to share the code; just the interface. Your suggestion of using std::visit looks promising. Thanks!

    – splicer
    Apr 13 at 23:06











  • @splicer: In that case, consider either renaming Encoder or perhaps taking some functionality out into a namespace.

    – einpoklum
    Apr 13 at 23:07






  • 2





    +1 for recommending span. I would even suggest implementing your own (simple) span if you're not allowed to use a library or C++20

    – Arvid
    Apr 14 at 15:49






  • 2





    @Arvid: Justin beat me to it, you should +1 his answer. Also - GSL is header-only, it's really not a big deal to use it, and if it's the fact that it's multiple files - there's always GSL-lite.

    – einpoklum
    Apr 14 at 16:15

















Decoder and another class, Encoder, inherit from Codec because Codec provides the majority of maintained state and a bunch of shared logic. Unfortunately, I'm not allowed to share the code; just the interface. Your suggestion of using std::visit looks promising. Thanks!

– splicer
Apr 13 at 23:06





Decoder and another class, Encoder, inherit from Codec because Codec provides the majority of maintained state and a bunch of shared logic. Unfortunately, I'm not allowed to share the code; just the interface. Your suggestion of using std::visit looks promising. Thanks!

– splicer
Apr 13 at 23:06













@splicer: In that case, consider either renaming Encoder or perhaps taking some functionality out into a namespace.

– einpoklum
Apr 13 at 23:07





@splicer: In that case, consider either renaming Encoder or perhaps taking some functionality out into a namespace.

– einpoklum
Apr 13 at 23:07




2




2





+1 for recommending span. I would even suggest implementing your own (simple) span if you're not allowed to use a library or C++20

– Arvid
Apr 14 at 15:49





+1 for recommending span. I would even suggest implementing your own (simple) span if you're not allowed to use a library or C++20

– Arvid
Apr 14 at 15:49




2




2





@Arvid: Justin beat me to it, you should +1 his answer. Also - GSL is header-only, it's really not a big deal to use it, and if it's the fact that it's multiple files - there's always GSL-lite.

– einpoklum
Apr 14 at 16:15






@Arvid: Justin beat me to it, you should +1 his answer. Also - GSL is header-only, it's really not a big deal to use it, and if it's the fact that it's multiple files - there's always GSL-lite.

– einpoklum
Apr 14 at 16:15












5














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.






share|improve this answer


















  • 1





    Rather than writing my own span, do you think basic_string_view<byte> would make a good interim solution?

    – splicer
    Apr 14 at 1:40






  • 1





    @splicer I almost suggested basic_string_view<byte>, but it's very situational. If you can guarantee that the input buffer would be null-terminated, it can work. If you can't, it's dangerous.

    – Justin
    Apr 14 at 2:06






  • 3





    When basic_string_view<byte> is instantiated with both a pointer and a size, it allows for null characters to be stored mid-string. Of course, it's still too error-prone to use in the API. However, it does turn out to be useful on the implementation side if I go with the pointer & length API style.

    – splicer
    Apr 14 at 4:00















5














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.






share|improve this answer


















  • 1





    Rather than writing my own span, do you think basic_string_view<byte> would make a good interim solution?

    – splicer
    Apr 14 at 1:40






  • 1





    @splicer I almost suggested basic_string_view<byte>, but it's very situational. If you can guarantee that the input buffer would be null-terminated, it can work. If you can't, it's dangerous.

    – Justin
    Apr 14 at 2:06






  • 3





    When basic_string_view<byte> is instantiated with both a pointer and a size, it allows for null characters to be stored mid-string. Of course, it's still too error-prone to use in the API. However, it does turn out to be useful on the implementation side if I go with the pointer & length API style.

    – splicer
    Apr 14 at 4:00













5












5








5







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.






share|improve this answer













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.







share|improve this answer












share|improve this answer



share|improve this answer










answered Apr 13 at 22:23









JustinJustin

13.8k95899




13.8k95899







  • 1





    Rather than writing my own span, do you think basic_string_view<byte> would make a good interim solution?

    – splicer
    Apr 14 at 1:40






  • 1





    @splicer I almost suggested basic_string_view<byte>, but it's very situational. If you can guarantee that the input buffer would be null-terminated, it can work. If you can't, it's dangerous.

    – Justin
    Apr 14 at 2:06






  • 3





    When basic_string_view<byte> is instantiated with both a pointer and a size, it allows for null characters to be stored mid-string. Of course, it's still too error-prone to use in the API. However, it does turn out to be useful on the implementation side if I go with the pointer & length API style.

    – splicer
    Apr 14 at 4:00












  • 1





    Rather than writing my own span, do you think basic_string_view<byte> would make a good interim solution?

    – splicer
    Apr 14 at 1:40






  • 1





    @splicer I almost suggested basic_string_view<byte>, but it's very situational. If you can guarantee that the input buffer would be null-terminated, it can work. If you can't, it's dangerous.

    – Justin
    Apr 14 at 2:06






  • 3





    When basic_string_view<byte> is instantiated with both a pointer and a size, it allows for null characters to be stored mid-string. Of course, it's still too error-prone to use in the API. However, it does turn out to be useful on the implementation side if I go with the pointer & length API style.

    – splicer
    Apr 14 at 4:00







1




1





Rather than writing my own span, do you think basic_string_view<byte> would make a good interim solution?

– splicer
Apr 14 at 1:40





Rather than writing my own span, do you think basic_string_view<byte> would make a good interim solution?

– splicer
Apr 14 at 1:40




1




1





@splicer I almost suggested basic_string_view<byte>, but it's very situational. If you can guarantee that the input buffer would be null-terminated, it can work. If you can't, it's dangerous.

– Justin
Apr 14 at 2:06





@splicer I almost suggested basic_string_view<byte>, but it's very situational. If you can guarantee that the input buffer would be null-terminated, it can work. If you can't, it's dangerous.

– Justin
Apr 14 at 2:06




3




3





When basic_string_view<byte> is instantiated with both a pointer and a size, it allows for null characters to be stored mid-string. Of course, it's still too error-prone to use in the API. However, it does turn out to be useful on the implementation side if I go with the pointer & length API style.

– splicer
Apr 14 at 4:00





When basic_string_view<byte> is instantiated with both a pointer and a size, it allows for null characters to be stored mid-string. Of course, it's still too error-prone to use in the API. However, it does turn out to be useful on the implementation side if I go with the pointer & length API style.

– splicer
Apr 14 at 4:00

















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55670315%2fhow-should-i-replace-vectoruint8-tconst-iterator-in-an-api%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Sum ergo cogito? 1 nng

419 nièngy_Soadمي 19bal1.5o_g

Queiggey Chernihivv 9NnOo i Zw X QqKk LpB