patterncppMinor
Best syntax for executing an action between each loop iteration.
Viewed 0 times
executingeachloopsyntaxactioniterationbetweenforbest
Problem
The question of the cleanest way to write a loop that executes some action between each iteration has always interested me.
In a sense, what is the best way in c/c++ to implement this fictional construct:
Using such a construct you could trivially implement string join(): (pseudocode)
I have looked through some popular code libraries to see how these types of loops are implemented, and there are some common strategies:
Neither of these is a completely generalized solution, and while its only a few lines + a state variable, I'm trying to find an ideal solution.
Priorities:
In a sense, what is the best way in c/c++ to implement this fictional construct:
for (...) {
} between {
}Using such a construct you could trivially implement string join(): (pseudocode)
result = "";
foreach ( item : input ) {
result += str(item);
} between {
result += sep;
}I have looked through some popular code libraries to see how these types of loops are implemented, and there are some common strategies:
- move the "between" code into an "if (!first/last iteration)" inside the loop.
- This is the go-to method when the index/iterator/result freely stores the notion of first/last iteration (such as checks for values of 0, .empty(), NULL etc).
- transform the "loop body" into a function and call it from two places: before and during the loop, and change the loop to skip the first element. (minor code duplication)
- This is the go-to method when the "loop body" is a single function call
Neither of these is a completely generalized solution, and while its only a few lines + a state variable, I'm trying to find an ideal solution.
Priorities:
- No needless source code duplication (but willing to accept binary code duplication)
- Efficiency
- Clear semantics
- Trivially usable (aka simple syntax, few library requirements)
Solution
The closest I've come to this in C or C++ is a slight modification to Knuth's loop-and-a-half:
C++0x allows you to generalize:
Lambda capture even allows you to modify local variables in the function calling foreach.
template
void print(std::vector const &x) {
std::cout ::const_iterator
begin = x.begin(),
end = x.end();
if (begin != end) { // Duplicated condition to handle the empty case.
while (true) { // Here is the "loop and a half".
std::cout << *begin;
if (++begin == end) break;
std::cout << ", ";
}
}
std::cout << "]\n";
}C++0x allows you to generalize:
template
void foreach(Iter begin, Iter end, Body body, Between between) {
if (begin != end) { // This duplication doesn't matter as it is
// wrapped up in a library function.
while (true) {
body(*begin);
if (++begin == end) break;
between();
}
}
}
template
void print(std::vector const &x) {
std::cout << '[';
foreach(x.begin(), x.end(),
[](T const& v) {
std::cout << v;
// Long code here is still readable.
},
[]{
std::cout << ", ";
// Long code here is still readable.
});
std::cout << "]\n";
}Lambda capture even allows you to modify local variables in the function calling foreach.
Code Snippets
template<class T>
void print(std::vector<T> const &x) {
std::cout << '[';
typename std::vector<T>::const_iterator
begin = x.begin(),
end = x.end();
if (begin != end) { // Duplicated condition to handle the empty case.
while (true) { // Here is the "loop and a half".
std::cout << *begin;
if (++begin == end) break;
std::cout << ", ";
}
}
std::cout << "]\n";
}template<class Iter, class Body, class Between>
void foreach(Iter begin, Iter end, Body body, Between between) {
if (begin != end) { // This duplication doesn't matter as it is
// wrapped up in a library function.
while (true) {
body(*begin);
if (++begin == end) break;
between();
}
}
}
template<class T>
void print(std::vector<T> const &x) {
std::cout << '[';
foreach(x.begin(), x.end(),
[](T const& v) {
std::cout << v;
// Long code here is still readable.
},
[]{
std::cout << ", ";
// Long code here is still readable.
});
std::cout << "]\n";
}Context
StackExchange Code Review Q#2225, answer score: 5
Revisions (0)
No revisions yet.