Last active
December 16, 2015 22:09
-
-
Save Heimdell/5505403 to your computer and use it in GitHub Desktop.
This file contains hidden or 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.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 | |
; | |
} |
This file contains hidden or 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
#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