Skip to content

Instantly share code, notes, and snippets.

@jamboree
Last active August 29, 2015 14:20
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 jamboree/5a5797e8869168cc64d9 to your computer and use it in GitHub Desktop.
Save jamboree/5a5797e8869168cc64d9 to your computer and use it in GitHub Desktop.
#include <iostream>
#include <tuple>
#include <vector>
namespace variant_sequence_detail
{
struct span
{
int which;
std::size_t length;
};
using span_list = std::vector<span>;
template<int N, class... T>
struct match
{
static void get();
};
template<int N, class T, class... Ts>
struct match<N, T, Ts...> : match<N + 1, Ts...>
{
using match<N + 1, Ts...>::get;
static std::integral_constant<int, N> get(T)
{
return {};
}
};
template<class F, int N>
void entry_fn(F& f, std::size_t length)
{
f(std::integral_constant<int, N>{}, length);
}
template<class F, std::size_t... N>
inline void visit_span(span const& s, F& f, std::integer_sequence<std::size_t, N...>)
{
using fn_ptr = void(*)(F&, std::size_t);
static fn_ptr vtable[sizeof...(N)] = {&entry_fn<F, N>...};
vtable[s.which](f, s.length);
}
template<class Seqs, std::size_t... N>
inline auto get_begins(Seqs& seqs, std::integer_sequence<std::size_t, N...>)
{
return std::make_tuple(std::get<N>(seqs).begin()...);
}
template<class Seq, class F>
inline bool for_each_seq(Seq& seq, F& f)
{
for (auto& e : seq)
f(e);
return true;
}
template<class Seqs, class F, std::size_t... N>
inline void for_each_unordered(Seqs& seqs, F& f, std::integer_sequence<std::size_t, N...>)
{
std::initializer_list<bool>
{
for_each_seq(std::get<N>(seqs), f)...
};
}
template<class Seqs, class F>
inline void for_each(span_list const& spans, Seqs& seqs, F& f)
{
std::make_index_sequence<std::tuple_size<Seqs>::value> expand;
auto visitor([&f, begins(get_begins(seqs, expand))](auto idx, std::size_t length) mutable
{
auto it = std::get<decltype(idx)::value>(begins);
auto end = it + length;
for (; it != end; ++it)
f(*it);
std::get<decltype(idx)::value>(begins) = end;
});
for (auto const& span : spans)
{
visit_span(span, visitor, expand);
}
}
}
template<class... T>
struct variant_sequence
{
std::tuple<std::vector<T>...> _seqs;
variant_sequence_detail::span_list _spans;
template<class U>
void push_back(U u)
{
using variant_sequence_detail::match;
constexpr int idx = decltype(match<0, T...>::get(std::declval<U>()))::value;
std::get<idx>(_seqs).push_back(std::forward<U>(u));
if (!_spans.empty())
{
auto& last = _spans.back();
if (last.which == idx)
{
++last.length;
return;
}
}
_spans.push_back({idx, 1});
}
template<class F>
void for_each(F&& f)
{
variant_sequence_detail::for_each(_spans, _seqs, f);
}
template<class F>
void for_each(F&& f) const
{
variant_sequence_detail::for_each(_spans, _seqs, f);
}
template<class F>
void for_each_unordered(F&& f)
{
std::make_index_sequence<sizeof...(T)> expand;
variant_sequence_detail::for_each_unordered(_seqs, f, expand);
}
template<class F>
void for_each_unordered(F&& f) const
{
std::make_index_sequence<sizeof...(T)> expand;
variant_sequence_detail::for_each_unordered(_seqs, f, expand);
}
};
int main(int argc, char *argv[])
{
variant_sequence<int, char const*> seq;
seq.push_back(1);
seq.push_back(2);
seq.push_back(3);
seq.push_back("four");
seq.push_back("five");
seq.push_back(6);
seq.push_back(7);
seq.for_each([](auto e)
{
std::cout << e << "\n";
});
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment