Skip to content

Instantly share code, notes, and snippets.

@Heimdell
Last active December 16, 2015 22:09
Show Gist options
  • Save Heimdell/5505403 to your computer and use it in GitHub Desktop.
Save Heimdell/5505403 to your computer and use it in GitHub Desktop.
#include "variant.h++"
#include <string>
#include <iostream>
using namespace std;
template <int N>
struct Loudy
{
Loudy() { cout << "created #" << N << endl; }
Loudy(const Loudy &) { cout << "copied #" << N << endl; }
~Loudy() { cout << "killed #" << N << endl; }
void shout() { cout << "Loudy #" << N << " is here!" << endl; }
};
int main() {
using First = Loudy<1>;
using Second = Loudy<2>;
using Third = Loudy<3>;
auto var = oneOf<First, Second, Third>(Second());
if (var.is<First>())
get<First>(var).shout();
if (var.is<Second>())
get<Second>(var).shout();
if (var.is<Third>())
get<Third>(var).shout();
cout << boolalpha
<< (bool) inside<int, double, int, string>::value
<< " but "
<< (bool) inside<int, double, float, string>::value
<< " "
<< sizeof(var)
<< endl
;
}
#ifndef VARIANT_H
#define VARIANT_H
#include <type_traits>
#include <typeinfo>
#include <stdexcept>
#include <iostream>
#include <functional>
using namespace std;
/* type equality */
template <class, class> struct equals;
template <class T, class U> struct equals { enum { value = false }; };
template <class T> struct equals<T, T> { enum { value = true }; };
/* check if first arg is a member of a typelist */
template <class, class...> struct inside;
template <class What>
struct inside<What> {
enum { value = false };
};
template <class What, class Head, class... Tail>
struct inside<What, Head, Tail...>
{
enum { value = equals<What, Head> ::value
|| inside<What, Tail...>::value };
};
/* sum of types (set disjunction) with max-size-accumulator
this template generates a coinduction over `Types...`
calculating max size of elements and also there are coinductions
in implementation of its methods, terminating or dying
at the cobase */
template <int size, class... Types> struct oneOf1;
/* exported template; accumulator is initialized */
template<class... Types> using oneOf = oneOf1<0, Types...>;
/* zero-variant case, cannot be instantiated in wild;
although, all real data is stored inside */
template<int size>
struct oneOf1<size> {
enum { mark = -1 };
int real_mark;
char buffer[size];
protected:
/* it is a "bottom-type" (uninhabited type) representation
so, nothing can be of that type - also terminates "recursion" */
template <class T>
bool is() { return false; }
oneOf1() { }
virtual ~oneOf1() { }
};
constexpr int max(int x, int y) { return x > y ? x : y; }
/* coinduction step: cutting Head and folding max size into the first
template arg */
template <int size, class Head, class... Tail>
using next_oneOf1
= oneOf1<
max(size, sizeof(Head)),
Tail...
>;
/* inhabitated case: there is at least 1 variant */
template <int size, class Head, class... Tail>
struct oneOf1<size, Head, Tail...>
/* oneOf<X, Y> essentially is a special case of oneOf<X, Y, Z> */
: public next_oneOf1<size, Head, Tail...> {
typedef next_oneOf1<size, Head, Tail...> super;
enum { mark = super::mark + 1 };
// when we are creating wrapper around owned type
oneOf1(const Head &h) {
super::real_mark = mark;
new (asCurrent()) Head(h);
}
// when we are creating wrapper around the type we don't own
template <class U>
oneOf1(const U &u)
/* maybe, the super knows? */
/* there is no such ctor in zero-variant case, so we are protected here */
: super(u) { }
// implicitly "recurrent" (because virtual) dtor
~oneOf1() override {
if (super::real_mark == mark)
asCurrent()->Head::~Head();
}
/* var.is<T>() checks that we are holding a value of type T */
template <class T>
bool is() {
return equals<T, Head>::value && mark == super::real_mark
|| super::template is<T>();
}
/* call a function over a value of owned type */
void bind(function<void(Head &h)> f) {
if (is<Head>())
f(*asCurrent());
}
/* type is not owned - descent the solution to supers */
template <class U>
void bind(function<void(U &u)> f) {
super::bind(f);
}
protected:
oneOf1() {}
private:
Head * asCurrent() {
return (Head *) super::buffer;
}
};
/* due to impossibility to make a partial spec of method template
the getter was made into a static function */
/* this getter retrieves reference to object of type T if holded;
ref-into-zero otherwise */
template <class T, class Variant>
T &get(Variant &var) {
T * result = nullptr;
var.bind(function<void(T &)> ([&result](T &t) { result = &t; }));
return *result;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment