c++ - How to return a variant from an input iterator with high performance? -




i have file format decoder returns custom input iterator. value type of iterator (when dereferencing *iter) can 1 of many token types.

here's simplified usage example:

file file {"/path/to/file"};  (const auto& token : file) {     // token } 

how can token have multiple possible types? depending on type of token, type of payload changes too.

performance important here during traversal. don't want unnecessary allocation, example. why iterator's type input iterator: advance iterator, previous token invalidated per requirements of inputiterator tag.

i have 2 ideas in mind far:

  1. use single token class private union of possible payloads (with public getters) , public type id (enum) getter. user needs switch on type id know payload getter call:

    for (const auto& token : file) {     switch (token.type()) {     case token::type::apple:         const auto& apple = token.apple();         // ...         break;      case token::type::banana:         const auto& banana = token.banana();         // ...         break;      // ...     } } 

    although choose in c, i'm not fan of solution in c++ because user can still call wrong getter , nothing can enforce (except run-time checks want avoid performance concerns).

  2. create abstract token base class has accept() method accept visitor, , multiple concrete classes (one each payload type) inheriting base class. in iterator object, instantiate 1 of each concrete class @ creation time. have token *token member. when iterating, fill appropriate pre-allocated payload object, , set this->token = this->specifictoken. make operator*() return this->token (reference to). ask user use visitor during iteration (or worse, use dynamic_cast):

    class myvisitor : public tokenvisitor { public:     void visit(const appletoken& token) override {         // ...     }      void visit(const bananatoken& token) override {         // ...     } };  tokenvisitor visitor;  (const auto& token : file) {     token.accept(visitor); } 

    this introduces additional function calls each token, @ least 1 of them virtual, might not end of world; remain open solution.

is there other interesting solution? consider returning boost::variant or std::variant same idea #2.

although choose in c, i'm not fan of solution in c++ because user can still call wrong getter , nothing can enforce (except run-time checks want avoid performance concerns).

you can reverse approach , accept callable object instead of returning iterator user. can iterate container internally , dispatch right type. way users cannot mistakes anymore ignoring information carried tagged union, in charge of taking in consideration.

here minimal, working example show mean:

#include <vector> #include <utility> #include <iostream>  struct {}; struct b {};  class c {     struct s {         enum { a_tag, b_tag } tag;         union { a; b b; };     };  public:     void add(a a) {         s s;         s.a = a;         s.tag = s::a_tag;         vec.push_back(s);     }      void add(b b) {         s s;         s.b = b;         s.tag = s::b_tag;         vec.push_back(s);     }      template<typename f>     void iterate(f &&f) {         for(auto &&s: vec) {             if(s.tag == s::a_tag) {                 std::forward<f>(f)(s.a);             } else {                 std::forward<f>(f)(s.b);             }         }     }  private:     std::vector<s> vec; };  void f(const &) {     std::cout << "a" << std::endl; }  void f(const b &) {     std::cout << "b" << std::endl; }  int main() {     c c;     c.add(a{});     c.add(b{});     c.add(a{});     c.iterate([](auto item) { f(item); });  } 

see , running on coliru.





wiki

Comments

Popular posts from this blog

Asterisk AGI Python Script to Dialplan does not work -

python - Read npy file directly from S3 StreamingBody -

kotlin - Out-projected type in generic interface prohibits the use of metod with generic parameter -