From 3067b77c650f5dea0c00a7a92a9fc927e028c742 Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Thu, 16 Nov 2017 18:38:27 -0800 Subject: [core, ios, macos] Implement unique_any and remove linb::any --- CMakeLists.txt | 1 - cmake/core-files.cmake | 2 +- cmake/core.cmake | 1 - cmake/filesource.cmake | 1 - cmake/test-files.cmake | 1 + cmake/test.cmake | 1 - include/mbgl/style/layer.hpp | 4 +- include/mbgl/style/source.hpp | 4 +- include/mbgl/util/any.hpp | 10 -- include/mbgl/util/unique_any.hpp | 275 +++++++++++++++++++++++++++++++++++++++ platform/darwin/src/MGLStyle.mm | 4 +- test/util/unique_any.test.cpp | 186 ++++++++++++++++++++++++++ 12 files changed, 469 insertions(+), 21 deletions(-) delete mode 100644 include/mbgl/util/any.hpp create mode 100644 include/mbgl/util/unique_any.hpp create mode 100644 test/util/unique_any.test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f88252754..24ff7a989a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,6 @@ set_source_files_properties(src/mbgl/util/version.cpp PROPERTIES COMPILE_DEFINIT mason_use(geometry VERSION 0.9.2 HEADER_ONLY) mason_use(variant VERSION 1.1.4 HEADER_ONLY) -mason_use(any VERSION 8fef1e9 HEADER_ONLY) mason_use(unique_resource VERSION cba309e HEADER_ONLY) mason_use(rapidjson VERSION 1.1.0 HEADER_ONLY) mason_use(boost VERSION 1.62.0 HEADER_ONLY) diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 42c25c8ea9..d6c9493742 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -594,7 +594,6 @@ set(MBGL_CORE_FILES src/mbgl/tile/vector_tile_data.hpp # util - include/mbgl/util/any.hpp include/mbgl/util/async_request.hpp include/mbgl/util/async_task.hpp include/mbgl/util/char_array_buffer.hpp @@ -631,6 +630,7 @@ set(MBGL_CORE_FILES include/mbgl/util/timer.hpp include/mbgl/util/traits.hpp include/mbgl/util/type_list.hpp + include/mbgl/util/unique_any.hpp include/mbgl/util/unitbezier.hpp include/mbgl/util/util.hpp include/mbgl/util/variant.hpp diff --git a/cmake/core.cmake b/cmake/core.cmake index c4e711f558..e1001787fa 100644 --- a/cmake/core.cmake +++ b/cmake/core.cmake @@ -14,7 +14,6 @@ target_include_directories(mbgl-core target_add_mason_package(mbgl-core PUBLIC geometry) target_add_mason_package(mbgl-core PUBLIC variant) -target_add_mason_package(mbgl-core PUBLIC any) target_add_mason_package(mbgl-core PRIVATE unique_resource) target_add_mason_package(mbgl-core PRIVATE rapidjson) target_add_mason_package(mbgl-core PRIVATE boost) diff --git a/cmake/filesource.cmake b/cmake/filesource.cmake index bb1b4e8c05..6251224d44 100644 --- a/cmake/filesource.cmake +++ b/cmake/filesource.cmake @@ -26,7 +26,6 @@ add_library(mbgl-filesource STATIC target_add_mason_package(mbgl-filesource PUBLIC geometry) target_add_mason_package(mbgl-filesource PUBLIC variant) -target_add_mason_package(mbgl-filesource PUBLIC any) target_add_mason_package(mbgl-filesource PRIVATE rapidjson) target_add_mason_package(mbgl-filesource PRIVATE boost) target_add_mason_package(mbgl-filesource PRIVATE geojson) diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake index 43bc1210fd..273852602f 100644 --- a/cmake/test-files.cmake +++ b/cmake/test-files.cmake @@ -147,5 +147,6 @@ set(MBGL_TEST_FILES test/util/tile_cover.test.cpp test/util/timer.test.cpp test/util/token.test.cpp + test/util/unique_any.test.cpp test/util/url.test.cpp ) diff --git a/cmake/test.cmake b/cmake/test.cmake index c821d53316..ab498879e5 100644 --- a/cmake/test.cmake +++ b/cmake/test.cmake @@ -27,7 +27,6 @@ target_link_libraries(mbgl-test target_add_mason_package(mbgl-test PRIVATE geometry) target_add_mason_package(mbgl-test PRIVATE variant) -target_add_mason_package(mbgl-test PRIVATE any) target_add_mason_package(mbgl-test PRIVATE unique_resource) target_add_mason_package(mbgl-test PRIVATE rapidjson) target_add_mason_package(mbgl-test PRIVATE gtest) diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp index c6a3c0e735..eb2dbf830b 100644 --- a/include/mbgl/style/layer.hpp +++ b/include/mbgl/style/layer.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include #include @@ -126,7 +126,7 @@ public: // For use in SDK bindings, which store a reference to a platform-native peer // object here, so that separately-obtained references to this object share // identical platform-native peers. - any peer; + util::unique_any peer; }; } // namespace style diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp index cec9619451..0b6a6c72d9 100644 --- a/include/mbgl/style/source.hpp +++ b/include/mbgl/style/source.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -76,7 +76,7 @@ public: // For use in SDK bindings, which store a reference to a platform-native peer // object here, so that separately-obtained references to this object share // identical platform-native peers. - any peer; + util::unique_any peer; }; } // namespace style diff --git a/include/mbgl/util/any.hpp b/include/mbgl/util/any.hpp deleted file mode 100644 index eea64b188a..0000000000 --- a/include/mbgl/util/any.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -namespace mbgl { - -using linb::any; -using linb::any_cast; - -} // namespace mbgl diff --git a/include/mbgl/util/unique_any.hpp b/include/mbgl/util/unique_any.hpp new file mode 100644 index 0000000000..d488930a03 --- /dev/null +++ b/include/mbgl/util/unique_any.hpp @@ -0,0 +1,275 @@ +#pragma once + +#include +#include +#include +namespace mbgl { +namespace util { + +class bad_any_cast : public std::bad_cast { +public: + const char* what() const noexcept override { + return "bad any_cast<>()"; + } +}; +/** + * A variant of `std::any` for non-copyable types. + * + * Use `unique_any` for non-copyable types (e.g. `std::unique_ptr`) + * or to ensure that no copies are made of copyable types that are + * moved in. + * + * `uniqe_any` differs from `std::any` in that it does not support copy construction + * or copy assignment. It also does not require the contained type to be copy + * constructible. + * + * The `any_cast()` methods work similar to `std::any_cast()` except that + * non-copyable types may only be cast to references. + * + * Example usage: + * unique_any u1(3); + * auto u2 = unique_any(std::move(u1)); // u1 is moved from + * int i = any_cast(u2); + * + * unique_any u2; + * u2 = std::unique_ptr(new int); + * std::unique_ptr iPtr = any_cast>(std::move(u2)); + * + * Inspired by linb::any (https://github.com/thelink2012/any) and the + * libc++ implementation (https://github.com/llvm-mirror/libcxx). + */ +class unique_any final +{ +public: + unique_any() = default; + + //Copy constructor (deleted) + unique_any(const unique_any& rhs) = delete; + + unique_any(unique_any&& rhs) : vtable(rhs.vtable) { + if (vtable) { + vtable->move(std::move(rhs.storage), storage); + } + rhs.vtable = nullptr; + } + + // Constructs with a direct-initilizated object of type ValueType + template , + typename = std::enable_if_t::value> > + unique_any(ValueType&& value) { + create(std::forward(value)); + } + + ~unique_any() { + reset(); + } + + unique_any& operator=(unique_any&& rhs) { + unique_any(std::move(rhs)).swap(*this); + return *this; + } + + template , unique_any>::value> > + unique_any& operator=(ValueType&& rhs) { + unique_any(std::forward(rhs)).swap(*this); + return *this; + } + + void reset() { + if (vtable) { + vtable->destroy(storage); + vtable = nullptr; + } + } + + void swap(unique_any& rhs) { + if (this == &rhs) { + return; + } else { + unique_any tmp(std::move(rhs)); + rhs.vtable = vtable; + if (rhs.vtable) { + rhs.vtable->move(std::move(storage), rhs.storage); + } + vtable = tmp.vtable; + if (vtable) { + vtable->move(std::move(tmp.storage), storage); + } + } + } + + const std::type_info& type() const { + return !has_value()? typeid(void) : vtable->type(); + } + + bool has_value() const { + return vtable != nullptr; + } + +private: + + union Storage { + using StackStorage = std::aligned_storage_t<3*sizeof(void*), std::alignment_of::value>; + Storage() = default; + + void * dynamic { nullptr }; + StackStorage stack; + }; + + template + struct AllocateOnStack : std::integral_constant::value <= std::alignment_of::value + && std::is_nothrow_move_constructible::value> { + }; + + struct VTable { + virtual ~VTable() = default; + virtual void move(Storage&& src, Storage& dest) = 0; + virtual void destroy(Storage&) = 0; + virtual const std::type_info& type() = 0; + }; + + template + struct VTableHeap : public VTable { + void move(Storage&& src, Storage& dest) override { + destroy(dest); + dest.dynamic = src.dynamic; + } + + void destroy(Storage& s) override { + if (s.dynamic) { + delete reinterpret_cast(s.dynamic); + } + s.dynamic = nullptr; + } + + const std::type_info& type() override { + return typeid(ValueType); + } + }; + + template + struct VTableStack : public VTable { + void move(Storage&& src, Storage& dest) override { + auto srcValue = reinterpret_cast(src.stack); + new (static_cast(&dest.stack)) ValueType(std::move(srcValue)); + srcValue.~ValueType(); + } + + void destroy(Storage& s) override { + reinterpret_cast(s.stack).~ValueType(); + } + + const std::type_info& type() override { + return typeid(ValueType); + } + }; + + template + static VTable* vtableForType() { + using VTableType = std::conditional_t::value, VTableStack, VTableHeap >; + static VTableType vtable; + return &vtable; + } + + template + std::enable_if_t::value> + createStorage(ValueType&& value) { + new (static_cast(&storage.stack)) _Vt(std::forward(value)); + } + + template + std::enable_if_t::value> + createStorage(ValueType&& value) { + storage.dynamic = static_cast(new _Vt(std::forward(value))); + } + + template + void create(ValueType&& value) { + using _Vt = std::decay_t; + vtable = vtableForType<_Vt>(); + createStorage(std::forward(value)); + } + + VTable* vtable { nullptr }; + Storage storage; + +protected: + template + friend const ValueType* any_cast(const unique_any* operand) ; + + template + friend ValueType* any_cast(unique_any* operand) ; + + template > + ValueType* cast() + { + return reinterpret_cast( + AllocateOnStack<_Vt>::value ? &storage.stack : storage.dynamic); + } +}; + +template +inline const ValueType* any_cast(const unique_any* any) +{ + return any_cast(const_cast(any)); +} + +template +inline ValueType* any_cast(unique_any* any) +{ + if(any == nullptr || any->type() != typeid(ValueType)) + return nullptr; + else + return any->cast(); +} + +template > +inline ValueType any_cast(const unique_any& any) +{ + static_assert(std::is_constructible::value, + "any_cast type can't construct copy of contained object"); + auto temp = any_cast<_Vt>(&any); + if (temp == nullptr) { + throw bad_any_cast(); + } + return static_cast(*temp); +} + +template > +inline ValueType any_cast(unique_any& any) +{ + static_assert(std::is_constructible::value, + "any_cast type can't construct copy of contained object"); + auto temp = any_cast<_Vt>(&any); + if (temp == nullptr) { + throw bad_any_cast(); + } + return static_cast(*temp); +} + +template > +inline ValueType any_cast(unique_any&& any) +{ + auto temp = any_cast<_Vt>(&any); + if (temp == nullptr) { + throw bad_any_cast(); + } + auto retValue = static_cast(std::move(*temp)); + any.reset(); + return std::move(retValue); +} + +} // namespace util +} // namespace mbgl + +namespace std { + +inline void swap(mbgl::util::unique_any& lhs, mbgl::util::unique_any& rhs) { + lhs.swap(rhs); +} + +} // namespace std diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 244fb94ef9..71f2785a94 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -221,7 +221,7 @@ static NSURL *MGLStyleURL_trafficNight; } - (MGLSource *)sourceFromMBGLSource:(mbgl::style::Source *)rawSource { - if (MGLSource *source = rawSource->peer.empty() ? nil : mbgl::any_cast(rawSource->peer).source) { + if (MGLSource *source = rawSource->peer.has_value() ? mbgl::util::any_cast(rawSource->peer).source : nil) { return source; } @@ -383,7 +383,7 @@ static NSURL *MGLStyleURL_trafficNight; { NSParameterAssert(rawLayer); - if (MGLStyleLayer *layer = rawLayer->peer.empty() ? nil : mbgl::any_cast(rawLayer->peer).layer) { + if (MGLStyleLayer *layer = rawLayer->peer.has_value() ? mbgl::util::any_cast(&(rawLayer->peer))->layer : nil) { return layer; } diff --git a/test/util/unique_any.test.cpp b/test/util/unique_any.test.cpp new file mode 100644 index 0000000000..9357b9c0ec --- /dev/null +++ b/test/util/unique_any.test.cpp @@ -0,0 +1,186 @@ +#include + +#include + +using namespace mbgl::util; + +class TestType { +public: + TestType() : i1(0), i2(1) { + str[0] = 'a'; + } + + TestType(unique_any& p) : TestType() { + p = std::unique_ptr(this); + } + + //Detect moves + TestType(TestType&& t): i1(t.i1+1), i2(t.i2+2) { + str[0] = t.str[0]+1; + } + + int i1; + int i2; + char str[256]; +}; + +bool IsStackAllocated (const unique_any& a, const void* obj1) { + uintptr_t a_ptr = (uintptr_t)(&a); + uintptr_t obj = (uintptr_t)(obj1); + return (obj >= a_ptr && obj < a_ptr + sizeof(unique_any)); +}; + +TEST(UniqueAny, Empty) { + EXPECT_FALSE(unique_any().has_value()); + EXPECT_TRUE(unique_any().type() == typeid(void)); + EXPECT_THROW(any_cast(unique_any()), bad_any_cast); +} + +TEST(UniqueAny, BasicTypes) { + unique_any i = 3; + EXPECT_TRUE(i.has_value()); + EXPECT_TRUE(i.type() == typeid(int)); + EXPECT_TRUE(IsStackAllocated(i, any_cast(&i))); + + auto iValue = any_cast(i); + EXPECT_TRUE(iValue == 3); + + EXPECT_TRUE(unique_any(4).has_value()); + EXPECT_TRUE(unique_any(4).type() == typeid(int)); + + unique_any f = 6.2f; + EXPECT_TRUE(f.has_value()); + EXPECT_TRUE(f.type() == typeid(float)); + EXPECT_TRUE(IsStackAllocated(f, any_cast(&f))); + + const float fValue = any_cast(f); + EXPECT_TRUE(fValue == 6.2f); + + EXPECT_TRUE(unique_any(1.0f).has_value()); + EXPECT_TRUE(unique_any(1.0f).type() == typeid(float)); + + unique_any c = 'z'; + EXPECT_TRUE(c.has_value()); + EXPECT_TRUE(c.type() == typeid(char)); + EXPECT_TRUE(IsStackAllocated(c, any_cast(&c))); + + EXPECT_THROW(any_cast(c), bad_any_cast); + + EXPECT_TRUE(unique_any('4').has_value()); + EXPECT_TRUE(unique_any('4').type() == typeid(char)); +} + +TEST(UniqueAny, BasicTypes_Move) { + unique_any i = 3; + EXPECT_TRUE(i.has_value()); + EXPECT_TRUE(i.type() == typeid(int)); + + unique_any f = 6.2f; + EXPECT_TRUE(f.has_value()); + EXPECT_TRUE(f.type() == typeid(float)); + + f = std::move(i); + EXPECT_FALSE(i.has_value()); + EXPECT_TRUE(i.type() == typeid(void)); + + EXPECT_TRUE(f.has_value()); + EXPECT_TRUE(f.type() == typeid(int)); + +} + +TEST(UniqueAny, LargeType) { + TestType t1; + unique_any u1 = unique_any(std::move(t1)); + EXPECT_TRUE(u1.has_value()); + EXPECT_TRUE(u1.type() == typeid(TestType)); + EXPECT_FALSE(IsStackAllocated(u1, any_cast(&u1))); + + //TestType should be moved into owning unique_any + EXPECT_EQ(any_cast(&u1)->i1, 1); + + auto u2(std::move(u1)); + EXPECT_TRUE(u2.type() == typeid(TestType)); + EXPECT_TRUE(u1.type() == typeid(void)); + + //TestType should not be moved when owning unique_any is moved; + EXPECT_EQ(any_cast(&u2)->i1, 1); + + //TestType should be moved out of owning unique_any + // Note: two moves are involved in returning the moved value + // First out of the unique_any, and then in the return statement + auto t2 = any_cast(std::move(u2)); + EXPECT_EQ(t2.i1, 3); + EXPECT_TRUE(u2.type() == typeid(void)); +} + +TEST(UniqueAny, Pointer) { + auto t1 = new TestType(); + + auto u1 = unique_any(std::move(t1)); + EXPECT_TRUE(u1.has_value()); + EXPECT_TRUE(u1.type() == typeid(TestType *)); + EXPECT_TRUE(IsStackAllocated(u1, any_cast(&u1))); + + //Only the pointer should be moved + TestType * t2 = *any_cast(&u1); + EXPECT_EQ(t2->i1, 0); + + unique_any u2(4); + std::swap(u2, u1); + + EXPECT_TRUE(u1.has_value()); + EXPECT_TRUE(u1.type() == typeid(int)); + + EXPECT_TRUE(u2.has_value()); + EXPECT_TRUE(u2.type() == typeid(TestType *)); + + t2 = *any_cast(&u2); + EXPECT_EQ(t2->i1, 0); + delete t2; +} + + +TEST(UniqueAny, UniquePtr) { + auto t1 = std::make_unique(); + auto u1 = unique_any(std::move(t1)); + + EXPECT_EQ(t1.get(), nullptr); + EXPECT_TRUE(u1.has_value()); + EXPECT_TRUE(u1.type() == typeid(std::unique_ptr)); + + EXPECT_TRUE(IsStackAllocated(u1, any_cast>(&u1))); + + auto t2 = any_cast >(std::move(u1)); + EXPECT_FALSE(u1.has_value()); + + unique_any u2; + TestType * t3 = new TestType(); + u2 = std::unique_ptr(t3); + EXPECT_TRUE(u2.has_value()); + EXPECT_TRUE(any_cast>(&u2)->get() == t3); +} + +TEST(UniqueAny, SharedPtr) { + + std::shared_ptr shared(new int(3)); + std::weak_ptr weak = shared; + unique_any u1 = 0; + + EXPECT_THROW(any_cast(u1), bad_any_cast); + + EXPECT_EQ(weak.use_count(), 1); + unique_any u2 = shared; + EXPECT_EQ(weak.use_count(), 2); + + EXPECT_EQ(any_cast>(&u1), nullptr); + EXPECT_FALSE(IsStackAllocated(u1, any_cast>(&u1))); + + u1 = std::move(u2); + EXPECT_EQ(weak.use_count(), 2); + u2.swap(u1); + EXPECT_EQ(weak.use_count(), 2); + u2 = 0; + EXPECT_EQ(weak.use_count(), 1); + shared = nullptr; + EXPECT_EQ(weak.use_count(), 0); +} -- cgit v1.2.1