How to lazily generate a finished sequence of items and iterate over it

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
10
down vote

favorite
4












I feel like this question must have been asked and solved many times, because this seems to me a quite generic scenario, but I could not find anything that pointed me in the direction of a solution.



I'm trying to implement a generic iterable Generator object that produces a sequence of numbers up until a certain termination condition is met, signaling that such condition has been reached in order to stop the iteration.



The basic idea is, essentially, to have something similar to Python's generators where an object yields values until it has no more to yield and then a StopIteration exception is raised to inform the outside loop that the sequence is finished.



From what I understand, the problem splits into creating the sequence-generating object an then obtaining an iterator over it.



For the sequence-generating object, I thought I'd define a base Generator class which is then extended to provide specific behaviours (e.g., get values from a set of ranges, or from a list of fixed values, etc). All Generaors produce a new value at each call of operator() or throw a ValuesFinishedException if the generator ran to the end of the sequence.
I implemented this as such (I show the single-range subclass as example, but I need to be able to model more types of sequences):



struct ValuesFinishedException : public std::exception ;

template <typename T>
class Generator

public:
Generator() ;
~Generator() ;
virtual T operator()() = 0; // return the new number or raise a ValuesFinishedException
;

template <typename T>
class RangeGenerator : public Generator<T>

private:
T m_start;
T m_stop;
T m_step;

T m_next_val;

public:
RangeGenerator(T start, T stop, T step) :
m_start(start),
m_stop(stop),
m_step(step),
m_next_val(start)


T operator()() override

if (m_next_val >= m_stop)
throw ValuesFinishedException();

T retval = m_next_val;
m_next_val += m_step;
return retval;


void setStep(T step) m_step = step;
T step() return m_step;
;


For the iterator part, though, I'm stuck.
I have researched any combination I could think of of "Iterator", "Generator" and synonyms, but all I find only considers the case where a generator function has an unlimited number of values (see for example boost's generator_iterator). I thought about writing a Generator::iterator class myself, but I only found examples of trivial iterators (linked lists, array reimplementations) where end is well-defined. I don't know in advance when the end will be reached, I only know that if the generator I'm iterating over raises the exception, I need to set the iterator's current value to "end()", but I don't know how to represent it.



Edit: Adding the intended use-case



The reason for this class is to have a flexible sequence object I can loop over:



RangeGenerator gen(0.25f, 95.3f, 1.2f);
for(auto v : gen)

// do something with v



The example of the range is just the simplest one. I will have at least three actual use-cases:



  • simple range (with variable step)

  • concatenation of multiple ranges

  • sequence of constant values stored in a vector

For each of these I'm planning on having a Generator subclass, with the iterator defined for the abstract Generator.










share|improve this question



















  • 2




    Just have a sentinel state for your iterator representing "end", and write your comparison operator so i == end is only true if i has reached it's terminal condition. This can be as simple as adding a bool isAtEnd member. Don't use an exception, that's importing a Python convention to C++
    – Useless
    Sep 4 at 9:00










  • Yeah, I felt that using the exception was bad practice, but coming from Python I went for the solution I knew. The thing is, IMO the exception cleanly separates the "there is another value in the sequence" case from "the sequence is over". How would I notify someone calling generator() that that instance actually ran through the whole sequence without the exception? I can't assume I always have a value to return... (writing this I'm thinking of std::optional<T> as return value for operator().. would that be any better?)
    – GPhilo
    Sep 4 at 9:06






  • 1




    I think you're trying to model Python too closely, which is why you're now having trouble matching up a Python generator to a C++ iterator. Iterators in C++ don't call until exception, you compare them. Your "end" iterator is one where m_next_val == m_stop. You will also need to write an equality method to check for this.
    – Hitobat
    Sep 4 at 9:07










  • If you want to generate infinite sequences, do you need the iterator interface at all? When you get an iterator, what will you do next with it? (i.e. the use-case)
    – Hitobat
    Sep 4 at 9:08










  • @Hitobat Same thing you do in other languages, for example: en.cppreference.com/w/cpp/algorithm/copy_n
    – BartoszKP
    Sep 4 at 9:10















up vote
10
down vote

favorite
4












I feel like this question must have been asked and solved many times, because this seems to me a quite generic scenario, but I could not find anything that pointed me in the direction of a solution.



I'm trying to implement a generic iterable Generator object that produces a sequence of numbers up until a certain termination condition is met, signaling that such condition has been reached in order to stop the iteration.



The basic idea is, essentially, to have something similar to Python's generators where an object yields values until it has no more to yield and then a StopIteration exception is raised to inform the outside loop that the sequence is finished.



From what I understand, the problem splits into creating the sequence-generating object an then obtaining an iterator over it.



For the sequence-generating object, I thought I'd define a base Generator class which is then extended to provide specific behaviours (e.g., get values from a set of ranges, or from a list of fixed values, etc). All Generaors produce a new value at each call of operator() or throw a ValuesFinishedException if the generator ran to the end of the sequence.
I implemented this as such (I show the single-range subclass as example, but I need to be able to model more types of sequences):



struct ValuesFinishedException : public std::exception ;

template <typename T>
class Generator

public:
Generator() ;
~Generator() ;
virtual T operator()() = 0; // return the new number or raise a ValuesFinishedException
;

template <typename T>
class RangeGenerator : public Generator<T>

private:
T m_start;
T m_stop;
T m_step;

T m_next_val;

public:
RangeGenerator(T start, T stop, T step) :
m_start(start),
m_stop(stop),
m_step(step),
m_next_val(start)


T operator()() override

if (m_next_val >= m_stop)
throw ValuesFinishedException();

T retval = m_next_val;
m_next_val += m_step;
return retval;


void setStep(T step) m_step = step;
T step() return m_step;
;


For the iterator part, though, I'm stuck.
I have researched any combination I could think of of "Iterator", "Generator" and synonyms, but all I find only considers the case where a generator function has an unlimited number of values (see for example boost's generator_iterator). I thought about writing a Generator::iterator class myself, but I only found examples of trivial iterators (linked lists, array reimplementations) where end is well-defined. I don't know in advance when the end will be reached, I only know that if the generator I'm iterating over raises the exception, I need to set the iterator's current value to "end()", but I don't know how to represent it.



Edit: Adding the intended use-case



The reason for this class is to have a flexible sequence object I can loop over:



RangeGenerator gen(0.25f, 95.3f, 1.2f);
for(auto v : gen)

// do something with v



The example of the range is just the simplest one. I will have at least three actual use-cases:



  • simple range (with variable step)

  • concatenation of multiple ranges

  • sequence of constant values stored in a vector

For each of these I'm planning on having a Generator subclass, with the iterator defined for the abstract Generator.










share|improve this question



















  • 2




    Just have a sentinel state for your iterator representing "end", and write your comparison operator so i == end is only true if i has reached it's terminal condition. This can be as simple as adding a bool isAtEnd member. Don't use an exception, that's importing a Python convention to C++
    – Useless
    Sep 4 at 9:00










  • Yeah, I felt that using the exception was bad practice, but coming from Python I went for the solution I knew. The thing is, IMO the exception cleanly separates the "there is another value in the sequence" case from "the sequence is over". How would I notify someone calling generator() that that instance actually ran through the whole sequence without the exception? I can't assume I always have a value to return... (writing this I'm thinking of std::optional<T> as return value for operator().. would that be any better?)
    – GPhilo
    Sep 4 at 9:06






  • 1




    I think you're trying to model Python too closely, which is why you're now having trouble matching up a Python generator to a C++ iterator. Iterators in C++ don't call until exception, you compare them. Your "end" iterator is one where m_next_val == m_stop. You will also need to write an equality method to check for this.
    – Hitobat
    Sep 4 at 9:07










  • If you want to generate infinite sequences, do you need the iterator interface at all? When you get an iterator, what will you do next with it? (i.e. the use-case)
    – Hitobat
    Sep 4 at 9:08










  • @Hitobat Same thing you do in other languages, for example: en.cppreference.com/w/cpp/algorithm/copy_n
    – BartoszKP
    Sep 4 at 9:10













up vote
10
down vote

favorite
4









up vote
10
down vote

favorite
4






4





I feel like this question must have been asked and solved many times, because this seems to me a quite generic scenario, but I could not find anything that pointed me in the direction of a solution.



I'm trying to implement a generic iterable Generator object that produces a sequence of numbers up until a certain termination condition is met, signaling that such condition has been reached in order to stop the iteration.



The basic idea is, essentially, to have something similar to Python's generators where an object yields values until it has no more to yield and then a StopIteration exception is raised to inform the outside loop that the sequence is finished.



From what I understand, the problem splits into creating the sequence-generating object an then obtaining an iterator over it.



For the sequence-generating object, I thought I'd define a base Generator class which is then extended to provide specific behaviours (e.g., get values from a set of ranges, or from a list of fixed values, etc). All Generaors produce a new value at each call of operator() or throw a ValuesFinishedException if the generator ran to the end of the sequence.
I implemented this as such (I show the single-range subclass as example, but I need to be able to model more types of sequences):



struct ValuesFinishedException : public std::exception ;

template <typename T>
class Generator

public:
Generator() ;
~Generator() ;
virtual T operator()() = 0; // return the new number or raise a ValuesFinishedException
;

template <typename T>
class RangeGenerator : public Generator<T>

private:
T m_start;
T m_stop;
T m_step;

T m_next_val;

public:
RangeGenerator(T start, T stop, T step) :
m_start(start),
m_stop(stop),
m_step(step),
m_next_val(start)


T operator()() override

if (m_next_val >= m_stop)
throw ValuesFinishedException();

T retval = m_next_val;
m_next_val += m_step;
return retval;


void setStep(T step) m_step = step;
T step() return m_step;
;


For the iterator part, though, I'm stuck.
I have researched any combination I could think of of "Iterator", "Generator" and synonyms, but all I find only considers the case where a generator function has an unlimited number of values (see for example boost's generator_iterator). I thought about writing a Generator::iterator class myself, but I only found examples of trivial iterators (linked lists, array reimplementations) where end is well-defined. I don't know in advance when the end will be reached, I only know that if the generator I'm iterating over raises the exception, I need to set the iterator's current value to "end()", but I don't know how to represent it.



Edit: Adding the intended use-case



The reason for this class is to have a flexible sequence object I can loop over:



RangeGenerator gen(0.25f, 95.3f, 1.2f);
for(auto v : gen)

// do something with v



The example of the range is just the simplest one. I will have at least three actual use-cases:



  • simple range (with variable step)

  • concatenation of multiple ranges

  • sequence of constant values stored in a vector

For each of these I'm planning on having a Generator subclass, with the iterator defined for the abstract Generator.










share|improve this question















I feel like this question must have been asked and solved many times, because this seems to me a quite generic scenario, but I could not find anything that pointed me in the direction of a solution.



I'm trying to implement a generic iterable Generator object that produces a sequence of numbers up until a certain termination condition is met, signaling that such condition has been reached in order to stop the iteration.



The basic idea is, essentially, to have something similar to Python's generators where an object yields values until it has no more to yield and then a StopIteration exception is raised to inform the outside loop that the sequence is finished.



From what I understand, the problem splits into creating the sequence-generating object an then obtaining an iterator over it.



For the sequence-generating object, I thought I'd define a base Generator class which is then extended to provide specific behaviours (e.g., get values from a set of ranges, or from a list of fixed values, etc). All Generaors produce a new value at each call of operator() or throw a ValuesFinishedException if the generator ran to the end of the sequence.
I implemented this as such (I show the single-range subclass as example, but I need to be able to model more types of sequences):



struct ValuesFinishedException : public std::exception ;

template <typename T>
class Generator

public:
Generator() ;
~Generator() ;
virtual T operator()() = 0; // return the new number or raise a ValuesFinishedException
;

template <typename T>
class RangeGenerator : public Generator<T>

private:
T m_start;
T m_stop;
T m_step;

T m_next_val;

public:
RangeGenerator(T start, T stop, T step) :
m_start(start),
m_stop(stop),
m_step(step),
m_next_val(start)


T operator()() override

if (m_next_val >= m_stop)
throw ValuesFinishedException();

T retval = m_next_val;
m_next_val += m_step;
return retval;


void setStep(T step) m_step = step;
T step() return m_step;
;


For the iterator part, though, I'm stuck.
I have researched any combination I could think of of "Iterator", "Generator" and synonyms, but all I find only considers the case where a generator function has an unlimited number of values (see for example boost's generator_iterator). I thought about writing a Generator::iterator class myself, but I only found examples of trivial iterators (linked lists, array reimplementations) where end is well-defined. I don't know in advance when the end will be reached, I only know that if the generator I'm iterating over raises the exception, I need to set the iterator's current value to "end()", but I don't know how to represent it.



Edit: Adding the intended use-case



The reason for this class is to have a flexible sequence object I can loop over:



RangeGenerator gen(0.25f, 95.3f, 1.2f);
for(auto v : gen)

// do something with v



The example of the range is just the simplest one. I will have at least three actual use-cases:



  • simple range (with variable step)

  • concatenation of multiple ranges

  • sequence of constant values stored in a vector

For each of these I'm planning on having a Generator subclass, with the iterator defined for the abstract Generator.







c++ iterator






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Sep 4 at 9:22









YSC

16.9k33986




16.9k33986










asked Sep 4 at 8:56









GPhilo

5,00011841




5,00011841







  • 2




    Just have a sentinel state for your iterator representing "end", and write your comparison operator so i == end is only true if i has reached it's terminal condition. This can be as simple as adding a bool isAtEnd member. Don't use an exception, that's importing a Python convention to C++
    – Useless
    Sep 4 at 9:00










  • Yeah, I felt that using the exception was bad practice, but coming from Python I went for the solution I knew. The thing is, IMO the exception cleanly separates the "there is another value in the sequence" case from "the sequence is over". How would I notify someone calling generator() that that instance actually ran through the whole sequence without the exception? I can't assume I always have a value to return... (writing this I'm thinking of std::optional<T> as return value for operator().. would that be any better?)
    – GPhilo
    Sep 4 at 9:06






  • 1




    I think you're trying to model Python too closely, which is why you're now having trouble matching up a Python generator to a C++ iterator. Iterators in C++ don't call until exception, you compare them. Your "end" iterator is one where m_next_val == m_stop. You will also need to write an equality method to check for this.
    – Hitobat
    Sep 4 at 9:07










  • If you want to generate infinite sequences, do you need the iterator interface at all? When you get an iterator, what will you do next with it? (i.e. the use-case)
    – Hitobat
    Sep 4 at 9:08










  • @Hitobat Same thing you do in other languages, for example: en.cppreference.com/w/cpp/algorithm/copy_n
    – BartoszKP
    Sep 4 at 9:10













  • 2




    Just have a sentinel state for your iterator representing "end", and write your comparison operator so i == end is only true if i has reached it's terminal condition. This can be as simple as adding a bool isAtEnd member. Don't use an exception, that's importing a Python convention to C++
    – Useless
    Sep 4 at 9:00










  • Yeah, I felt that using the exception was bad practice, but coming from Python I went for the solution I knew. The thing is, IMO the exception cleanly separates the "there is another value in the sequence" case from "the sequence is over". How would I notify someone calling generator() that that instance actually ran through the whole sequence without the exception? I can't assume I always have a value to return... (writing this I'm thinking of std::optional<T> as return value for operator().. would that be any better?)
    – GPhilo
    Sep 4 at 9:06






  • 1




    I think you're trying to model Python too closely, which is why you're now having trouble matching up a Python generator to a C++ iterator. Iterators in C++ don't call until exception, you compare them. Your "end" iterator is one where m_next_val == m_stop. You will also need to write an equality method to check for this.
    – Hitobat
    Sep 4 at 9:07










  • If you want to generate infinite sequences, do you need the iterator interface at all? When you get an iterator, what will you do next with it? (i.e. the use-case)
    – Hitobat
    Sep 4 at 9:08










  • @Hitobat Same thing you do in other languages, for example: en.cppreference.com/w/cpp/algorithm/copy_n
    – BartoszKP
    Sep 4 at 9:10








2




2




Just have a sentinel state for your iterator representing "end", and write your comparison operator so i == end is only true if i has reached it's terminal condition. This can be as simple as adding a bool isAtEnd member. Don't use an exception, that's importing a Python convention to C++
– Useless
Sep 4 at 9:00




Just have a sentinel state for your iterator representing "end", and write your comparison operator so i == end is only true if i has reached it's terminal condition. This can be as simple as adding a bool isAtEnd member. Don't use an exception, that's importing a Python convention to C++
– Useless
Sep 4 at 9:00












Yeah, I felt that using the exception was bad practice, but coming from Python I went for the solution I knew. The thing is, IMO the exception cleanly separates the "there is another value in the sequence" case from "the sequence is over". How would I notify someone calling generator() that that instance actually ran through the whole sequence without the exception? I can't assume I always have a value to return... (writing this I'm thinking of std::optional<T> as return value for operator().. would that be any better?)
– GPhilo
Sep 4 at 9:06




Yeah, I felt that using the exception was bad practice, but coming from Python I went for the solution I knew. The thing is, IMO the exception cleanly separates the "there is another value in the sequence" case from "the sequence is over". How would I notify someone calling generator() that that instance actually ran through the whole sequence without the exception? I can't assume I always have a value to return... (writing this I'm thinking of std::optional<T> as return value for operator().. would that be any better?)
– GPhilo
Sep 4 at 9:06




1




1




I think you're trying to model Python too closely, which is why you're now having trouble matching up a Python generator to a C++ iterator. Iterators in C++ don't call until exception, you compare them. Your "end" iterator is one where m_next_val == m_stop. You will also need to write an equality method to check for this.
– Hitobat
Sep 4 at 9:07




I think you're trying to model Python too closely, which is why you're now having trouble matching up a Python generator to a C++ iterator. Iterators in C++ don't call until exception, you compare them. Your "end" iterator is one where m_next_val == m_stop. You will also need to write an equality method to check for this.
– Hitobat
Sep 4 at 9:07












If you want to generate infinite sequences, do you need the iterator interface at all? When you get an iterator, what will you do next with it? (i.e. the use-case)
– Hitobat
Sep 4 at 9:08




If you want to generate infinite sequences, do you need the iterator interface at all? When you get an iterator, what will you do next with it? (i.e. the use-case)
– Hitobat
Sep 4 at 9:08












@Hitobat Same thing you do in other languages, for example: en.cppreference.com/w/cpp/algorithm/copy_n
– BartoszKP
Sep 4 at 9:10





@Hitobat Same thing you do in other languages, for example: en.cppreference.com/w/cpp/algorithm/copy_n
– BartoszKP
Sep 4 at 9:10













4 Answers
4






active

oldest

votes

















up vote
2
down vote



accepted










If you want the generic generator you originally asked for (rather than the simpler use-cases added later), it is possible to set up something like this:



template <typename T>
struct Generator
Generator()
explicit Generator(std::function<std::optional<T>()> f_) : f(f_), v(f())

Generator(Generator<T> const &) = default;
Generator(Generator<T> &&) = default;
Generator<T>& operator=(Generator<T> const &) = default;
Generator<T>& operator=(Generator<T> &&) = default;

bool operator==(Generator<T> const &rhs)
return (!v) && (!rhs.v); // only compare equal if both at end

bool operator!=(Generator<T> const &rhs) return !(*this == rhs);

Generator<T>& operator++()
v = f();
return *this;

Generator<T> operator++(int)
auto tmp = *this;
++*this;
return tmp;


// throw `std::bad_optional_access` if you try to dereference an end iterator
T const& operator*() const
return v.value();


private:
std::function<std::optional<T>()> f;
std::optional<T> v;
;


if you have C++17 (if you don't, use Boost or just track validity manually). The begin/end functions needed to use this nicely look something like



template <typename T>
Generator<T> generate_begin(std::function<std::optional<T>()> f) return Generator<T>(f);
template <typename T>
Generator<T> generate_end(std::function<std::optional<T>()>) return Generator<T>();


Now for a suitable function foo you can use this like a normal input operator:



auto sum = std::accumulate(generate_begin(foo), generate_end(foo), 0);


I omitted the iterator traits which should be defined in Generator as they are in YSC's answer- they should be something like the below (and operator* should return reference, and you should add operator->, etc. etc.)



 // iterator traits
using difference_type = int;
using value_type = T;
using pointer = const T*;
using reference = const T&;
using iterator_category = std::input_iterator_tag;





share|improve this answer






















  • Thanks, this is the closest I've seen so far to what I originally needed. While the other answers do provide a solution to the simpler sub-problems, I'd rather have a a generic solution that works with any functor. I'll need some time to understand your code, but I believe this is pretty much what I need
    – GPhilo
    Sep 4 at 10:17










  • Note that if your generator function is more naturally written as throwing an exception or returning some sentinel value, you can easily write an adapter function to turn it into the required std::optional form.
    – Useless
    Sep 4 at 10:19










  • About the iterator traits, YSC shows them for type int. Using the generic type T what would difference_type be? Should I still use int? Or std::ptrdiff_t as I've seen in some examples? Also, do I uderstand it correctly that in my case an input_iterator_tag would be the right choice? (forward iterator requires the values to be modifiable, which is not my case)
    – GPhilo
    Sep 4 at 10:34











  • Correct, it's an input iterator (the function could be stateful, so we can only assume single-pass). For the difference type, int or size_t are fine. Mostly I didn't write them because value_type, reference_type and pointer_type need reference removal to be completely safe (a function could return optional<reference_wrapper<T>> to externally-stored persistent objects).
    – Useless
    Sep 4 at 10:44










  • I'm not sure I get the part about reference removal (template programming is almost entirely new to me), but I plan on using the classes only with simple types (most likely just floats) at least for now, so I believe I can ignore that part. As per the difference type, does it need to be signed? (if so, size_t won't work)
    – GPhilo
    Sep 4 at 10:51

















up vote
4
down vote













You should use a C++ idiom: the forward iterator. This let you use C++ syntactic sugar and support the standard library. Here is a minimal example:



template<int tstart, int tstop, int tstep = 1>
class Range
public:
class iterator
int start;
int stop;
int step;
int current;
public:
iterator(int start, int stop, int step = 0, int current = tstart) : start(start), stop(stop), step(step == 0 ? (start < stop ? 1 : -1) : step), current(current)
iterator& operator++() current += step; return *this;
iterator operator++(int) iterator retval = *this; ++(*this); return retval;
bool operator==(iterator other) const return std::tie(current, step, stop) == std::tie(other.current, other.step, other.stop);
bool operator!=(iterator other) const return !(*this == other);
long operator*() return current;
// iterator traits
using difference_type = int;
using value_type = int;
using pointer = const int*;
using reference = const int&;
using iterator_category = std::forward_iterator_tag;
;
iterator begin() return iteratortstart, tstop, tstep;
iterator end() return iteratortstart, tstop, tstep, tstop;
;


It can be used with the C++98 way:



using range = Range<0, 10, 2>;
auto r = range;
for (range::iterator it = r.begin() ; it != r.end() ; ++it)
std::cout << *it << 'n';



Or with the new range loop:



for (auto n : Range<0, 10, 2>) 
std::cout << n << 'n';



In cunjunction with the stl:



std::copy(std::begin(r), std::end(r), std::back_inserter(v));


Demo: http://coliru.stacked-crooked.com/a/35ad4ce16428e65d






share|improve this answer






















  • You don't need the start, stop, step members, since those values are template parameters. You also don't need to compare step and stop, since those can only be different for a different instantiation.
    – molbdnilo
    Sep 4 at 9:35











  • @molbdnilo I agree. In fact, that iterator would need additional enhancements before it starts being useful. This is just a example to avoid complex and long explanations.
    – YSC
    Sep 4 at 10:49

















up vote
2
down vote













The range based for loop is all about the iterator implementing begin(), end() and operator++.



So the generator must implement them.



template<typename T>
struct generator
T first;
T last;

struct iterator
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T *;
using reference = T &;
T value;

iterator(T &value) : value(value)

iterator &operator++()
++value;
return *this;

iterator operator++(int) = delete;

bool operator==(const iterator &rhs) return value == rhs.value;
bool operator!=(const iterator &rhs) return !(*this == rhs);
const reference operator *() return value;
const pointer operator->() const return std::addressof(value);
;

iterator begin() return iterator(first);
iterator end() return iterator(last);
;


Then add a function which instantiates the generator and you are done



template<typename T>
generator<T> range(T start, T end)
return generator<T> start, end ;


for (auto i : range(0, 10))







share|improve this answer






















  • I read it was deprecated in C++17 already (although tbh I can live with that for now)
    – GPhilo
    Sep 4 at 9:25

















up vote
1
down vote













The use case you describe (concatenation of ranges etc.) might justify a dependency on a library, so here is a solution based on range-v3, on its way into C++20. You can easily iterate through integral values, here from 0 to 10 with a step size of 2,



#include <range/v3/all.hpp>

using namespace ranges;

for (auto i : view::ints(0, 11) | view::stride(2))
std::cout << i << "n";


or implement a similar loop with floating point values (note that [from, to] is a closed range here, and the third argument denotes the number of steps)



for (auto f : view::linear_distribute(1.25f, 2.5f, 10))
std::cout << f << "n";


and when it comes to concatenation, the library begins to shine:



const std::vector world32, 119, 111, 114, 108, 100;

for (auto i : view::concat("hello", world))
std::cout << char(i);

std::cout << "n";


Note that the above snippets compile with -std=c++17. The library is header only.






share|improve this answer






















  • This looks great and I wish that was included in the standard already, but currently this is out of reach for me sadly. Also, I need to be able to alter the step while iterating over floats, which I think this doesn't support (although I haven't looked in depth into the library)
    – GPhilo
    Sep 4 at 10:19











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',
convertImagesToLinks: true,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
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%2f52162505%2fhow-to-lazily-generate-a-finished-sequence-of-items-and-iterate-over-it%23new-answer', 'question_page');

);

Post as a guest






























4 Answers
4






active

oldest

votes








4 Answers
4






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
2
down vote



accepted










If you want the generic generator you originally asked for (rather than the simpler use-cases added later), it is possible to set up something like this:



template <typename T>
struct Generator
Generator()
explicit Generator(std::function<std::optional<T>()> f_) : f(f_), v(f())

Generator(Generator<T> const &) = default;
Generator(Generator<T> &&) = default;
Generator<T>& operator=(Generator<T> const &) = default;
Generator<T>& operator=(Generator<T> &&) = default;

bool operator==(Generator<T> const &rhs)
return (!v) && (!rhs.v); // only compare equal if both at end

bool operator!=(Generator<T> const &rhs) return !(*this == rhs);

Generator<T>& operator++()
v = f();
return *this;

Generator<T> operator++(int)
auto tmp = *this;
++*this;
return tmp;


// throw `std::bad_optional_access` if you try to dereference an end iterator
T const& operator*() const
return v.value();


private:
std::function<std::optional<T>()> f;
std::optional<T> v;
;


if you have C++17 (if you don't, use Boost or just track validity manually). The begin/end functions needed to use this nicely look something like



template <typename T>
Generator<T> generate_begin(std::function<std::optional<T>()> f) return Generator<T>(f);
template <typename T>
Generator<T> generate_end(std::function<std::optional<T>()>) return Generator<T>();


Now for a suitable function foo you can use this like a normal input operator:



auto sum = std::accumulate(generate_begin(foo), generate_end(foo), 0);


I omitted the iterator traits which should be defined in Generator as they are in YSC's answer- they should be something like the below (and operator* should return reference, and you should add operator->, etc. etc.)



 // iterator traits
using difference_type = int;
using value_type = T;
using pointer = const T*;
using reference = const T&;
using iterator_category = std::input_iterator_tag;





share|improve this answer






















  • Thanks, this is the closest I've seen so far to what I originally needed. While the other answers do provide a solution to the simpler sub-problems, I'd rather have a a generic solution that works with any functor. I'll need some time to understand your code, but I believe this is pretty much what I need
    – GPhilo
    Sep 4 at 10:17










  • Note that if your generator function is more naturally written as throwing an exception or returning some sentinel value, you can easily write an adapter function to turn it into the required std::optional form.
    – Useless
    Sep 4 at 10:19










  • About the iterator traits, YSC shows them for type int. Using the generic type T what would difference_type be? Should I still use int? Or std::ptrdiff_t as I've seen in some examples? Also, do I uderstand it correctly that in my case an input_iterator_tag would be the right choice? (forward iterator requires the values to be modifiable, which is not my case)
    – GPhilo
    Sep 4 at 10:34











  • Correct, it's an input iterator (the function could be stateful, so we can only assume single-pass). For the difference type, int or size_t are fine. Mostly I didn't write them because value_type, reference_type and pointer_type need reference removal to be completely safe (a function could return optional<reference_wrapper<T>> to externally-stored persistent objects).
    – Useless
    Sep 4 at 10:44










  • I'm not sure I get the part about reference removal (template programming is almost entirely new to me), but I plan on using the classes only with simple types (most likely just floats) at least for now, so I believe I can ignore that part. As per the difference type, does it need to be signed? (if so, size_t won't work)
    – GPhilo
    Sep 4 at 10:51














up vote
2
down vote



accepted










If you want the generic generator you originally asked for (rather than the simpler use-cases added later), it is possible to set up something like this:



template <typename T>
struct Generator
Generator()
explicit Generator(std::function<std::optional<T>()> f_) : f(f_), v(f())

Generator(Generator<T> const &) = default;
Generator(Generator<T> &&) = default;
Generator<T>& operator=(Generator<T> const &) = default;
Generator<T>& operator=(Generator<T> &&) = default;

bool operator==(Generator<T> const &rhs)
return (!v) && (!rhs.v); // only compare equal if both at end

bool operator!=(Generator<T> const &rhs) return !(*this == rhs);

Generator<T>& operator++()
v = f();
return *this;

Generator<T> operator++(int)
auto tmp = *this;
++*this;
return tmp;


// throw `std::bad_optional_access` if you try to dereference an end iterator
T const& operator*() const
return v.value();


private:
std::function<std::optional<T>()> f;
std::optional<T> v;
;


if you have C++17 (if you don't, use Boost or just track validity manually). The begin/end functions needed to use this nicely look something like



template <typename T>
Generator<T> generate_begin(std::function<std::optional<T>()> f) return Generator<T>(f);
template <typename T>
Generator<T> generate_end(std::function<std::optional<T>()>) return Generator<T>();


Now for a suitable function foo you can use this like a normal input operator:



auto sum = std::accumulate(generate_begin(foo), generate_end(foo), 0);


I omitted the iterator traits which should be defined in Generator as they are in YSC's answer- they should be something like the below (and operator* should return reference, and you should add operator->, etc. etc.)



 // iterator traits
using difference_type = int;
using value_type = T;
using pointer = const T*;
using reference = const T&;
using iterator_category = std::input_iterator_tag;





share|improve this answer






















  • Thanks, this is the closest I've seen so far to what I originally needed. While the other answers do provide a solution to the simpler sub-problems, I'd rather have a a generic solution that works with any functor. I'll need some time to understand your code, but I believe this is pretty much what I need
    – GPhilo
    Sep 4 at 10:17










  • Note that if your generator function is more naturally written as throwing an exception or returning some sentinel value, you can easily write an adapter function to turn it into the required std::optional form.
    – Useless
    Sep 4 at 10:19










  • About the iterator traits, YSC shows them for type int. Using the generic type T what would difference_type be? Should I still use int? Or std::ptrdiff_t as I've seen in some examples? Also, do I uderstand it correctly that in my case an input_iterator_tag would be the right choice? (forward iterator requires the values to be modifiable, which is not my case)
    – GPhilo
    Sep 4 at 10:34











  • Correct, it's an input iterator (the function could be stateful, so we can only assume single-pass). For the difference type, int or size_t are fine. Mostly I didn't write them because value_type, reference_type and pointer_type need reference removal to be completely safe (a function could return optional<reference_wrapper<T>> to externally-stored persistent objects).
    – Useless
    Sep 4 at 10:44










  • I'm not sure I get the part about reference removal (template programming is almost entirely new to me), but I plan on using the classes only with simple types (most likely just floats) at least for now, so I believe I can ignore that part. As per the difference type, does it need to be signed? (if so, size_t won't work)
    – GPhilo
    Sep 4 at 10:51












up vote
2
down vote



accepted







up vote
2
down vote



accepted






If you want the generic generator you originally asked for (rather than the simpler use-cases added later), it is possible to set up something like this:



template <typename T>
struct Generator
Generator()
explicit Generator(std::function<std::optional<T>()> f_) : f(f_), v(f())

Generator(Generator<T> const &) = default;
Generator(Generator<T> &&) = default;
Generator<T>& operator=(Generator<T> const &) = default;
Generator<T>& operator=(Generator<T> &&) = default;

bool operator==(Generator<T> const &rhs)
return (!v) && (!rhs.v); // only compare equal if both at end

bool operator!=(Generator<T> const &rhs) return !(*this == rhs);

Generator<T>& operator++()
v = f();
return *this;

Generator<T> operator++(int)
auto tmp = *this;
++*this;
return tmp;


// throw `std::bad_optional_access` if you try to dereference an end iterator
T const& operator*() const
return v.value();


private:
std::function<std::optional<T>()> f;
std::optional<T> v;
;


if you have C++17 (if you don't, use Boost or just track validity manually). The begin/end functions needed to use this nicely look something like



template <typename T>
Generator<T> generate_begin(std::function<std::optional<T>()> f) return Generator<T>(f);
template <typename T>
Generator<T> generate_end(std::function<std::optional<T>()>) return Generator<T>();


Now for a suitable function foo you can use this like a normal input operator:



auto sum = std::accumulate(generate_begin(foo), generate_end(foo), 0);


I omitted the iterator traits which should be defined in Generator as they are in YSC's answer- they should be something like the below (and operator* should return reference, and you should add operator->, etc. etc.)



 // iterator traits
using difference_type = int;
using value_type = T;
using pointer = const T*;
using reference = const T&;
using iterator_category = std::input_iterator_tag;





share|improve this answer














If you want the generic generator you originally asked for (rather than the simpler use-cases added later), it is possible to set up something like this:



template <typename T>
struct Generator
Generator()
explicit Generator(std::function<std::optional<T>()> f_) : f(f_), v(f())

Generator(Generator<T> const &) = default;
Generator(Generator<T> &&) = default;
Generator<T>& operator=(Generator<T> const &) = default;
Generator<T>& operator=(Generator<T> &&) = default;

bool operator==(Generator<T> const &rhs)
return (!v) && (!rhs.v); // only compare equal if both at end

bool operator!=(Generator<T> const &rhs) return !(*this == rhs);

Generator<T>& operator++()
v = f();
return *this;

Generator<T> operator++(int)
auto tmp = *this;
++*this;
return tmp;


// throw `std::bad_optional_access` if you try to dereference an end iterator
T const& operator*() const
return v.value();


private:
std::function<std::optional<T>()> f;
std::optional<T> v;
;


if you have C++17 (if you don't, use Boost or just track validity manually). The begin/end functions needed to use this nicely look something like



template <typename T>
Generator<T> generate_begin(std::function<std::optional<T>()> f) return Generator<T>(f);
template <typename T>
Generator<T> generate_end(std::function<std::optional<T>()>) return Generator<T>();


Now for a suitable function foo you can use this like a normal input operator:



auto sum = std::accumulate(generate_begin(foo), generate_end(foo), 0);


I omitted the iterator traits which should be defined in Generator as they are in YSC's answer- they should be something like the below (and operator* should return reference, and you should add operator->, etc. etc.)



 // iterator traits
using difference_type = int;
using value_type = T;
using pointer = const T*;
using reference = const T&;
using iterator_category = std::input_iterator_tag;






share|improve this answer














share|improve this answer



share|improve this answer








edited Sep 4 at 11:47

























answered Sep 4 at 10:07









Useless

42.3k45093




42.3k45093











  • Thanks, this is the closest I've seen so far to what I originally needed. While the other answers do provide a solution to the simpler sub-problems, I'd rather have a a generic solution that works with any functor. I'll need some time to understand your code, but I believe this is pretty much what I need
    – GPhilo
    Sep 4 at 10:17










  • Note that if your generator function is more naturally written as throwing an exception or returning some sentinel value, you can easily write an adapter function to turn it into the required std::optional form.
    – Useless
    Sep 4 at 10:19










  • About the iterator traits, YSC shows them for type int. Using the generic type T what would difference_type be? Should I still use int? Or std::ptrdiff_t as I've seen in some examples? Also, do I uderstand it correctly that in my case an input_iterator_tag would be the right choice? (forward iterator requires the values to be modifiable, which is not my case)
    – GPhilo
    Sep 4 at 10:34











  • Correct, it's an input iterator (the function could be stateful, so we can only assume single-pass). For the difference type, int or size_t are fine. Mostly I didn't write them because value_type, reference_type and pointer_type need reference removal to be completely safe (a function could return optional<reference_wrapper<T>> to externally-stored persistent objects).
    – Useless
    Sep 4 at 10:44










  • I'm not sure I get the part about reference removal (template programming is almost entirely new to me), but I plan on using the classes only with simple types (most likely just floats) at least for now, so I believe I can ignore that part. As per the difference type, does it need to be signed? (if so, size_t won't work)
    – GPhilo
    Sep 4 at 10:51
















  • Thanks, this is the closest I've seen so far to what I originally needed. While the other answers do provide a solution to the simpler sub-problems, I'd rather have a a generic solution that works with any functor. I'll need some time to understand your code, but I believe this is pretty much what I need
    – GPhilo
    Sep 4 at 10:17










  • Note that if your generator function is more naturally written as throwing an exception or returning some sentinel value, you can easily write an adapter function to turn it into the required std::optional form.
    – Useless
    Sep 4 at 10:19










  • About the iterator traits, YSC shows them for type int. Using the generic type T what would difference_type be? Should I still use int? Or std::ptrdiff_t as I've seen in some examples? Also, do I uderstand it correctly that in my case an input_iterator_tag would be the right choice? (forward iterator requires the values to be modifiable, which is not my case)
    – GPhilo
    Sep 4 at 10:34











  • Correct, it's an input iterator (the function could be stateful, so we can only assume single-pass). For the difference type, int or size_t are fine. Mostly I didn't write them because value_type, reference_type and pointer_type need reference removal to be completely safe (a function could return optional<reference_wrapper<T>> to externally-stored persistent objects).
    – Useless
    Sep 4 at 10:44










  • I'm not sure I get the part about reference removal (template programming is almost entirely new to me), but I plan on using the classes only with simple types (most likely just floats) at least for now, so I believe I can ignore that part. As per the difference type, does it need to be signed? (if so, size_t won't work)
    – GPhilo
    Sep 4 at 10:51















Thanks, this is the closest I've seen so far to what I originally needed. While the other answers do provide a solution to the simpler sub-problems, I'd rather have a a generic solution that works with any functor. I'll need some time to understand your code, but I believe this is pretty much what I need
– GPhilo
Sep 4 at 10:17




Thanks, this is the closest I've seen so far to what I originally needed. While the other answers do provide a solution to the simpler sub-problems, I'd rather have a a generic solution that works with any functor. I'll need some time to understand your code, but I believe this is pretty much what I need
– GPhilo
Sep 4 at 10:17












Note that if your generator function is more naturally written as throwing an exception or returning some sentinel value, you can easily write an adapter function to turn it into the required std::optional form.
– Useless
Sep 4 at 10:19




Note that if your generator function is more naturally written as throwing an exception or returning some sentinel value, you can easily write an adapter function to turn it into the required std::optional form.
– Useless
Sep 4 at 10:19












About the iterator traits, YSC shows them for type int. Using the generic type T what would difference_type be? Should I still use int? Or std::ptrdiff_t as I've seen in some examples? Also, do I uderstand it correctly that in my case an input_iterator_tag would be the right choice? (forward iterator requires the values to be modifiable, which is not my case)
– GPhilo
Sep 4 at 10:34





About the iterator traits, YSC shows them for type int. Using the generic type T what would difference_type be? Should I still use int? Or std::ptrdiff_t as I've seen in some examples? Also, do I uderstand it correctly that in my case an input_iterator_tag would be the right choice? (forward iterator requires the values to be modifiable, which is not my case)
– GPhilo
Sep 4 at 10:34













Correct, it's an input iterator (the function could be stateful, so we can only assume single-pass). For the difference type, int or size_t are fine. Mostly I didn't write them because value_type, reference_type and pointer_type need reference removal to be completely safe (a function could return optional<reference_wrapper<T>> to externally-stored persistent objects).
– Useless
Sep 4 at 10:44




Correct, it's an input iterator (the function could be stateful, so we can only assume single-pass). For the difference type, int or size_t are fine. Mostly I didn't write them because value_type, reference_type and pointer_type need reference removal to be completely safe (a function could return optional<reference_wrapper<T>> to externally-stored persistent objects).
– Useless
Sep 4 at 10:44












I'm not sure I get the part about reference removal (template programming is almost entirely new to me), but I plan on using the classes only with simple types (most likely just floats) at least for now, so I believe I can ignore that part. As per the difference type, does it need to be signed? (if so, size_t won't work)
– GPhilo
Sep 4 at 10:51




I'm not sure I get the part about reference removal (template programming is almost entirely new to me), but I plan on using the classes only with simple types (most likely just floats) at least for now, so I believe I can ignore that part. As per the difference type, does it need to be signed? (if so, size_t won't work)
– GPhilo
Sep 4 at 10:51












up vote
4
down vote













You should use a C++ idiom: the forward iterator. This let you use C++ syntactic sugar and support the standard library. Here is a minimal example:



template<int tstart, int tstop, int tstep = 1>
class Range
public:
class iterator
int start;
int stop;
int step;
int current;
public:
iterator(int start, int stop, int step = 0, int current = tstart) : start(start), stop(stop), step(step == 0 ? (start < stop ? 1 : -1) : step), current(current)
iterator& operator++() current += step; return *this;
iterator operator++(int) iterator retval = *this; ++(*this); return retval;
bool operator==(iterator other) const return std::tie(current, step, stop) == std::tie(other.current, other.step, other.stop);
bool operator!=(iterator other) const return !(*this == other);
long operator*() return current;
// iterator traits
using difference_type = int;
using value_type = int;
using pointer = const int*;
using reference = const int&;
using iterator_category = std::forward_iterator_tag;
;
iterator begin() return iteratortstart, tstop, tstep;
iterator end() return iteratortstart, tstop, tstep, tstop;
;


It can be used with the C++98 way:



using range = Range<0, 10, 2>;
auto r = range;
for (range::iterator it = r.begin() ; it != r.end() ; ++it)
std::cout << *it << 'n';



Or with the new range loop:



for (auto n : Range<0, 10, 2>) 
std::cout << n << 'n';



In cunjunction with the stl:



std::copy(std::begin(r), std::end(r), std::back_inserter(v));


Demo: http://coliru.stacked-crooked.com/a/35ad4ce16428e65d






share|improve this answer






















  • You don't need the start, stop, step members, since those values are template parameters. You also don't need to compare step and stop, since those can only be different for a different instantiation.
    – molbdnilo
    Sep 4 at 9:35











  • @molbdnilo I agree. In fact, that iterator would need additional enhancements before it starts being useful. This is just a example to avoid complex and long explanations.
    – YSC
    Sep 4 at 10:49














up vote
4
down vote













You should use a C++ idiom: the forward iterator. This let you use C++ syntactic sugar and support the standard library. Here is a minimal example:



template<int tstart, int tstop, int tstep = 1>
class Range
public:
class iterator
int start;
int stop;
int step;
int current;
public:
iterator(int start, int stop, int step = 0, int current = tstart) : start(start), stop(stop), step(step == 0 ? (start < stop ? 1 : -1) : step), current(current)
iterator& operator++() current += step; return *this;
iterator operator++(int) iterator retval = *this; ++(*this); return retval;
bool operator==(iterator other) const return std::tie(current, step, stop) == std::tie(other.current, other.step, other.stop);
bool operator!=(iterator other) const return !(*this == other);
long operator*() return current;
// iterator traits
using difference_type = int;
using value_type = int;
using pointer = const int*;
using reference = const int&;
using iterator_category = std::forward_iterator_tag;
;
iterator begin() return iteratortstart, tstop, tstep;
iterator end() return iteratortstart, tstop, tstep, tstop;
;


It can be used with the C++98 way:



using range = Range<0, 10, 2>;
auto r = range;
for (range::iterator it = r.begin() ; it != r.end() ; ++it)
std::cout << *it << 'n';



Or with the new range loop:



for (auto n : Range<0, 10, 2>) 
std::cout << n << 'n';



In cunjunction with the stl:



std::copy(std::begin(r), std::end(r), std::back_inserter(v));


Demo: http://coliru.stacked-crooked.com/a/35ad4ce16428e65d






share|improve this answer






















  • You don't need the start, stop, step members, since those values are template parameters. You also don't need to compare step and stop, since those can only be different for a different instantiation.
    – molbdnilo
    Sep 4 at 9:35











  • @molbdnilo I agree. In fact, that iterator would need additional enhancements before it starts being useful. This is just a example to avoid complex and long explanations.
    – YSC
    Sep 4 at 10:49












up vote
4
down vote










up vote
4
down vote









You should use a C++ idiom: the forward iterator. This let you use C++ syntactic sugar and support the standard library. Here is a minimal example:



template<int tstart, int tstop, int tstep = 1>
class Range
public:
class iterator
int start;
int stop;
int step;
int current;
public:
iterator(int start, int stop, int step = 0, int current = tstart) : start(start), stop(stop), step(step == 0 ? (start < stop ? 1 : -1) : step), current(current)
iterator& operator++() current += step; return *this;
iterator operator++(int) iterator retval = *this; ++(*this); return retval;
bool operator==(iterator other) const return std::tie(current, step, stop) == std::tie(other.current, other.step, other.stop);
bool operator!=(iterator other) const return !(*this == other);
long operator*() return current;
// iterator traits
using difference_type = int;
using value_type = int;
using pointer = const int*;
using reference = const int&;
using iterator_category = std::forward_iterator_tag;
;
iterator begin() return iteratortstart, tstop, tstep;
iterator end() return iteratortstart, tstop, tstep, tstop;
;


It can be used with the C++98 way:



using range = Range<0, 10, 2>;
auto r = range;
for (range::iterator it = r.begin() ; it != r.end() ; ++it)
std::cout << *it << 'n';



Or with the new range loop:



for (auto n : Range<0, 10, 2>) 
std::cout << n << 'n';



In cunjunction with the stl:



std::copy(std::begin(r), std::end(r), std::back_inserter(v));


Demo: http://coliru.stacked-crooked.com/a/35ad4ce16428e65d






share|improve this answer














You should use a C++ idiom: the forward iterator. This let you use C++ syntactic sugar and support the standard library. Here is a minimal example:



template<int tstart, int tstop, int tstep = 1>
class Range
public:
class iterator
int start;
int stop;
int step;
int current;
public:
iterator(int start, int stop, int step = 0, int current = tstart) : start(start), stop(stop), step(step == 0 ? (start < stop ? 1 : -1) : step), current(current)
iterator& operator++() current += step; return *this;
iterator operator++(int) iterator retval = *this; ++(*this); return retval;
bool operator==(iterator other) const return std::tie(current, step, stop) == std::tie(other.current, other.step, other.stop);
bool operator!=(iterator other) const return !(*this == other);
long operator*() return current;
// iterator traits
using difference_type = int;
using value_type = int;
using pointer = const int*;
using reference = const int&;
using iterator_category = std::forward_iterator_tag;
;
iterator begin() return iteratortstart, tstop, tstep;
iterator end() return iteratortstart, tstop, tstep, tstop;
;


It can be used with the C++98 way:



using range = Range<0, 10, 2>;
auto r = range;
for (range::iterator it = r.begin() ; it != r.end() ; ++it)
std::cout << *it << 'n';



Or with the new range loop:



for (auto n : Range<0, 10, 2>) 
std::cout << n << 'n';



In cunjunction with the stl:



std::copy(std::begin(r), std::end(r), std::back_inserter(v));


Demo: http://coliru.stacked-crooked.com/a/35ad4ce16428e65d







share|improve this answer














share|improve this answer



share|improve this answer








edited Sep 4 at 9:20

























answered Sep 4 at 9:18









YSC

16.9k33986




16.9k33986











  • You don't need the start, stop, step members, since those values are template parameters. You also don't need to compare step and stop, since those can only be different for a different instantiation.
    – molbdnilo
    Sep 4 at 9:35











  • @molbdnilo I agree. In fact, that iterator would need additional enhancements before it starts being useful. This is just a example to avoid complex and long explanations.
    – YSC
    Sep 4 at 10:49
















  • You don't need the start, stop, step members, since those values are template parameters. You also don't need to compare step and stop, since those can only be different for a different instantiation.
    – molbdnilo
    Sep 4 at 9:35











  • @molbdnilo I agree. In fact, that iterator would need additional enhancements before it starts being useful. This is just a example to avoid complex and long explanations.
    – YSC
    Sep 4 at 10:49















You don't need the start, stop, step members, since those values are template parameters. You also don't need to compare step and stop, since those can only be different for a different instantiation.
– molbdnilo
Sep 4 at 9:35





You don't need the start, stop, step members, since those values are template parameters. You also don't need to compare step and stop, since those can only be different for a different instantiation.
– molbdnilo
Sep 4 at 9:35













@molbdnilo I agree. In fact, that iterator would need additional enhancements before it starts being useful. This is just a example to avoid complex and long explanations.
– YSC
Sep 4 at 10:49




@molbdnilo I agree. In fact, that iterator would need additional enhancements before it starts being useful. This is just a example to avoid complex and long explanations.
– YSC
Sep 4 at 10:49










up vote
2
down vote













The range based for loop is all about the iterator implementing begin(), end() and operator++.



So the generator must implement them.



template<typename T>
struct generator
T first;
T last;

struct iterator
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T *;
using reference = T &;
T value;

iterator(T &value) : value(value)

iterator &operator++()
++value;
return *this;

iterator operator++(int) = delete;

bool operator==(const iterator &rhs) return value == rhs.value;
bool operator!=(const iterator &rhs) return !(*this == rhs);
const reference operator *() return value;
const pointer operator->() const return std::addressof(value);
;

iterator begin() return iterator(first);
iterator end() return iterator(last);
;


Then add a function which instantiates the generator and you are done



template<typename T>
generator<T> range(T start, T end)
return generator<T> start, end ;


for (auto i : range(0, 10))







share|improve this answer






















  • I read it was deprecated in C++17 already (although tbh I can live with that for now)
    – GPhilo
    Sep 4 at 9:25














up vote
2
down vote













The range based for loop is all about the iterator implementing begin(), end() and operator++.



So the generator must implement them.



template<typename T>
struct generator
T first;
T last;

struct iterator
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T *;
using reference = T &;
T value;

iterator(T &value) : value(value)

iterator &operator++()
++value;
return *this;

iterator operator++(int) = delete;

bool operator==(const iterator &rhs) return value == rhs.value;
bool operator!=(const iterator &rhs) return !(*this == rhs);
const reference operator *() return value;
const pointer operator->() const return std::addressof(value);
;

iterator begin() return iterator(first);
iterator end() return iterator(last);
;


Then add a function which instantiates the generator and you are done



template<typename T>
generator<T> range(T start, T end)
return generator<T> start, end ;


for (auto i : range(0, 10))







share|improve this answer






















  • I read it was deprecated in C++17 already (although tbh I can live with that for now)
    – GPhilo
    Sep 4 at 9:25












up vote
2
down vote










up vote
2
down vote









The range based for loop is all about the iterator implementing begin(), end() and operator++.



So the generator must implement them.



template<typename T>
struct generator
T first;
T last;

struct iterator
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T *;
using reference = T &;
T value;

iterator(T &value) : value(value)

iterator &operator++()
++value;
return *this;

iterator operator++(int) = delete;

bool operator==(const iterator &rhs) return value == rhs.value;
bool operator!=(const iterator &rhs) return !(*this == rhs);
const reference operator *() return value;
const pointer operator->() const return std::addressof(value);
;

iterator begin() return iterator(first);
iterator end() return iterator(last);
;


Then add a function which instantiates the generator and you are done



template<typename T>
generator<T> range(T start, T end)
return generator<T> start, end ;


for (auto i : range(0, 10))







share|improve this answer














The range based for loop is all about the iterator implementing begin(), end() and operator++.



So the generator must implement them.



template<typename T>
struct generator
T first;
T last;

struct iterator
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T *;
using reference = T &;
T value;

iterator(T &value) : value(value)

iterator &operator++()
++value;
return *this;

iterator operator++(int) = delete;

bool operator==(const iterator &rhs) return value == rhs.value;
bool operator!=(const iterator &rhs) return !(*this == rhs);
const reference operator *() return value;
const pointer operator->() const return std::addressof(value);
;

iterator begin() return iterator(first);
iterator end() return iterator(last);
;


Then add a function which instantiates the generator and you are done



template<typename T>
generator<T> range(T start, T end)
return generator<T> start, end ;


for (auto i : range(0, 10))








share|improve this answer














share|improve this answer



share|improve this answer








edited Sep 4 at 9:28

























answered Sep 4 at 9:21









Serve Laurijssen

5,37822252




5,37822252











  • I read it was deprecated in C++17 already (although tbh I can live with that for now)
    – GPhilo
    Sep 4 at 9:25
















  • I read it was deprecated in C++17 already (although tbh I can live with that for now)
    – GPhilo
    Sep 4 at 9:25















I read it was deprecated in C++17 already (although tbh I can live with that for now)
– GPhilo
Sep 4 at 9:25




I read it was deprecated in C++17 already (although tbh I can live with that for now)
– GPhilo
Sep 4 at 9:25










up vote
1
down vote













The use case you describe (concatenation of ranges etc.) might justify a dependency on a library, so here is a solution based on range-v3, on its way into C++20. You can easily iterate through integral values, here from 0 to 10 with a step size of 2,



#include <range/v3/all.hpp>

using namespace ranges;

for (auto i : view::ints(0, 11) | view::stride(2))
std::cout << i << "n";


or implement a similar loop with floating point values (note that [from, to] is a closed range here, and the third argument denotes the number of steps)



for (auto f : view::linear_distribute(1.25f, 2.5f, 10))
std::cout << f << "n";


and when it comes to concatenation, the library begins to shine:



const std::vector world32, 119, 111, 114, 108, 100;

for (auto i : view::concat("hello", world))
std::cout << char(i);

std::cout << "n";


Note that the above snippets compile with -std=c++17. The library is header only.






share|improve this answer






















  • This looks great and I wish that was included in the standard already, but currently this is out of reach for me sadly. Also, I need to be able to alter the step while iterating over floats, which I think this doesn't support (although I haven't looked in depth into the library)
    – GPhilo
    Sep 4 at 10:19















up vote
1
down vote













The use case you describe (concatenation of ranges etc.) might justify a dependency on a library, so here is a solution based on range-v3, on its way into C++20. You can easily iterate through integral values, here from 0 to 10 with a step size of 2,



#include <range/v3/all.hpp>

using namespace ranges;

for (auto i : view::ints(0, 11) | view::stride(2))
std::cout << i << "n";


or implement a similar loop with floating point values (note that [from, to] is a closed range here, and the third argument denotes the number of steps)



for (auto f : view::linear_distribute(1.25f, 2.5f, 10))
std::cout << f << "n";


and when it comes to concatenation, the library begins to shine:



const std::vector world32, 119, 111, 114, 108, 100;

for (auto i : view::concat("hello", world))
std::cout << char(i);

std::cout << "n";


Note that the above snippets compile with -std=c++17. The library is header only.






share|improve this answer






















  • This looks great and I wish that was included in the standard already, but currently this is out of reach for me sadly. Also, I need to be able to alter the step while iterating over floats, which I think this doesn't support (although I haven't looked in depth into the library)
    – GPhilo
    Sep 4 at 10:19













up vote
1
down vote










up vote
1
down vote









The use case you describe (concatenation of ranges etc.) might justify a dependency on a library, so here is a solution based on range-v3, on its way into C++20. You can easily iterate through integral values, here from 0 to 10 with a step size of 2,



#include <range/v3/all.hpp>

using namespace ranges;

for (auto i : view::ints(0, 11) | view::stride(2))
std::cout << i << "n";


or implement a similar loop with floating point values (note that [from, to] is a closed range here, and the third argument denotes the number of steps)



for (auto f : view::linear_distribute(1.25f, 2.5f, 10))
std::cout << f << "n";


and when it comes to concatenation, the library begins to shine:



const std::vector world32, 119, 111, 114, 108, 100;

for (auto i : view::concat("hello", world))
std::cout << char(i);

std::cout << "n";


Note that the above snippets compile with -std=c++17. The library is header only.






share|improve this answer














The use case you describe (concatenation of ranges etc.) might justify a dependency on a library, so here is a solution based on range-v3, on its way into C++20. You can easily iterate through integral values, here from 0 to 10 with a step size of 2,



#include <range/v3/all.hpp>

using namespace ranges;

for (auto i : view::ints(0, 11) | view::stride(2))
std::cout << i << "n";


or implement a similar loop with floating point values (note that [from, to] is a closed range here, and the third argument denotes the number of steps)



for (auto f : view::linear_distribute(1.25f, 2.5f, 10))
std::cout << f << "n";


and when it comes to concatenation, the library begins to shine:



const std::vector world32, 119, 111, 114, 108, 100;

for (auto i : view::concat("hello", world))
std::cout << char(i);

std::cout << "n";


Note that the above snippets compile with -std=c++17. The library is header only.







share|improve this answer














share|improve this answer



share|improve this answer








edited Sep 4 at 10:06

























answered Sep 4 at 9:59









lubgr

6,61021340




6,61021340











  • This looks great and I wish that was included in the standard already, but currently this is out of reach for me sadly. Also, I need to be able to alter the step while iterating over floats, which I think this doesn't support (although I haven't looked in depth into the library)
    – GPhilo
    Sep 4 at 10:19

















  • This looks great and I wish that was included in the standard already, but currently this is out of reach for me sadly. Also, I need to be able to alter the step while iterating over floats, which I think this doesn't support (although I haven't looked in depth into the library)
    – GPhilo
    Sep 4 at 10:19
















This looks great and I wish that was included in the standard already, but currently this is out of reach for me sadly. Also, I need to be able to alter the step while iterating over floats, which I think this doesn't support (although I haven't looked in depth into the library)
– GPhilo
Sep 4 at 10:19





This looks great and I wish that was included in the standard already, but currently this is out of reach for me sadly. Also, I need to be able to alter the step while iterating over floats, which I think this doesn't support (although I haven't looked in depth into the library)
– GPhilo
Sep 4 at 10:19


















 

draft saved


draft discarded















































 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52162505%2fhow-to-lazily-generate-a-finished-sequence-of-items-and-iterate-over-it%23new-answer', 'question_page');

);

Post as a guest













































































這個網誌中的熱門文章

Is there any way to eliminate the singular point to solve this integral by hand or by approximations?

Why am i infinitely getting the same tweet with the Twitter Search API?

Amount of Number Combinations to Reach a Sum of 10 With Integers 1-9 Using 2 or More Integers [closed]