Last active
August 29, 2015 14:10
-
-
Save Trass3r/4b9ef3be73877de61fa0 to your computer and use it in GitHub Desktop.
fun with range templates
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 <type_traits> | |
//! range spanned by 2 iterators | |
template <typename Iterator> | |
struct IteratorRange | |
{ | |
IteratorRange(Iterator begin, Iterator end) | |
: _begin(std::move(begin)), | |
_end(std::move(end)) | |
{ | |
} | |
Iterator begin() const | |
{ | |
return _begin; | |
} | |
Iterator end() const | |
{ | |
return _end; | |
} | |
private: | |
Iterator _begin; | |
Iterator _end; | |
}; | |
//! create an iterator range | |
template <typename Iterator> | |
inline IteratorRange<Iterator> make_range(Iterator begin, Iterator end) | |
{ | |
return IteratorRange<Iterator>(std::move(begin), std::move(end)); | |
} | |
//! stupid adapter for range-based for | |
template <typename Range> | |
struct RangeIterator | |
{ | |
Range& r; | |
RangeIterator(Range& r) | |
: r(r) | |
{ | |
} | |
void operator++() | |
{ | |
r.advance(); | |
} | |
bool operator!=(const RangeIterator& /*that*/) const | |
{ | |
return !r.empty(); | |
} | |
auto operator*() const -> decltype(r.current()) | |
{ | |
return r.current(); | |
} | |
}; | |
//! auto-deref if it is a pointer | |
template <typename T> | |
auto dereference(T&& t) -> decltype(std::forward<T>(t)) | |
{ | |
return std::forward<T>(t); | |
} | |
template <typename T> | |
auto dereference(T* t) -> decltype(*t) | |
{ | |
return *t; | |
} | |
//! basic range with custom interface | |
template <typename ContainerType> | |
struct Range | |
{ | |
using ItType = decltype(std::declval<ContainerType>().begin()); | |
using value_type = typename ContainerType::value_type; | |
protected: | |
ItType it, endit; | |
public: | |
Range() = default; | |
Range(ContainerType& container) | |
: it(container.begin()), | |
endit(container.end()) | |
{ | |
} | |
RangeIterator<Range> begin() { return RangeIterator<Range>(*this); } | |
RangeIterator<Range> end() { return RangeIterator<Range>(*this); } | |
bool empty() const { return it == endit; } | |
auto current() const -> decltype(*it) { return *it; } | |
void advance() { ++it; } | |
}; | |
//! flatten nested ranges into 1 range | |
// Terminator is the desired target element type used to stop the recursion | |
template <typename ContainerType, typename Terminator> | |
struct FlattenedRange : Range<ContainerType> | |
{ | |
using Base = Range<ContainerType>; | |
using SubRange = FlattenedRange<std::remove_pointer_t<typename ContainerType::value_type>, Terminator>; | |
using value_type = typename SubRange::value_type; | |
private: | |
SubRange innerIt; | |
public: | |
FlattenedRange() = default; | |
FlattenedRange(ContainerType& container) | |
: Base(container) | |
{ | |
skipEmpties(); | |
} | |
using Base::empty; | |
auto current() const -> decltype(innerIt.current()) { return innerIt.current(); } | |
void advance() | |
{ | |
if (!innerIt.empty()) | |
innerIt.advance(); | |
if (innerIt.empty()) | |
{ | |
++this->it; | |
skipEmpties(); | |
} | |
} | |
private: | |
void skipEmpties() | |
{ | |
while (!empty()) | |
{ | |
innerIt = SubRange(dereference(*this->it)); | |
if (!innerIt.empty()) | |
break; | |
++this->it; | |
} | |
} | |
}; | |
//! recursion base case: container with desired Terminator element type | |
template <typename ContainerType> | |
struct FlattenedRange <ContainerType, typename ContainerType::value_type> : Range<ContainerType> | |
{ | |
FlattenedRange() = default; | |
FlattenedRange(ContainerType& container) | |
: Range<ContainerType>(container) | |
{} | |
}; | |
//! convenience method | |
template <typename Terminator, typename T> | |
FlattenedRange<std::remove_reference_t<T>, Terminator> makeFlattenedRange(T&& container) | |
{ | |
static_assert(!std::is_pointer<T>::value, "no pointers allowed"); | |
return FlattenedRange<std::remove_reference_t<T>, Terminator>(std::forward<T>(container)); | |
} | |
#include <vector> | |
struct Bar | |
{ | |
}; | |
struct Foo | |
{ | |
Foo() = default; | |
std::vector<Bar*> v = std::vector<Bar*>{new Bar, new Bar, new Bar}; | |
using value_type = Bar*; | |
using iterator = std::vector<Bar*>::iterator; | |
using const_iterator = std::vector<Bar*>::const_iterator; | |
const_iterator begin() const { return v.begin(); } | |
const_iterator end() const { return v.end(); } | |
}; | |
int main() | |
{ | |
std::vector<int> v{1}; | |
const std::vector<Foo*> v2 = {new Foo, new Foo}; | |
std::vector<Foo*> sv(1, nullptr); | |
const std::vector<Foo*> csv(1, nullptr); | |
std::vector<const Foo*> svc(1, nullptr); | |
const std::vector<const Foo*> csvc(1, nullptr); | |
printf("Result: "); | |
for (Bar* el : makeFlattenedRange<Bar*>(v2)) | |
{ | |
printf("%x, ", el); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment