Skip to content

Instantly share code, notes, and snippets.

@hotwatermorning
Created January 23, 2018 09:01
Show Gist options
  • Select an option

  • Save hotwatermorning/c593e1d82b836e61a11385aa68844d3d to your computer and use it in GitHub Desktop.

Select an option

Save hotwatermorning/c593e1d82b836e61a11385aa68844d3d to your computer and use it in GitHub Desktop.
permutation_range
#include <cassert>
#include <utility>
#include <tuple>
#include <functional>
#include <iostream>
#include <vector>
#include <array>
#include <list>
#include <memory>
template<bool UseConst = false>
struct Op
{
template<class U>
static auto begin(U&& u) { return std::begin(u); }
template<class U>
static auto end(U&& u) { return std::end(u); }
template<class U>
static auto begin(std::reference_wrapper<U> u) { return std::begin(u.get()); }
template<class U>
static auto end(std::reference_wrapper<U> u) { return std::end(u.get()); }
};
template<>
struct Op<true>
{
template<class U>
static auto begin(U const& u) { return std::begin(u); }
template<class U>
static auto end(U const& u) { return std::end(u); }
template<class U>
static auto begin(std::reference_wrapper<U> const u) { return std::begin(u.get()); }
template<class U>
static auto end(std::reference_wrapper<U> const u) { return std::end(u.get()); }
};
template<class... T>
struct permutation_range
{
private:
template<int N>
struct Int
{
static int const value = N;
};
template<class U>
static auto wrap(U&& u)
{
return std::forward<U>(u);
}
template<class U>
static auto wrap(U& u)
{
return std::ref(u);
}
template<class U>
static auto wrap(const U& u)
{
return std::cref<U>(u);
}
using range_set = std::tuple<decltype(wrap(std::declval<T>()))...>;
using this_type = permutation_range<T...>;
public:
template<class OwnerType, bool UseConst>
struct iterator_base;
permutation_range(T&&... ranges)
: ranges_(wrap(std::forward<T>(ranges))...)
{}
using iterator = iterator_base<this_type, false>;
using const_iterator = iterator_base<typename std::add_const<this_type>::type, true>;
auto begin()
{
if(is_valid()) {
return iterator(*this, init_with_begin{});
} else {
return end();
}
}
auto begin() const
{
if(is_valid()) {
return const_iterator(*this, init_with_begin{});
} else {
return end();
}
}
auto end() { return iterator(*this); }
auto end() const { return const_iterator(*this); }
const_iterator cbegin() const { return begin(); }
const_iterator cend() const { return end(); }
struct init_with_begin {};
template<class OwnerType, bool UseConst>
struct iterator_base
{
using this_type = iterator_base<OwnerType, UseConst>;
using owner_type = OwnerType;
using op = Op<UseConst>;
using indices = std::index_sequence_for<T...>;
using iterator_set = std::tuple<decltype(op::begin(std::declval<T>()))...>;
// initialize with ends
iterator_base(OwnerType& owner)
: owner_(&owner)
, iters_(get_end_impl(owner, indices{}))
{}
iterator_base(OwnerType& owner, init_with_begin)
: owner_(&owner)
, iters_(get_begin_impl(owner, indices{}))
{}
decltype(auto) operator*()
{
assert(!is_end());
return dereference(indices{});
}
this_type & operator++()
{
assert(!is_end());
next(Int<sizeof...(T)-1>());
return *this;
}
this_type operator++(int)
{
auto ret = *this;
next();
return *ret;
}
bool operator==(this_type const &rhs)
{
assert(owner_ == rhs.owner_);
return
(is_end() && rhs.is_end())
||
(iters_ == rhs.iters_);
}
bool operator!=(this_type const &rhs)
{
return !(*this == rhs);
}
private:
template<class Ix, Ix... I>
auto get_begin_impl(owner_type& owner, std::integer_sequence<Ix, I...>)
{
return std::make_tuple(op::begin(std::get<I>(owner.ranges_))...);
}
template<class Ix, Ix... I>
auto get_end_impl(owner_type& owner, std::integer_sequence<Ix, I...>)
{
return std::make_tuple(op::end(std::get<I>(owner.ranges_))...);
}
template <class Ix, Ix... I>
auto dereference(std::integer_sequence<Ix, I...>)
{
return std::make_tuple(wrap(*std::get<I>(iters_))...);
}
bool is_end() const
{
return std::get<0>(iters_) == op::end(std::get<0>(owner_->ranges_));
}
template<int N>
void next(Int<N>)
{
auto &iter = std::get<N>(iters_);
auto &range = std::get<N>(owner_->ranges_);
++iter;
if(iter == op::end(range)) {
iter = op::begin(range);
next(Int<N-1>());
}
}
void next(Int<-1>)
{
std::get<0>(iters_) = op::end(std::get<0>(owner_->ranges_));
}
private:
owner_type *owner_;
iterator_set iters_;
};
private:
bool is_valid() const
{
return is_valid(Int<sizeof...(T)-1>());
}
template<int N>
bool is_valid(Int<N>) const
{
auto &range = std::get<N>(ranges_);
return (Op<true>::begin(range) != Op<true>::end(range)) && is_valid(Int<N-1>());
}
bool is_valid(Int<-1>) const { return true; }
range_set ranges_;
};
template<class... Range>
auto make_permutation_range(Range&&... range)
{
using result_type
= permutation_range<decltype(std::forward<Range>(range))...>;
return result_type(std::forward<Range>(range)...);
}
int main()
{
std::vector<int> x = { 1, 2, 3 };
std::list<std::string> y = { "Alice", "Bob", "Charlie" };
double z[] = { 3.14159, 1.41421, 2.71828 };
std::vector<std::unique_ptr<int>> ps;
ps.push_back(std::make_unique<int>(10));
ps.push_back(std::make_unique<int>(20));
auto ranges = make_permutation_range(x, std::move(y), z, std::move(ps));
auto begin = ranges.begin();
auto end = ranges.end();
for(auto it = begin; it != end; ++it) {
auto v = *it;
std::cout
<< std::get<0>(v) << ", "
<< std::get<1>(v) << ", "
<< std::get<2>(v) << ", "
<< *std::get<3>(v) << ", "
<< std::endl;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment