Skip to content
Snippets Groups Projects
variant.hpp 11.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jan Trávníček's avatar
    Jan Trávníček committed
    /**
    
    Jan Trávníček's avatar
    Jan Trávníček committed
     * variant.hpp
     *
    
     * https://gist.github.com/tibordp/6909880
     *
     * Created on: Feb 28, 2014
     * Author: Tibor Djurica Potpara
     * Modified: Jan Travnicek
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    #ifndef __VATIANT_HPP_
    #define __VATIANT_HPP_
    
    #include <iostream>
    #include <utility>
    #include <typeinfo>
    #include <string>
    #include <cstring>
    #include <sstream>
    
    #include "compare.hpp"
    #include "type_traits.hpp"
    
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    namespace std {
    
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    template <size_t arg1, size_t ... others>
    struct static_max;
    
    template <size_t arg>
    
    struct static_max<arg> {
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    	static const size_t value = arg;
    };
    
    template <size_t arg1, size_t arg2, size_t ... others>
    
    struct static_max<arg1, arg2, others...> {
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    	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;
    	}
    
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    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) {
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    		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) {
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    		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(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 std::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 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(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);
    	}
    };
    
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    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(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;
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    	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;
    
    
    	variant_base( type_id_hash_code id ) : variant_base<ST, AT, Ts...>::variant_base ( id ) { }
    
    	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 ) { }
    
    	variant_base() : variant_base<ST, AT, Ts...>::variant_base ( type_id_hash_code ( typeid(void).hash_code() ) ) { }
    
    };
    
    template<class T>
    struct SizeOf {
    	static const size_t size = sizeof(T);
    };
    
    template<>
    struct SizeOf<void> {
    
    	static const size_t size = 1;
    
    };
    
    template<class T>
    struct AlignOf {
    	static const size_t align = alignof(T);
    };
    
    template<>
    struct AlignOf<void> {
    	static const size_t align = 1;
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    template<typename ... Ts>
    
    class variant : public variant_base<static_max<SizeOf<Ts>::size...>, static_max<AlignOf<Ts>::align...>, Ts...> {
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    	using helper_t = variant_helper<Ts...>;
    
    	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...> ( ) {
    	}
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    
    	//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);
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    	}
    
    	//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 {
    
    		std::swap(this->type_id, old.type_id);
    		std::swap(this->data, old.data);
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    
    		return *this;
    	}
    
    
    	bool operator== (const variant<Ts...>& other) const {
    
    		return compare ( other ) == 0;
    
    	bool operator!= (const variant<Ts...>& other) const {
    
    	bool operator< (const variant<Ts...>& other) const {
    
    		return compare ( other ) < 0;
    
    	bool operator> (const variant<Ts...>& other) const {
    
    	bool operator<= (const variant<Ts...>& other) const {
    
    	bool operator>= (const variant<Ts...>& other) const {
    
    	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 > >
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    	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() {
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    		// It is a dynamic_cast-like behaviour
    
    		if (this->type_id == typeid(T).hash_code())
    			return *reinterpret_cast<T*>(&this->data);
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    		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 {
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    		// It is a dynamic_cast-like behaviour
    
    		if (this->type_id == typeid(T).hash_code())
    			return *reinterpret_cast<const T*>(&this->data);
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    		else
    			throw std::bad_cast();
    	}
    
    
    	~variant() noexcept {
    
    		helper_t::destroy(this->type_id, &this->data);
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    	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<variant<Ts...>> {
    
    	int operator()(const variant<Ts...>& first, const variant<Ts...>& second) const {
    
    		return first.compare(second);
    	}
    };
    
    
    template < class ... Ts  >
    string to_string ( const std::variant < Ts ... > & value ) {
    	std::stringstream ss;
    	ss << value;
    	return ss.str();
    }
    
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    } /* namespace std */
    
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    #endif /* __VARIANT_HPP_ */