# Best syntax for executing an action between each loop iteration.

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:

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)
• This seems more appropriate for Stack Overflow. – Fred Nurk May 4 '11 at 5:27
• Though it doesn't generalize all situations, for the specific example you give, I'd use an infix_iterator: stackoverflow.com/questions/3496982/…. This is similar to your first option, except that the iterator and the iteration are separated from each other. – Jerry Coffin May 4 '11 at 21:46

The closest I've come to this in C or C++ is a slight modification to Knuth's loop-and-a-half:

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";
}


C++0x allows you to generalize:

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";
}


Lambda capture even allows you to modify local variables in the function calling foreach.

• I really like this solution, and like that you have provided a C++0x generalization (I am an avid user of C++0x though admittedly most are not). I would also add a Range overload to use c++0x range based for loop, and I would also add a version that accepts four lambdas "check" "increment" "body" "between". I am going to play with this solution today. – mmocny May 4 '11 at 14:57

For more complex object, sample:

template<class T>
struct Walker
{
Walker(Item *parent): m_parent(parent){}
Walker(Item *parent, T const& f): m_parent(parent), f(f){}
// visit all elements
Walker& Walk()
{
TestClass::iterator it = parent->begin(), end = parent->end();
for( ; it != end; ++it)
{
f.Visit(*it);
}
return *this;
}
// get visited result
T const& GetF()const { return f; }
private:
Walker& operator = (Walker const& rhs);
Item* m_parent;
T f;
};


and define class, for example:

struct VisitorClass
{
void Visit(TestClass*){}
};


after that use it:

Walker<VisitorClass> testVisit(this);

• I don't think this answers the question.. and I very well know about the visitor pattern and don't think your implementation is very well done.. – mmocny May 4 '11 at 15:00