Last active
February 12, 2019 09:00
-
-
Save cjxgm/83b08092411ede89ecb3 to your computer and use it in GitHub Desktop.
a simple variant implementation similar to boost::variant, with nil value support, written in C++14.
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
#pragma once | |
#include <type_traits> | |
#include <utility> // for std::forward | |
#include "utils.hh" | |
#include "constraints.hh" | |
#include "const-helper.macro.hh" | |
namespace meta | |
{ | |
template <class NIL, class ...MEMBERS> | |
struct aligned_union : non_transferable | |
{ | |
using self = aligned_union; | |
template <class T> | |
static constexpr auto nil_requirement = | |
std::is_nothrow_constructible<T>::value && | |
std::is_nothrow_destructible <T>::value; | |
using nil = std::decay_t<NIL>; | |
static_assert(nil_requirement<nil>, "NIL does not meet the nil-requirement"); | |
static constexpr auto alignment_value() | |
{ | |
return utils::alignment_of<nil, std::decay_t<MEMBERS>...>; | |
} | |
static constexpr auto size() | |
{ | |
return utils::max(sizeof(nil), sizeof(std::decay_t<MEMBERS>)...); | |
} | |
template <class T, class = utils::disable_if_base_of<self, T>> | |
aligned_union(T&& x) { construct<T>(std::forward<T>(x)); } | |
aligned_union() : aligned_union{nil{}} {} | |
template <class T, class ...TS> | |
void construct(TS&&... xs) | |
{ | |
using type = std::decay_t<T>; | |
try { | |
new (data) type(std::forward<TS>(xs)...); | |
} | |
catch (...) { | |
new (data) nil; | |
throw; | |
} | |
} | |
template <class T> | |
void destruct() { data.~T(); } | |
CONST_HELPER( | |
template <class T> | |
auto& as() CONST noexcept | |
{ | |
using type = utils::const_if<decltype(this), std::decay_t<T>>; | |
return *reinterpret_cast<type *>(data); | |
} | |
); | |
private: | |
alignas(alignment_value()) char data[size()]; | |
}; | |
} | |
#include "const-helper.undef.hh" | |
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
#define CONST_HELPER_IMPL_KP(X...) X // kill parenthesis | |
#define CONST_HELPER_IMPL(BEFORE, AFTER...) \ | |
CONST_HELPER_IMPL_KP BEFORE AFTER \ | |
CONST_HELPER_IMPL_KP BEFORE const AFTER | |
#define CONST_HELPER(CODE...) CONST_HELPER_IMPL((CODE) | |
#define CONST ), |
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
#undef CONST | |
#undef CONST_HELPER | |
#undef CONST_HELPER_IMPL | |
#undef CONST_HELPER_IMPL_KP |
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
#pragma once | |
namespace meta | |
{ | |
inline namespace constraints | |
{ | |
struct non_transferable | |
{ | |
non_transferable() = default; | |
non_transferable(non_transferable const&) = delete; | |
non_transferable(non_transferable &&) = delete; | |
non_transferable & operator = (non_transferable const&) = delete; | |
non_transferable & operator = (non_transferable &&) = delete; | |
}; | |
} | |
} |
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
/* http://en.cppreference.com/w/cpp/language/union | |
#include <iostream> | |
// S has one non-static data member (tag), three enumerator members, | |
// and three variant members (c, n, d) | |
struct S | |
{ | |
enum {CHAR, INT, DOUBLE} tag; | |
union { | |
char c; | |
int n; | |
double d; | |
}; | |
}; | |
void print_s(const S& s) | |
{ | |
switch(s.tag) | |
{ | |
case S::CHAR: std::cout << s.c << '\n'; break; | |
case S::INT: std::cout << s.n << '\n'; break; | |
case S::DOUBLE: std::cout << s.d << '\n'; break; | |
} | |
} | |
int main() | |
{ | |
S s = {S::CHAR, {'a'}}; | |
print_s(s); | |
s.tag = S::INT; s.n = 123; | |
print_s(s); | |
} | |
*/ | |
#include <iostream> | |
#include "variant.hh" | |
using S = meta::variant<char, int, double>; | |
void print_s(const S& s) | |
{ | |
s.visit([](auto& x) { std::cout << x << "\n"; }); | |
} | |
int main() | |
{ | |
S s{'a'}; | |
print_s(s); | |
s = 123; | |
print_s(s); | |
} |
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
#pragma once | |
#include <type_traits> | |
namespace meta | |
{ | |
namespace utils | |
{ | |
//------ left fold | |
// | |
// foldl(F, x, xs...) => left fold x:xs by F | |
// | |
namespace foldl_impl | |
{ | |
template <class F, class U> | |
constexpr auto foldl(F, U y) { return y; } | |
template <class F, class U, class T, class ...TS> | |
constexpr auto foldl(F f, U y, T x, TS... xs) { return foldl(f, f(y, x), xs...); } | |
} | |
using foldl_impl::foldl; | |
//------ max | |
// | |
// max(x, xs...) => maximum value of x:xs | |
// | |
namespace max_impl | |
{ | |
template <class T> | |
constexpr auto max(T a, T b) { return (a > b ? a : b); } | |
} | |
template <class T, class ...TS> | |
constexpr auto max(T x, TS ...xs) | |
{ | |
return foldl(max_impl::max<T>, x, xs...); | |
} | |
//------ alignment of | |
// | |
// alignment_of<TS...> => maximum alignment of TS | |
// | |
template <class ...TS> | |
constexpr auto alignment_of = max(alignof(TS)...); | |
//------ index of | |
// | |
// index_of<U, TS...> => index of U in TS | |
// | |
namespace index_of_impl | |
{ | |
using index_type = decltype(sizeof 0); // same as std::size_t according to the standard | |
template <index_type I, class U, class ...TS> | |
constexpr auto index_of = I; | |
template <index_type I, class U, class T, class ...TS> | |
constexpr auto index_of<I, U, T, TS...> = index_of<I+1, U, TS...>; | |
template <index_type I, class U, class ...TS> | |
constexpr auto index_of<I, U, U, TS...> = I; | |
template <index_type I, class ...TS> | |
constexpr auto check() | |
{ | |
static_assert(I < sizeof...(TS), "not one of the specified types"); | |
return I; | |
}; | |
template <class U, class ...TS> | |
constexpr auto checked = check<index_of<0, U, TS...>, TS...>(); | |
} | |
template <class U, class ...TS> | |
constexpr auto index_of = index_of_impl::checked< | |
std::decay_t<U>, | |
std::decay_t<TS>... | |
>; | |
using index_of_impl::index_type; | |
//------ const if | |
// | |
// const_if<U, T> => T | |
// const_if<U const*, T> => const T | |
// | |
namespace const_if_impl | |
{ | |
template <class U, class T> struct const_if { using type = T; }; | |
template <class U, class T> struct const_if<U const*, T> { using type = const T; }; | |
} | |
template <class U, class T> | |
using const_if = typename const_if_impl::const_if<U, T>::type; | |
//------ disable if base of | |
// | |
// disable_if_base_of<U, T> => nothing if U is same of T, or U is the base of T | |
// => void otherwise | |
// | |
template <class U, class T> | |
using disable_if_base_of = std::enable_if_t< | |
!std::is_base_of< | |
std::decay_t<U>, | |
std::decay_t<T> | |
>::value | |
>; | |
} | |
} | |
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.hh" | |
#include <iostream> | |
using std::cerr; | |
struct foo | |
{ | |
foo() { cerr << "ctor\n"; } | |
~foo() { cerr << "dtor\n"; } | |
foo(foo const&) { cerr << "copy ctor\n"; } | |
foo(foo &&) { cerr << "move ctor\n"; } | |
auto& operator = (foo const&) { cerr << "copy asgn\n"; return *this; } | |
auto& operator = (foo &&) { cerr << "move asgn\n"; return *this; } | |
}; | |
int main() | |
{ | |
using va = meta::variant<int, double, foo>; | |
va v(3); | |
foo f; | |
v = f; | |
v = std::move(f); | |
double x = 3.2; | |
v = x; | |
cerr << v.strict_get<double>() << "\n"; | |
v = foo{}; | |
va b = v; | |
va c = std::move(v); | |
cerr << v.strict_get<int>() << "\n"; | |
} | |
/* output: | |
ctor | |
copy ctor | |
move asgn | |
dtor | |
3.2 | |
ctor | |
move ctor | |
dtor | |
copy ctor | |
move ctor | |
terminate called after throwing an instance of 'meta::bad_cast' | |
*/ |
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
#pragma once | |
#include <utility> // for std::forward, std::move | |
#include "aligned-union.hh" | |
#include "utils.hh" | |
#include "const-helper.macro.hh" | |
namespace meta | |
{ | |
struct bad_cast {}; | |
template <class ...MEMBERS> | |
struct variant | |
{ | |
using self = variant; | |
template <class T, class = utils::disable_if_base_of<self, T>> | |
variant(T&& x) : ti{index_of<T>}, au{std::forward<T>(x)} {} | |
variant() : variant(nil{}) {} | |
variant(self const& x) { x.visit_with_nil<copy_constructor>({*this}); } | |
variant(self && x) { x.visit_with_nil<move_constructor>({*this}); } | |
~variant() { destruct(); } | |
template <class T, class ...TS> | |
void emplace(TS&&... xs) | |
{ | |
destruct(); | |
construct<T>(std::forward<TS>(xs)...); | |
} | |
template <class T> | |
auto is() const noexcept { return (index_of<T> == ti); } | |
operator bool () const { return !is<nil>(); } | |
CONST_HELPER( | |
template <class F> | |
decltype(auto) visit(F const& f={}) CONST | |
{ | |
if (!ti) throw bad_cast{}; | |
return visit<F, MEMBERS...>(f, ti-1); | |
} | |
); | |
CONST_HELPER( | |
template <class T> auto& strict_get() CONST | |
{ | |
if (is<T>()) return as<T>(); | |
throw bad_cast{}; | |
} | |
); | |
template <class T, class = utils::disable_if_base_of<self, T>> | |
auto& operator = (T&& x) | |
{ | |
if (is<T>()) as<T>() = std::forward<T>(x); | |
else emplace<T>(std::forward<T>(x)); | |
return *this; | |
} | |
auto& operator = (self x) | |
{ | |
destruct(); | |
x.visit<move_constructor>({*this}); | |
return *this; | |
} | |
private: | |
using type_index = utils::index_type; | |
struct nil {}; | |
type_index ti; | |
aligned_union<nil, MEMBERS...> au; | |
template <class T> | |
static constexpr auto index_of = utils::index_of<T, nil, MEMBERS...>; | |
CONST_HELPER( | |
template <class T> auto& as() CONST noexcept { return au.template as<T>(); } | |
); | |
CONST_HELPER( | |
template <class F, class T> | |
decltype(auto) visit(F const& f, type_index) CONST { return f(as<T>()); } | |
); | |
CONST_HELPER( | |
template <class F, class U, class T, class ...TS> | |
decltype(auto) visit(F const& f, type_index i) CONST | |
{ | |
if (i) return visit<F, T, TS...>(f, i-1); | |
return f(as<U>()); | |
} | |
); | |
CONST_HELPER( | |
template <class F> | |
decltype(auto) visit_with_nil(F const& f={}) CONST | |
{ | |
return visit<F, nil, MEMBERS...>(f, ti); | |
} | |
); | |
struct destructor | |
{ | |
template <class T> | |
void operator () (T& x) const { x.~T(); } | |
}; | |
void destruct() { visit_with_nil<destructor>(); } | |
template <class T, class ...TS> | |
void construct(TS&&... xs) | |
{ | |
au.template construct<T>(std::forward<TS>(xs)...); | |
ti = index_of<T>; | |
} | |
struct move_constructor | |
{ | |
variant& self; | |
move_constructor(variant& self) : self{self} {} | |
template <class T> | |
void operator () (T& x) const { self.construct<T>(std::move(x)); } | |
}; | |
struct copy_constructor | |
{ | |
variant& self; | |
copy_constructor(variant& self) : self{self} {} | |
template <class T> | |
void operator () (T const& x) const { self.construct<T>(x); } | |
}; | |
}; | |
} | |
#include "const-helper.undef.hh" | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment