I want a function that modifies passed in elements of known type, matching against private data we iterate over in an outer loop comparing each to the passed in elements. Quantities are small so no need to build a map or optimize away the n² nature of this. In pseudo-code:
function fill_in_thing_data([in-out] things)
for (item in private_items)
info = wrangle(item)
for (thing in things)
if (matching(thing, info))
thing.data = info.data
Say private_items is expensive to iterator over, or to setup for iteration, and so I definitely want that in the outer loop.
Easy, right? Except that I want to two C++ overloaded functions that share some underlying code, one that takes a non-const reference to thing, one that takes a reference to a vector of things:
void fill_in_thing_data(Thing& single_thing);
void fill_in_thing_data(std::vector<Thing>& some_things);
What's the best way to share code between these 2 functions? I was thinking a helper function that takes iterators or some similar sequence type, and the first function passing in an iterator or sequence made out of the 1 element, the other one made out of the vector. I want to use C++ idioms so was looking at doing it with ForwardIterators.
Problem:
- C++ iterators in general aren't easy. Ok using them is ok, but writing a function that takes any kind of ForwardIterator to simply a known type seems unexpectedly tricky.
- Is a special case iterator over a single element reference something that's available? Or something that's easy to define? It seems the answers are no and no.
Instead of iterators, this is my ugly solution using a visitor lambda passed in, and a visit lambda passed back, allowing the iteration logic to be abstracted out of the shared function:
using VisitThing = std::function<bool(Thing& thing)>;
using ThingVisitor = std::function<bool(VisitThing visit)>;
void match_things(ThingVisitor visitor);
void fill_in_thing_data(Thing& single_thing) {
match_things([&](VisitThing visit) {
visit(single_thing);
});
}
void fill_in_thing_data(std::vector<Thing>& some_things) {
match_things([&](VisitThing visit) {
for (auto& thing : some_things) { visit(thing); }
});
}
void match_things(ThingVisitor visitor) {
auto stuff = fetch_private_stuff()
while (item = stuff.get_next_item()) { // can't change this home-brew iteration
auto item_info = wrangle(item) // but more complex, logic about skipping items etc
visitor([&](Thing& thing) {
if (item_info.token == thing.token)) {
thing.data = item_info.data;
}
}
}
}
Am I wrong and can do this with iterators without too much complexity? Or can I do this better in some other way, like maybe a good data structure class that can either be built with either the reference or the vector and then pass that in? Or like something else obvious I'm just not seeing? Thanks!