From 93a0316607fad144d9edd26ac6e47b520f6a41d3 Mon Sep 17 00:00:00 2001 From: crypto-ape <43807588+crypto-ape@users.noreply.github.com> Date: Thu, 25 Oct 2018 12:46:00 +0200 Subject: [PATCH 1/3] Dynamic memory allocation of static_variant & other small changes. --- CMakeLists.txt | 1 + include/fc/static_variant.hpp | 134 +++++++++++++++++++++------------- src/static_variant.cpp | 31 ++++++++ 3 files changed, 115 insertions(+), 51 deletions(-) create mode 100644 src/static_variant.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a07b213..7d05f1e 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 1b2628b..6c0ef5b 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 * @@ -176,6 +176,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 @@ -213,40 +248,57 @@ class static_variant { 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(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(0); } template @@ -254,6 +306,7 @@ public: { cpy.visit( impl::copy_construct(*this) ); } + static_variant( const static_variant& cpy ) { cpy.visit( impl::copy_construct(*this) ); @@ -264,40 +317,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; } @@ -310,52 +355,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 @@ -394,9 +427,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(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..97c69d9 --- /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 +{ + 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; +} + +}} From 384d4f14c444fef5ddd9ef57c3df1b6833b9f1c0 Mon Sep 17 00:00:00 2001 From: crypto-ape <43807588+crypto-ape@users.noreply.github.com> Date: Thu, 25 Oct 2018 12:46:30 +0200 Subject: [PATCH 2/3] Replaced assert with FC_ASSERT --- src/static_variant.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static_variant.cpp b/src/static_variant.cpp index 97c69d9..63cbb8f 100644 --- a/src/static_variant.cpp +++ b/src/static_variant.cpp @@ -12,7 +12,7 @@ dynamic_storage::~dynamic_storage() void* dynamic_storage::data() const { - assert( storage != nullptr ); + FC_ASSERT( storage != nullptr ); return (void*)storage; } From f8b86fc75755f0687f725232587b4cdbd158e4f4 Mon Sep 17 00:00:00 2001 From: crypto-ape <43807588+crypto-ape@users.noreply.github.com> Date: Mon, 29 Oct 2018 18:10:34 +0100 Subject: [PATCH 3/3] added testcase types_edge_cases_test for static_variant --- include/fc/static_variant.hpp | 7 ++--- tests/variant_test.cpp | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/include/fc/static_variant.hpp b/include/fc/static_variant.hpp index 6c0ef5b..17c4453 100644 --- a/include/fc/static_variant.hpp +++ b/include/fc/static_variant.hpp @@ -245,6 +245,7 @@ std::vector> init_co 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."); @@ -269,7 +270,7 @@ class static_variant { new(storage.data()) X( std::move(x) ); } - void init(tag_type tag) + void init_from_tag(tag_type tag) { FC_ASSERT( tag >= 0 ); FC_ASSERT( tag < count() ); @@ -298,7 +299,7 @@ public: static_variant() { - init(0); + init_from_tag(0); } template @@ -428,7 +429,7 @@ public: FC_ASSERT( w >= 0 ); FC_ASSERT( w < count() ); clean(); - init(w); + init_from_tag(w); } tag_type which() const {return _tag;} 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 {