-
Jan Trávníček authoredJan Trávníček authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
variant.hpp 11.35 KiB
/**
* variant.hpp
*
* https://gist.github.com/tibordp/6909880
*
* Created on: Feb 28, 2014
* Author: Tibor Djurica Potpara
* Modified: Jan Travnicek
*/
#ifndef __VATIANT_HPP_
#define __VATIANT_HPP_
#include <iostream>
#include <utility>
#include <typeinfo>
#include <string>
#include <cstring>
#include <sstream>
#include "compare.hpp"
#include "utility.hpp"
#include "type_traits.hpp"
namespace ext {
template <size_t arg1, size_t ... others>
struct static_max;
template <size_t arg>
struct static_max<arg> {
static const size_t value = arg;
};
template <size_t arg1, size_t arg2, size_t ... others>
struct static_max<arg1, arg2, others...> {
static const size_t value = arg1 >= arg2 ? static_max<arg1, others...>::value :
static_max<arg2, others...>::value;
};
struct type_id_hash_code {
size_t m_hash_code;
type_id_hash_code ( size_t hash_code ) : m_hash_code ( hash_code ) {
}
friend inline bool operator == ( const type_id_hash_code & first, const type_id_hash_code & second ) {
return first.m_hash_code == second.m_hash_code;
}
friend inline bool operator != ( const type_id_hash_code & first, const type_id_hash_code & second ) {
return first.m_hash_code != second.m_hash_code;
}
friend inline bool operator < ( const type_id_hash_code & first, const type_id_hash_code & second ) {
return first.m_hash_code < second.m_hash_code;
}
friend inline bool operator <= ( const type_id_hash_code & first, const type_id_hash_code & second ) {
return first.m_hash_code <= second.m_hash_code;
}
friend inline bool operator > ( const type_id_hash_code & first, const type_id_hash_code & second ) {
return first.m_hash_code > second.m_hash_code;
}
friend inline bool operator >= ( const type_id_hash_code & first, const type_id_hash_code & second ) {
return first.m_hash_code >= second.m_hash_code;
}
};
template<typename... Ts>
struct variant_helper;
template<typename F, typename... Ts>
struct variant_helper<F, Ts...> {
inline static void destroy(type_id_hash_code id, void * data) {
if (id == typeid(F).hash_code())
reinterpret_cast<F*>(data)->~F();
else
variant_helper<Ts...>::destroy(id, data);
}
inline static void copy(type_id_hash_code old_t, const void * old_v, void * new_v) {
if (old_t == typeid(F).hash_code())
new (new_v) F(*reinterpret_cast<const F*>(old_v));
else
variant_helper<Ts...>::copy(old_t, old_v, new_v);
}
inline static void move(type_id_hash_code old_t, void * old_v, void * new_v) {
if (old_t == typeid(F).hash_code())
new (new_v) F(std::move(*reinterpret_cast<F*>(old_v)));
else
variant_helper<Ts...>::move(old_t, old_v, new_v);
}
inline static void print(std::ostream& out, type_id_hash_code id, const void* data) {
if (id == typeid(F).hash_code())
out << *reinterpret_cast<const F*>(data);
else
variant_helper<Ts...>::print(out, id, data);
}
inline static std::string string(type_id_hash_code id, const void* data) {
if (id == typeid(F).hash_code())
return ext::to_string ( *reinterpret_cast<const F*>(data) );
else
return variant_helper<Ts...>::string(id, data);
}
inline static int compareHelper(type_id_hash_code this_t, const void * this_v, type_id_hash_code other_t, const void * other_v) {
if (this_t == typeid(F).hash_code() && other_t == typeid(F).hash_code()) {
static ext::compare<typename std::decay < F >::type > comp;
return comp( *(reinterpret_cast<const F*>(this_v)), *(reinterpret_cast<const F*>(other_v)));
} else
return variant_helper<Ts...>::compareHelper(this_t, this_v, other_t, other_v);
}
};
template<typename... Ts>
struct variant_helper<void, Ts...> {
inline static void destroy(type_id_hash_code id, void * data) {
if (id != typeid(void).hash_code())
variant_helper<Ts...>::destroy(id, data);
}
inline static void copy(type_id_hash_code old_t, const void * old_v, void * new_v) {
if (old_t != typeid(void).hash_code())
variant_helper<Ts...>::copy(old_t, old_v, new_v);
}
inline static void move(type_id_hash_code old_t, void * old_v, void * new_v) {
if (old_t != typeid(void).hash_code())
variant_helper<Ts...>::move(old_t, old_v, new_v);
}
inline static void print(std::ostream& out, type_id_hash_code id, const void* data) {
if (id == typeid(void).hash_code())
out << "void";
else
variant_helper<Ts...>::print(out, id, data);
}
inline static std::string string(type_id_hash_code id, const void* data) {
if (id == typeid(void).hash_code())
return "void";
else
return variant_helper<Ts...>::string(id, data);
}
inline static int compareHelper(type_id_hash_code this_t, const void * this_v, type_id_hash_code other_t, const void * other_v) {
if (this_t == typeid(void).hash_code() && other_t == typeid(void).hash_code())
return 0;
else
return variant_helper<Ts...>::compareHelper(this_t, this_v, other_t, other_v);
}
};
template<> struct variant_helper<> {
inline static void destroy(type_id_hash_code, void *) { }
inline static void copy(type_id_hash_code, const void *, void *) { }
inline static void move(type_id_hash_code, void *, void *) { }
inline static void print(std::ostream&, type_id_hash_code, const void *) {}
inline static std::string string(type_id_hash_code, const void *) { return ""; }
inline static int compareHelper(type_id_hash_code, const void *, type_id_hash_code, const void *) { return 0; }
};
template<typename ST, typename AT, typename... Ts>
class variant_base;
template<typename ST, typename AT>
class variant_base<ST, AT> {
static const size_t data_size = ST::value;
static const size_t data_align = AT::value;
using data_t = typename std::aligned_storage<data_size, data_align>::type;
protected:
type_id_hash_code type_id;
data_t data;
variant_base( type_id_hash_code id ) : type_id ( id ) {
// just to make the -Werror=maybe-uninitialized go away
std::memset( & data, 0, data_size );
}
};
template<typename ST, typename AT, typename F, typename... Ts>
class variant_base<ST, AT, F, Ts...> : public variant_base<ST, AT, Ts...> {
using variant_base<ST, AT, Ts...>::variant_base;
protected:
variant_base( type_id_hash_code id ) : variant_base<ST, AT, Ts...>::variant_base ( id ) { }
public:
variant_base(const F& value) : variant_base<ST, AT, Ts...>::variant_base ( type_id_hash_code ( typeid(F).hash_code() ) ) { new (&this->data) F(value); }
variant_base(F&& value) : variant_base<ST, AT, Ts...>::variant_base ( type_id_hash_code ( typeid(F).hash_code() ) ) { new (&this->data) F(std::move(value)); }
variant_base() : variant_base<ST, AT, Ts ... >::variant_base ( ) { }
};
template<typename ST, typename AT, typename... Ts>
class variant_base<ST, AT, void, Ts...> : public variant_base<ST, AT, Ts...> {
using variant_base<ST, AT, Ts...>::variant_base;
protected:
variant_base( type_id_hash_code id ) : variant_base<ST, AT, Ts...>::variant_base ( id ) { }
public:
variant_base() : variant_base<ST, AT, Ts...>::variant_base ( type_id_hash_code ( typeid(void).hash_code() ) ) { }
};
template<typename ... Ts>
class variant : public variant_base<static_max<SizeOf<Ts>::size...>, static_max<AlignOf<Ts>::align...>, Ts...> {
using helper_t = variant_helper<Ts...>;
public:
using variant_base<static_max<SizeOf<Ts>::size...>, static_max<AlignOf<Ts>::align...>, Ts...>::variant_base;
template < typename = std::enable_if < std::is_in<void, Ts...>::value > >
variant ( ) : variant_base<static_max<SizeOf<Ts>::size...>, static_max<AlignOf<Ts>::align...>, Ts...> ( ) {
}
//copy consructor
variant(const variant<Ts...>& old) : variant_base<static_max<SizeOf<Ts>::size...>, static_max<AlignOf<Ts>::align...>, Ts...> ( old.type_id ) {
helper_t::copy(old.type_id, &old.data, &this->data);
}
//move constructor
variant(variant<Ts...>&& old) noexcept : variant_base<static_max<SizeOf<Ts>::size...>, static_max<AlignOf<Ts>::align...>, Ts...> ( old.type_id ) {
helper_t::move(old.type_id, &old.data, &this->data);
}
// copy assignment operator
variant<Ts...>& operator =( const variant<Ts...> & other ) {
return this->operator =( variant <Ts...> ( other ) );
}
// move assignment operator
variant<Ts...>& operator= (variant<Ts...> && old) noexcept {
using std::swap;
swap(this->type_id, old.type_id);
swap(this->data, old.data);
return *this;
}
bool operator== (const variant<Ts...>& other) const {
return compare ( other ) == 0;
}
bool operator!= (const variant<Ts...>& other) const {
return !(*this == other);
}
bool operator< (const variant<Ts...>& other) const {
return compare ( other ) < 0;
}
bool operator> (const variant<Ts...>& other) const {
return other < *this;
}
bool operator<= (const variant<Ts...>& other) const {
return !(*this > other);
}
bool operator>= (const variant<Ts...>& other) const {
return !(*this < other);
}
int compare(const variant<Ts...>& other) const {
if (this->type_id < other.type_id) return -1;
if (this->type_id > other.type_id) return 1;
return helper_t::compareHelper(this->type_id, &this->data, other.type_id, &other.data);
}
template<typename T, typename = std::enable_if < std::is_in<T, Ts...>::value > >
bool is() const {
return (this->type_id == typeid(T).hash_code());
}
template<typename = std::enable_if < std::is_in<void, Ts...>::value > >
void set ( ) {
helper_t::destroy(this->type_id, &this->data);
this->type_id = typeid(void).hash_code();
}
template<typename T, typename = std::enable_if < std::is_in<T, Ts...>::value && ! std::is_same < void, T >::value > >
void set(T&& value) {
helper_t::destroy(this->type_id, &this->data);
new (&this->data) T(value);
this->type_id = typeid(T).hash_code();
}
template<typename T, typename = std::enable_if < std::is_in<T, Ts...>::value && ! std::is_same < void, T >::value > >
void set(const T& value) {
helper_t::destroy(this->type_id, &this->data);
new (&this->data) T(std::move(value));
this->type_id = typeid(T).hash_code();
}
template<typename T, typename = std::enable_if < std::is_in<T, Ts...>::value && ! std::is_same < void, T >::value > >
T& get() {
// It is a dynamic_cast-like behaviour
if (this->type_id == typeid(T).hash_code())
return *reinterpret_cast<T*>(&this->data);
else
throw std::bad_cast();
}
template<typename T, typename = std::enable_if < std::is_in<T, Ts...>::value && ! std::is_same < void, T >::value > >
const T& get() const {
// It is a dynamic_cast-like behaviour
if (this->type_id == typeid(T).hash_code())
return *reinterpret_cast<const T*>(&this->data);
else
throw std::bad_cast();
}
~variant() noexcept {
helper_t::destroy(this->type_id, &this->data);
}
friend std::ostream& operator <<(std::ostream& out, const variant<Ts...>& obj) {
helper_t::print(out, obj.type_id, &obj.data);
return out;
}
explicit operator std::string() const {
return helper_t::string(this->type_id, &this->data);
}
template < typename T >
static typename std::enable_if < ! std::is_same < void, T >::value, variant < Ts ... > >::type from ( ) {
return variant < Ts ... >( T { } );
}
template < typename T >
static typename std::enable_if < std::is_same < void, T >::value, variant < Ts ... > >::type from ( ) {
return variant < Ts ... >( );
}
};
template<typename ... Ts>
struct compare<ext::variant<Ts...>> {
int operator()(const ext::variant<Ts...>& first, const ext::variant<Ts...>& second) const {
return first.compare(second);
}
};
template < class ... Ts >
std::string to_string ( const ext::variant < Ts ... > & value ) {
std::stringstream ss;
ss << value;
return ss.str();
}
} /* namespace ext */
#endif /* __VARIANT_HPP_ */