Skip to content

Instantly share code, notes, and snippets.

@MatthewSteel
Created June 24, 2012 12:00
Show Gist options
  • Save MatthewSteel/2982976 to your computer and use it in GitHub Desktop.
Save MatthewSteel/2982976 to your computer and use it in GitHub Desktop.
Almost proper for-each algorithm for tuples in C++11. Works with the STL, everything type-checked, sadly uses heap data and inheritance, homogeneous data only.
#include <tuple>
#include <iostream>
#include <type_traits>
#include <memory>
#include <stdexcept>
#include <algorithm>
/*
* Before beginning: This is so stupid. Turning homogeneous tuples into STL-
* compatible containers using inheritance... No part of this is a good idea.
* The data is sitting in obvious places, almost certainly in contiguous
* storage... Ugh.
* Because this is such a bad idea I'm not going to make any effort to make
* it efficient. Hooray, laziness!
*/
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;
};
/*
* Base class, provides the special template iterators with a common interface.
* We need to do this because the iterator pointing to position 1 is of a
* different type to the iterator pointing to position 2. We need to do this
* because *it1 calls get<1>() and *it2 calls get<2>(). Counting on the memory
* layout of the tuple is probably non-portable.
* We can't give these to the STL because they're pure virtual - just an
* interface.
*/
template<typename T>
class IteratorBase {
public:
bool operator==(const IteratorBase<T> &i) const {
return objaddr() == i.objaddr();
}
virtual unique_ptr<IteratorBase> operator++() = 0;
virtual T& operator*() = 0;
virtual const T* objaddr() const = 0;//Essentially &**this
};
/*
* The real iterator types hidden by the above interface. A regular one for
* most of the tuple and a partial specialisation for the end iterator.
*/
template<typename Tuple, typename n=IntType<0>>
class RealIterator : public IteratorBase<typename tuple_element<n::get, Tuple>::type> {
typedef typename tuple_element<n::get, Tuple>::type T;
public:
RealIterator(Tuple &t) : t(t) {}
unique_ptr<IteratorBase<T>> operator++() {
return unique_ptr<IteratorBase<T>>(new RealIterator<Tuple, IntType<n::get+1>>(t));
}
T& operator*() {
return get<n::get>(t);
}
const T* objaddr() const {
return &get<n::get>(t);
}
tuple<void*, int> description() const {
return make_tuple(reinterpret_cast<void*>(&t), n::get);
}
private:
Tuple &t;
};
template<typename Tuple>
class RealIterator<Tuple, IntType<tuple_size<Tuple>::value>> : public IteratorBase<typename tuple_element<tuple_size<Tuple>::value-1, Tuple>::type> {
typedef typename tuple_element<tuple_size<Tuple>::value-1, Tuple>::type T;
public:
RealIterator(Tuple &t) : t(t) {}
unique_ptr<IteratorBase<T>> operator++() {
return unique_ptr<IteratorBase<T>>(this);
}
T& operator*() {
throw out_of_range("Tuple");
}
const T* objaddr() const {
return nullptr;
}
tuple<void*, int> description() const {
return make_tuple(reinterpret_cast<void*>(&t), tuple_size<Tuple>::value);
}
private:
Tuple &t;
};
/*
* A simple holder for the real iterators, something the STL can use without
* leaking memory all over the place or having to worry about implementation
* details.
*/
template<typename T>
class STLIterator {
public:
STLIterator(IteratorBase<T> *it) : it(it) {}
STLIterator<T>& operator++() {
it = ++(*it);
return *this;
}
bool operator==(const STLIterator<T> &i) const {
return *it == *(i.it);
}
bool operator!=(const STLIterator<T> &i) const {
return !(*this==i);
}
T &operator*() {
return **it;
}
private:
unique_ptr<IteratorBase<T>> it;
};
/*
* begin/end iterators for algorithms. Don't seem to work for range-based for
* loops because I don't understand ADL.
*/
template<typename Tuple>
STLIterator<typename tuple_element<0, Tuple>::type> begin(Tuple &t) {
return STLIterator<typename tuple_element<0, Tuple>::type>(
new RealIterator<Tuple>(t)
);
}
template<typename Tuple>
STLIterator<typename tuple_element<0, Tuple>::type> end(Tuple &t) {
return STLIterator<typename tuple_element<0, Tuple>::type>(
new RealIterator<Tuple, IntType<tuple_size<Tuple>::value>>(t)
);
}
int main()
{
auto t = make_tuple(1, 2, 3);
for_each(begin(t), end(t), [](int i) {
cout << i << endl;
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment