Skip to content

Instantly share code, notes, and snippets.

@MatthewSteel
Created June 24, 2012 10:02
Show Gist options
  • Save MatthewSteel/2982696 to your computer and use it in GitHub Desktop.
Save MatthewSteel/2982696 to your computer and use it in GitHub Desktop.
A "proper" for-each algorithm for tuples in C++11. Everything type-checked, no virtual function calls, heterogeneous data works.
#include <tuple>
#include <iostream>
#include <stdexcept>
using namespace std;
/*
* "A partially specialized non-type argument expression shall not involve a
* template parameter of the partial specialization except when the argument
* expression is a simple identifier."
* A hack to wrap our ints so we can do partial specialisation for the
* end-iterator class.
*/
template<int n> struct IntType {
public:
const static int get=n;
};
/*
* Iterator class. Everything type-checked properly, no inheritance or any of
* that garbage.
* Have written assignment and equality operators, a plus<int>() func, not
* included for brevity.
*/
template<typename Tuple, typename n=IntType<0>>
class TupleIterator {
public:
TupleIterator(Tuple &t) : t(t) {}
typename tuple_element<n::get, Tuple>::type& operator*() {
return get<n::get>(t);
}
TupleIterator<Tuple, IntType<n::get+1>> operator++() {
return TupleIterator<Tuple, IntType<n::get+1>>(t);
}
private:
Tuple &t;
};
/*partial specialisation for an `end` pointer. Could give it a deref method
that throws if we wanted nice runtime errors instead of ugly compile-time
ones... */
template<typename Tuple>
class TupleIterator<Tuple, IntType<tuple_size<Tuple>::value>> {
public:
TupleIterator(Tuple &t) : t(t) {}
private:
Tuple &t;
};
/*
* begin/end for algorithms.
*/
template<typename Tuple>
TupleIterator<Tuple, IntType<0>> begin(Tuple &t) {
return TupleIterator<Tuple>(t);
}
template<typename Tuple>
TupleIterator<Tuple, IntType<tuple_size<Tuple>::value>> end(Tuple &t) {
return TupleIterator<Tuple, IntType<tuple_size<Tuple>::value>>(t);
}
/*
* for-each algorithm. Kinda "recursive", but never uses same template
* instantiation in a loop. GCC seems to get confused after about depth=3 and
* starts generating crappier code...
*/
template<template<typename> class Func, typename Iterator, typename End>
void tuple_for_each(Iterator i, End e) {
Func<decltype(*i)> f;
f(*i);
tuple_for_each<Func>(++i, e);//recursive call
}
//Base case: i==e, so do nothing
template<template<typename> class Func, typename End>
void tuple_for_each(End i, End e) {}
/*
* The templated function object we pass to the for-each. Tried to use a
* regular templated function, but I'm not sure that's allowed. Wrapping it up
* doesn't cost anything.
* Unfortunately, lambdas in C++11 aren't polymorphic, so we're stuck in 1998
* writing this stuff away from where we use it unless we use macros.
* Also: This is more powerful than it looks, because we can write template-
* specialisations for whatever classes we like. Super duper.
*/
template<typename T>
struct println {
void operator()(T& t) {
cout << t << endl;
}
};
int main()
{
auto t = make_tuple(1, 2.1, "three");
tuple_for_each<println>(begin(t), end(t));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment