Reputation: 9003
What I need is the following hierarchy of classes (given here as a sketch):
class DataClass {}
class AbstractGenerator {
public:
// Generates DataClass objects one by one. In a lazy manner
virtual DataClass produce() = 0;
}
class RandGenerator : AbstractGenerator {
public:
RandGenerator(int maximal_int) : maximal(maximal_int) {}
DataClass produce() {
// get a random number from 0 to this->maximal
// make a DataClass object from the random int and return it
}
private:
int maximal;
}
class FromFileGenerator : AbstractGenerator {
public:
FromFileGenerator(string file_name) : f_name(file_name) {}
DataClass produce() {
// read the next line from the file
// deserialize the DataClass object from the line and return it
}
private:
string f_name;
}
What I want to support for both RandGenerator
and FromFileGnerator
is:
RandGenerator rg();
for (DataClass data : rg) {...}
And also some method of taking "first n elements of the generator".
What are the appropriate tools in the plain C++11 that one could use to achieve this, or whatever is the closest to this in C++11?
Upvotes: 1
Views: 93
Reputation: 66922
boost::function_input_iterator
is the normal tool for this job, but since you want "plain" C++, we can just reimplement the same concept.
class generator_iterator {
std::shared_ptr<AbstractGenerator> generator;
public:
using iterator_category = std::input_iterator_tag;
generator_iterator(std::shared_ptr<AbstractGenerator> generator_)
:generator(generator_) {}
DataClass operator*(){return generator->produce();}
generator_iterator& operator++(){return *this;}
generator_iterator operator++(int){return *this;}
//plus all the other normal bits for an output_iterator
};
And then in your AbstractGenerator
class, provide begin
and end
methods.
generator_iterator begin() {return {this};}
generator_iterator end() {return {nullptr};} //or other logic depending on how you want to detect the end of a series
Upvotes: 2
Reputation: 16156
Add a begin
and end
member function to AbstractGenerator
, which return iterators which call the produce
member function.
Such an iterator (demo) could look similar to this:
template<typename Fn>
struct CallRepeatedlyIterator
{
using iterator_category = std::input_iterator_tag;
using value_type = typename std::result_of<Fn()>::type;
// Not sure if that's correct (probably not):
using difference_type = unsigned;
using pointer = value_type *;
using reference = value_type &;
bool is_end;
union {
Fn fn;
};
union {
value_type buffer;
};
value_type operator*() const
{
return buffer;
}
CallRepeatedlyIterator & operator++()
{
buffer = fn();
return *this;
}
CallRepeatedlyIterator()
: is_end(true)
{}
explicit CallRepeatedlyIterator(Fn f)
: is_end(false)
{
new (&fn) Fn(f);
new (&buffer) value_type(fn());
}
bool operator==(CallRepeatedlyIterator const & other) const
{
return is_end && other.is_end;
}
bool operator!=(CallRepeatedlyIterator const & other) const
{
return !(*this == other);
}
// NOTE: A destructor is missing here! It needs to destruct fn and buffer
// if its not the end iterator.
};
Now your begin
member function returns such an iterator which calls produce
(e.g. using a by reference capturing lambda) and end
returns an "end" iterator.
This means that your for
loop would run forever (no way to reach the end iterator)!
Upvotes: 1