diff --git a/CMakeLists.txt b/CMakeLists.txt index b7873ba..593231a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,6 +210,7 @@ set( fc_sources src/variant.cpp src/exception.cpp src/variant_object.cpp + src/static_variant.cpp src/thread/thread.cpp src/thread/thread_specific.cpp src/thread/future.cpp diff --git a/include/fc/static_variant.hpp b/include/fc/static_variant.hpp index f5227af..1e024bb 100644 --- a/include/fc/static_variant.hpp +++ b/include/fc/static_variant.hpp @@ -1,4 +1,4 @@ -/** This source adapted from https://github.com/kmicklas/variadic-static_variant +/** This source adapted from https://github.com/kmicklas/variadic-static_variant. Now available at https://github.com/kmicklas/variadic-variant. * * Copyright (C) 2013 Kenneth Micklas * @@ -178,6 +178,41 @@ struct type_info<> { static const size_t size = 0; }; +template +size_t size( TTag ) +{ + return 0; +} + +template +size_t size( TTag tag ) +{ + if (tag <= 0) + { + return sizeof(A); + } + + return size( --tag ); +} + + +class dynamic_storage +{ + char* storage; +public: + dynamic_storage(); + + ~dynamic_storage(); + + void* data() const; + + void alloc( size_t size ); + + void release(); +}; + + + } // namespace impl template @@ -218,43 +253,61 @@ static const fc::array template class static_variant { +protected: static_assert(impl::type_info::no_reference_types, "Reference types are not permitted in static_variant."); static_assert(impl::type_info::no_duplicates, "static_variant type arguments contain duplicate types."); + template + using type_in_typelist = typename std::enable_if::pos != -1, X>::type; // type is in typelist of static_variant. + using tag_type = int64_t; tag_type _tag; - char storage[impl::type_info::size]; + impl::dynamic_storage storage; - template + template> void init(const X& x) { _tag = impl::position::pos; - new(storage) X(x); + storage.alloc( sizeof(X) ); + new(storage.data()) X(x); } - template + template> void init(X&& x) { _tag = impl::position::pos; - new(storage) X( std::move(x) ); + storage.alloc( sizeof(X) ); + new(storage.data()) X( std::move(x) ); } + void init_from_tag(tag_type tag) + { + FC_ASSERT( tag >= 0 ); + FC_ASSERT( tag < count() ); + _tag = tag; + storage.alloc( impl::size( tag ) ); + impl::storage_ops<0, Types...>::con(_tag, storage.data()); + } + + void clean() + { + impl::storage_ops<0, Types...>::del(_tag, storage.data() ); + storage.release(); + } + + template friend struct impl::copy_construct; template friend struct impl::move_construct; public: - template + template> struct tag { - static_assert( - impl::position::pos != -1, - "Type not in static_variant." - ); static const int value = impl::position::pos; }; + static_variant() { - _tag = 0; - impl::storage_ops<0, Types...>::con(0, storage); + init_from_tag(0); } template @@ -262,6 +315,7 @@ public: { cpy.visit( impl::copy_construct(*this) ); } + static_variant( const static_variant& cpy ) { cpy.visit( impl::copy_construct(*this) ); @@ -272,40 +326,32 @@ public: mv.visit( impl::move_construct(*this) ); } - template + template> static_variant(const X& v) { - static_assert( - impl::position::pos != -1, - "Type not in static_variant." - ); init(v); } + ~static_variant() { - impl::storage_ops<0, Types...>::del(_tag, storage); + clean(); } - - template + template> static_variant& operator=(const X& v) { - static_assert( - impl::position::pos != -1, - "Type not in static_variant." - ); - this->~static_variant(); + clean(); init(v); return *this; } static_variant& operator=( const static_variant& v ) { if( this == &v ) return *this; - this->~static_variant(); + clean(); v.visit( impl::copy_construct(*this) ); return *this; } static_variant& operator=( static_variant&& v ) { if( this == &v ) return *this; - this->~static_variant(); + clean(); v.visit( impl::move_construct(*this) ); return *this; } @@ -318,52 +364,40 @@ public: return a.which() < b.which(); } - template + template> X& get() { - static_assert( - impl::position::pos != -1, - "Type not in static_variant." - ); if(_tag == impl::position::pos) { - void* tmp(storage); - return *reinterpret_cast(tmp); + return *reinterpret_cast(storage.data()); } else { FC_THROW_EXCEPTION( fc::assert_exception, "static_variant does not contain a value of type ${t}", ("t",fc::get_typename::name()) ); - // std::string("static_variant does not contain value of type ") + typeid(X).name() - // ); } } - template + template> const X& get() const { - static_assert( - impl::position::pos != -1, - "Type not in static_variant." - ); if(_tag == impl::position::pos) { - const void* tmp(storage); - return *reinterpret_cast(tmp); + return *reinterpret_cast(storage.data()); } else { FC_THROW_EXCEPTION( fc::assert_exception, "static_variant does not contain a value of type ${t}", ("t",fc::get_typename::name()) ); } } template typename visitor::result_type visit(visitor& v) { - return visit( _tag, v, (void*) storage ); + return visit( _tag, v, (void*) storage.data() ); } template typename visitor::result_type visit(const visitor& v) { - return visit( _tag, v, (void*) storage ); + return visit( _tag, v, (void*) storage.data() ); } template typename visitor::result_type visit(visitor& v)const { - return visit( _tag, v, (const void*) storage ); + return visit( _tag, v, (const void*) storage.data() ); } template typename visitor::result_type visit(const visitor& v)const { - return visit( _tag, v, (const void*) storage ); + return visit( _tag, v, (const void*) storage.data() ); } template @@ -402,9 +436,8 @@ public: void set_which( tag_type w ) { FC_ASSERT( w >= 0 ); FC_ASSERT( w < count() ); - this->~static_variant(); - _tag = w; - impl::storage_ops<0, Types...>::con(_tag, storage); + clean(); + init_from_tag(w); } tag_type which() const {return _tag;} diff --git a/src/static_variant.cpp b/src/static_variant.cpp new file mode 100644 index 0000000..63cbb8f --- /dev/null +++ b/src/static_variant.cpp @@ -0,0 +1,31 @@ +#include + + +namespace fc { namespace impl { + +dynamic_storage::dynamic_storage() : storage(nullptr) {}; + +dynamic_storage::~dynamic_storage() +{ + release(); +} + +void* dynamic_storage::data() const +{ + FC_ASSERT( storage != nullptr ); + return (void*)storage; +} + +void dynamic_storage::alloc( size_t size ) +{ + release(); + storage = new char[size]; +} + +void dynamic_storage::release() +{ + delete [] storage; + storage = nullptr; +} + +}} diff --git a/tests/variant_test.cpp b/tests/variant_test.cpp index 4a66ede..12c66bc 100644 --- a/tests/variant_test.cpp +++ b/tests/variant_test.cpp @@ -46,6 +46,55 @@ FC_REFLECT( fc::test::item, (level)(w) ); BOOST_AUTO_TEST_SUITE(fc_variant_and_log) +BOOST_AUTO_TEST_CASE( types_edge_cases_test ) +{ + using namespace fc::test; + + class sv : public fc::static_variant<> + { + public: + BOOST_ATTRIBUTE_UNUSED typedef fc::static_variant<>::tag_type tag_type; + }; + + BOOST_TEST_MESSAGE( "========== Test empty static_variant ==========" ); + + BOOST_CHECK_THROW( fc::static_variant<>(), fc::assert_exception ); + + BOOST_TEST_MESSAGE( "========== Test static_variant with tag_type ==========" ); + + sv::tag_type init_value = 2; + fc::static_variant< sv::tag_type, std::string, fc::variant > variant_with_tagtype(init_value); + + BOOST_CHECK_EQUAL( variant_with_tagtype.count(), 3 ); + BOOST_CHECK_EQUAL( variant_with_tagtype.which(), 0 ); + + sv::tag_type current_value = variant_with_tagtype.get(); + BOOST_CHECK_EQUAL( current_value, init_value ); + BOOST_CHECK( variant_with_tagtype == init_value ); + + for (sv::tag_type i = variant_with_tagtype.count(); i-->0;) + { + variant_with_tagtype.set_which(i); + BOOST_CHECK_EQUAL(variant_with_tagtype.which(), i); + } + + BOOST_TEST_MESSAGE( "========== Test static_variant with static_variant ==========" ); + + using sv_double = fc::static_variant; + using sv_float = fc::static_variant; + fc::static_variant< sv_float, std::string, sv_double > variant; + sv_float variant_float = 1.5f; + variant = variant_float; + BOOST_CHECK_EQUAL( variant.which(), 0 ); + BOOST_CHECK_EQUAL( variant.get().get(), 1.5f ); + + sv_double variant_double = 1.0; + variant = variant_double; + BOOST_CHECK_EQUAL( variant.which() , 2); + BOOST_CHECK_EQUAL( variant.get().get(), 1.0 ); +} + + BOOST_AUTO_TEST_CASE( nested_objects_test ) { try {