-
Notifications
You must be signed in to change notification settings - Fork 63
Description
I suspect there are good reasons to delete the iterators in python/template/macros/iterator/iterator.jinja2, but it makes searching through a collection a bit more cumbersome at times. I am wondering if there are ways in which some of the choices on iterator copy constructors and assignment operators deletion can be relaxed, or am I missing something I should be using instead?
The following are attempts at finding the first electron in an MCParticlesCollection. I am using gcc-11 with -std=gnu++20.
Using find_if to get an iterator to the first electron
const auto mc_first_electron = std::find_if(
mcparts.begin(),
mcparts.end(),
[](const auto& p){ return p.getPDG() == 11; });This fails because MCParticleCollectionIterator has a deleted copy constructor which is required (likely) to return a copy of the internal iterator inside std::find_if.
Using a simple (old-style) iterator loop
If we can't copy an iterator, then let's avoid the copy.
auto mc_first_electron = mcparts.end();
for (auto p = mcparts.begin(); p != mcparts.end(); p++) {
if (p->getPDG() == 11) {
mc_first_electron = p;
break;
}
}This fails because iterator post-increment is not implemented. Ok, easy enough to change (and arguably better though unlikely that an iterator takes up so much memory this matters here; still, it is good to instill best practices).
auto mc_first_electron = mcparts.end();
for (auto p = mcparts.begin(); p != mcparts.end(); ++p) {
if (p->getPDG() == 11) {
mc_first_electron = p;
break;
}
}This still fails but now because we cannot assign p to mc_first_electron since the assignment operator for iterators is deleted. Ok, we can avoid this assignment too, by avoiding a locally scoped iterator.
auto mc_first_electron = mcparts.begin();
for (; mc_first_electron != mcparts.end(); ++mc_first_electron) {
if (mc_first_electron->getPDG() == 11) {
break;
}
}Ah, we have something that compiles.
Checking that we actually found something
After the search, we want to make sure we found something (and return if not).
if (mc_first_electron == mcparts.end()) {
debug() << "No electron found" << endmsg;
return StatusCode::FAILURE;
}This now fails again because operator== isn't defined for iterators. We already used operator!= above, so we end up with this:
if (!(mc_first_electron != mcparts.end())) {
debug() << "No electron found" << endmsg;
return StatusCode::FAILURE;
}Ultimately, in a c++20 world,...
When I started off, I had hopes of being able to use the following compact syntax:
auto is_electron = [](const auto& p){ return p.getPDG() == 11; };
for (const auto& e: mcparts | std::views::filter(is_electron)) {
// stuff
}This does not compile (unsurprisingly) due to the missing operator| on collections. Implementing this doesn't seem too onerous but I think I'd need to understand first what the design decisions were for the deleted iterators and assignment operators.