I am looking at the lambda generators from https://stackoverflow.com/a/12735970 and https://stackoverflow.com/a/12639820. I would like to adapt these to a template version and I'm wondering what I should return to signal that the generator has reached its end.
Consider
template<typename T>
std::function<T()> my_template_vector_generator(std::vector<T> &v) {
int idx = 0;
return [=,&v]() mutable {
return v[idx++];
};
}
The [=,&v]
addition is mine and I hope it's correct. For now, it lets me change the vector outside as expected. Please comment on this if you have a bad feeling about it...
For reference, this works (REQUIRE is from catch):
std::vector<double> v({1.0, 2.0, 3.0});
auto vec_gen = my_template_vector_generator(v);
REQUIRE( vec_gen() == 1 );
REQUIRE( vec_gen() == 2 );
v[2] = 5.0;
REQUIRE( vec_gen() == 5.0 );
That vector version obviously requires knowledge of v.size()
at the call site. I'd like to go without that by returning something that indicates the generator is empty.
Brainstorming, I can think of the following:
- return
pair<T, bool>
and indicatefalse
once there are no more values. - return iterators to the container values and iterate
for (auto it=gen(); it!=gen.end(); it=gen()) {cout << *it << endl;}
- wrapping the generator in a class and implementing an
empty()
method. Not entirely sure how that would work, however.
Does either of these versions feel good to you? Do you have another idea? I'd particularly be interested in implications when mapping such a generator or combining them recursively (see this C++14 blog post for inspiration).
Update: After hearing the optional suggestions, I implemented something based on unique_ptr.
template<typename T>
std::function<std::unique_ptr<T>()> my_pointer_template_vector_generator(std::vector<T> &v) {
int idx = 0;
return [=,&v]() mutable {
if (idx < v.size()) {
return std::unique_ptr<T>(new T(v[idx++]));
} else {
return std::unique_ptr<T>();
}
};
}
This seems to work, consider the following passing test.
TEST_CASE("Pointer generator terminates as expected") {
std::vector<double> v({1.0, 2.0, 3.0});
int k = 0;
auto gen = my_pointer_template_vector_generator(v);
while(auto val = gen()) {
REQUIRE( *val == v[k++] );
}
REQUIRE( v.size() == k );
}
I have some concerns about creating lots of new T. I suppose I could also return pointers into the vector and nullptr otherwise. That should work in the same way. What do you think about this?