Skip to content

Instantly share code, notes, and snippets.

@matzemathics
Created September 20, 2021 20:31
Show Gist options
  • Save matzemathics/560df696c8404270fd0e986403239904 to your computer and use it in GitHub Desktop.
Save matzemathics/560df696c8404270fd0e986403239904 to your computer and use it in GitHub Desktop.
#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