Created
September 20, 2021 20:31
-
-
Save matzemathics/560df696c8404270fd0e986403239904 to your computer and use it in GitHub Desktop.
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 <variant> | |
#include <vector> | |
#include <iterator> | |
#include <algorithm> | |
#include <type_traits> | |
#include <cassert> | |
#include <string> | |
#include <iostream> | |
struct VariantListNotConvertible : std::bad_variant_access | |
{ | |
}; | |
template <class Variant, class T> | |
class View | |
{ | |
const std::vector<Variant> &inner; | |
public: | |
explicit View(const std::vector<Variant> &_inner) : inner(_inner) | |
{ | |
} | |
class iterator | |
{ | |
typename std::vector<Variant>::const_iterator current; | |
typename std::vector<Variant>::const_iterator end; | |
public: | |
explicit iterator( | |
typename std::vector<Variant>::const_iterator _current, | |
typename std::vector<Variant>::const_iterator _end) | |
: current(_current), end(_end) | |
{ | |
while (current != end) | |
{ | |
if (std::holds_alternative<T>(*current)) | |
break; | |
current++; | |
} | |
} | |
using difference_type = std::ptrdiff_t; | |
using value_type = const T &; | |
using pointer = const T *; | |
using reference = const T &; | |
using iterator_category = std::input_iterator_tag; | |
iterator &operator++() | |
{ | |
do | |
{ | |
current++; | |
if (std::holds_alternative<T>(*current)) | |
break; | |
} while (current != end); | |
return *this; | |
} | |
iterator operator++(int) | |
{ | |
auto ret_value = *this; | |
++(*this); | |
return ret_value; | |
} | |
bool operator==(iterator other) const { return current == other.current; } | |
bool operator!=(iterator other) const { return !(*this == other); } | |
iterator::reference operator*() const { return std::get<T>(*current); } | |
}; | |
iterator begin() const { return iterator(inner.begin(), inner.end()); } | |
iterator end() const { return iterator(inner.end(), inner.end()); } | |
}; | |
template <class... Ts> | |
class VariantList; | |
namespace impl | |
{ | |
template <class Variant, class... Ts> | |
class VariantListImpl; | |
template <class Variant> | |
struct VariantListImpl<Variant> | |
{ | |
template <class T> | |
static View<Variant, T> get_all(); | |
template <class... Us> | |
static void copy_from(std::vector<Variant> &_ref, VariantList<Us...> &orig) | |
{ | |
if (!orig.items.empty()) | |
{ | |
throw VariantListNotConvertible(); | |
} | |
} | |
template <class... Us> | |
static void copy_from(std::vector<Variant> &_ref, VariantList<Us...> &&orig) | |
{ | |
if (!orig.items.empty()) | |
{ | |
throw VariantListNotConvertible(); | |
} | |
} | |
}; | |
template <class Variant, class T, class... Ts> | |
struct VariantListImpl<Variant, T, Ts...> | |
{ | |
template <class U> | |
static View<Variant, U> get_all(const std::vector<Variant> &ref) | |
{ | |
if constexpr (std::is_same_v<U, T>) | |
return View<Variant, T>(ref); | |
else | |
return VariantListImpl<Variant, Ts...>::template get_all<U>(); | |
} | |
template <class... Us> | |
static void copy_from(std::vector<Variant> &ref, const VariantList<Us...> &orig) | |
{ | |
auto left = VariantList<Us...>(); | |
for (auto item : orig.items) | |
{ | |
if (std::holds_alternative<T>(item)) | |
{ | |
ref.push_back(std::get<T>(item)); | |
} | |
else | |
{ | |
left.push_back(item); | |
} | |
} | |
if (!left.isEmpty()) | |
{ | |
VariantListImpl<Variant, Ts...>::copy_from(ref, std::move(left)); | |
} | |
} | |
template <class... Us> | |
static void copy_from(std::vector<Variant> &ref, VariantList<Us...> &&orig) | |
{ | |
for (auto it = orig.items.begin(); it != orig.items.end();) | |
{ | |
if (std::holds_alternative<T>(*it)) | |
{ | |
ref.push_back(std::move(std::get<T>(*it))); | |
it = orig.items.erase(it); | |
} | |
else | |
++it; | |
} | |
if (!orig.isEmpty()) | |
{ | |
VariantListImpl<Variant, Ts...>::copy_from(ref, orig); | |
} | |
} | |
}; | |
} | |
template <class... Ts> | |
class VariantList | |
{ | |
using values = std::variant<Ts...>; | |
static_assert(sizeof...(Ts) > 0); | |
std::vector<values> items; | |
template <class Variant, class... Us> | |
friend class impl::VariantListImpl; | |
public: | |
void push_back(const values &item) { items.push_back(item); } | |
void push_back(values &&item) { items.push_back(item); } | |
bool isEmpty() const { return items.empty(); } | |
int size() const { return items.size(); } | |
template <class T> | |
View<values, T> get_all() | |
{ | |
return impl::VariantListImpl<values, Ts...>::template get_all<T>(items); | |
} | |
std::vector<values> toVector() const { return items; } | |
VariantList<Ts...> &operator<<(const values &item) | |
{ | |
items.push_back(item); | |
return *this; | |
} | |
VariantList<Ts...> &operator<<(values &&item) | |
{ | |
items.push_back(item); | |
return *this; | |
} | |
template <class... Us> | |
VariantList<Ts...> &operator<<(const VariantList<Us...> &orig) | |
{ | |
impl::VariantListImpl<values, Ts...>::copy_from(items, orig); | |
return *this; | |
} | |
template <class... Us> | |
VariantList<Ts...> &operator<<(VariantList<Us...> &&orig) | |
{ | |
impl::VariantListImpl<values, Ts...>::copy_from(items, orig); | |
return *this; | |
} | |
}; | |
template <class... Ts> | |
class SerializableOverview; | |
template <> | |
class SerializableOverview<> | |
{ | |
}; | |
template <class... Ts> | |
class SerializableOverview : SerializableOverview<> | |
{ | |
VariantList<Ts...> children; | |
}; | |
int main() | |
{ | |
auto test = VariantList<int, std::string>() << 1 << 2 << "test" << 5; | |
for (auto i : test.get_all<int>()) | |
std::cout << i << std::endl; | |
auto test2 = VariantList<int, std::string>() << test; | |
for (auto i : test2.get_all<int>()) | |
std::cout << i << std::endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment