Skip to content

Instantly share code, notes, and snippets.

@i-saint
Last active January 11, 2023 06:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save i-saint/34701714bf5954096c1f4fdcf88289e3 to your computer and use it in GitHub Desktop.
Save i-saint/34701714bf5954096c1f4fdcf88289e3 to your computer and use it in GitHub Desktop.
#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>
#include <array>
#include <vector>
#include <list>
template<class Iter>
struct range_holder
{
using iterator_type = Iter;
iterator_type begin, end;
bool next()
{
return ++begin != end;
}
};
template<class Iter>
inline range_holder<Iter> make_range_holder(Iter b, Iter e)
{
return { b, e };
}
template<typename ...T, size_t... I>
inline auto make_range_list_impl(std::tuple<T&...>& t, std::index_sequence<I...>)
{
return std::make_tuple(make_range_holder(std::begin(std::get<I>(t)), std::end(std::get<I>(t)))...);
}
template<typename ...T>
inline auto make_range_list(std::tuple<T&...>&& t)
{
return make_range_list_impl<T...>(t, std::make_index_sequence<sizeof...(T)>{});
}
// 全ての iterator が同じ型の場合こちら。こちらの方が早い
template<class Tuple>
struct iterate_across_mono
{
static constexpr size_t N = std::tuple_size_v<Tuple>;
using iter_type = typename std::tuple_element_t<0, Tuple>::iterator_type;
using next_state_type = void (iterate_across_mono::*)();
Tuple ranges;
std::array<next_state_type, N> next_state_funcs;
iter_type position, current_end;
int state = 0;
iterate_across_mono(Tuple&& c)
: ranges(std::move(c))
{
position = std::get<0>(ranges).begin;
current_end = std::get<0>(ranges).end;
init(std::make_index_sequence<N>{});
}
template<size_t... I>
void init(std::index_sequence<I...>)
{
next_state_funcs = { (&iterate_across_mono::do_next_state<I>)... };
}
template<size_t I>
void do_next_state()
{
if constexpr (I < N - 1) {
position = std::get<I + 1>(ranges).begin;
current_end = std::get<I + 1>(ranges).end;
}
}
auto& get()
{
return *position;
}
bool next()
{
if (++position == current_end) {
if (state < N) {
(this->*(next_state_funcs[state]))();
}
++state;
}
return state < N;
}
struct iterator
{
iterate_across_mono* host;
void next()
{
if (!host->next()) {
host = nullptr;
}
}
auto& operator*() { return host->get(); }
iterator& operator++() { next(); return *this; }
bool operator!=(const iterator& other) { return host != other.host; }
};
auto begin() { return iterator{ this }; }
auto end() { return iterator{ nullptr }; }
};
// 複数の型の iterator が混ざってる場合こちら
template<class Tuple>
struct iterate_across_hetero
{
static constexpr size_t N = std::tuple_size_v<Tuple>;
using value_type = std::decay_t<decltype(*std::declval<typename std::tuple_element_t<0, Tuple>::iterator_type>())>;
using get_type = value_type& (iterate_across_hetero::*)();
using next_type = bool (iterate_across_hetero::*)();
Tuple ranges;
std::array<get_type, N> get_funcs;
std::array<next_type, N> next_funcs;
int state = 0;
iterate_across_hetero(Tuple&& c)
: ranges(std::move(c))
{
init(std::make_index_sequence<N>{});
}
template<size_t... I>
void init(std::index_sequence<I...>)
{
get_funcs = { (&iterate_across_hetero::do_get<I>)... };
next_funcs = { (&iterate_across_hetero::do_next<I>)... };
}
template<size_t I>
value_type& do_get()
{
return *std::get<I>(ranges).begin;
}
template<size_t I>
bool do_next()
{
return std::get<I>(ranges).next();
}
auto& get()
{
return (this->*(get_funcs[state]))();
}
bool next()
{
if (!(this->*(next_funcs[state]))()) {
++state;
}
return state < N;
}
struct iterator
{
iterate_across_hetero* host;
void next()
{
if (!host->next()) {
host = nullptr;
}
}
auto& operator*() { return host->get(); }
iterator& operator++() { next(); return *this; }
bool operator!=(const iterator& other) { return host != other.host; }
};
auto begin() { return iterator{ this }; }
auto end() { return iterator{ nullptr }; }
};
template<class Tuple, size_t... I>
inline constexpr bool is_same_all_impl(std::index_sequence<I...>)
{
return ((std::is_same_v<std::tuple_element_t<0, Tuple>, std::tuple_element_t<I, Tuple>>) && ...);
}
template<class Tuple>
inline constexpr bool is_same_all()
{
return is_same_all_impl<Tuple>(std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
template<class... T>
inline auto iterate_across(T&&... ranges)
{
auto tmp = make_range_list(std::tie(std::forward<T>(ranges)...));
if constexpr (is_same_all<decltype(tmp)>()) {
printf("iterate_across_mono\n");
return iterate_across_mono<decltype(tmp)>{ std::move(tmp) };
}
else {
printf("iterate_across_hetero\n");
return iterate_across_hetero<decltype(tmp)>{ std::move(tmp) };
}
}
template<class Func, class... T>
inline void each_across(Func&& func, T&&... ranges)
{
([&]() { for(auto& v : ranges) { func(v); } }(), ...);
// or: ([&](auto& range) { for(auto& v : range) { func(v); } }(ranges), ...);
}
int main ()
{
{
std::vector v1{ 0, 1, 2 };
std::vector v2{ 3, 4, 5 };
std::vector v3{ 6, 7, 8 };
for (int& i : iterate_across(v1, v2, v3)) {
printf("%d ", i);
}
printf("\n");
each_across([](int& i){ printf("%d ", i); }, v1, v2, v3);
printf("\n");
}
{
int v1[3]{ 0, 1, 2 };
std::vector v2{ 3, 4, 5 };
std::list v3{ 6, 7, 8 };
for (int& i : iterate_across(v1, v2, v3)) {
printf("%d ", i);
}
printf("\n");
each_across([](int& i){ printf("%d ", i); }, v1, v2, v3);
printf("\n");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment