Created
June 24, 2012 12:00
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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