1

I have a class that uses custom logic to generate some sequence:

class Example{
   size_t FirstElement();
   size_t NextElement(size_t currentelement);
   //When element equals magic number this is 
   //signalling that the sequence is over.  
   static size_t MagicNumber =-1;     
}

I could consume the sequence as follows:

Example example{};
for(size_t curelement = example.FirstElement; 
             curelement != Example::MagicNumber;
               curelement = example.NextElement(currentelement))
{
      //do something with the curelement
}

I would like a solution which makes it similarly easy to consume the sequence, but:

  • Avoiding the use of the magic number external to Example (i.e. while consuming).
  • That does not store the currentelement inside the `example' object.
  • That has perhaps a bit cleaner consumtion code in general?
  • That does not give substantial performance penalties compared to this code.
  • EDIT: Example should not return the whole sequence in one go, i.e. as std::vector.

Is there a good alternative. (Based on my (very limited) understanding of iterators, they should be the goto solution here? If so, how to implement such a solution?)

willem
  • 2,617
  • 5
  • 26
  • 38
  • This looks like a YZ problem. Any standard container + a ranged for loop seems to solve what you are asking. – Vorac Jul 12 '19 at 08:10
  • https://stackoverflow.com/a/16504109/2378102 –  Jul 12 '19 at 08:14
  • @Vorac Ok so I don't want Example to output the whole sequence! If that is your suggestion? – willem Jul 12 '19 at 08:14
  • Are you saying you want to iterate the sequence but with the ability of applying a custom for-loop terminating clause? – acraig5075 Jul 12 '19 at 08:15
  • @acraig5075: Yes terminating prematurely is one reason for not wanting to return a container that contains the whole sequence. Other reason is performance; sequence may be very long and consumer only needs elements one at a a time. – willem Jul 12 '19 at 08:17

2 Answers2

1

Yes, I would advise you to use iterators. They usually work the same way, like your code:

Example e;
for(auto it = e.begin(); it != e.end(); ++it)
{...}

//Can be simplified to 
for(const auto &x : e)
{...}

where begin() will return the iterator pointing to the first element (like Example::FirstElement(). and end() the iterator to the element after the last (This could return your magic number). However...both values should be wrapped in a class and you should not just return integers as iterators.

For further reading I would suggest this and this post.

Lukas-T
  • 11,133
  • 3
  • 20
  • 30
0

A for-loop is fine for iterating your class. You just seem to want it to be generic in how it terminates and presumably what the body does. Seems like overkill but this is a solution.

void Consume(Example &example, std::function<bool(Example)> terminateFunc, std::function<void(Example)> bodyFunc)
{
    for(size_t curelement = example.FirstElement; !terminateFunc(curelement); curelement = example.NextElement(curelement))
    {
        bodyFunc(curelement);
    }
}

i.e. terminateFunc and bodyFunc are passed in, and can be custom for whatever conditions you require.

auto condition = [](Example e) { return true; /* check if e satisfies some condition; */ };
auto body = [](Example e) { /* do something with e */ };

Consume(example, condition, body);
acraig5075
  • 10,588
  • 3
  • 31
  • 50