summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--cmake/core-files.cmake2
-rw-r--r--cmake/core.cmake1
-rw-r--r--cmake/filesource.cmake1
-rw-r--r--cmake/test-files.cmake1
-rw-r--r--cmake/test.cmake1
-rw-r--r--include/mbgl/style/layer.hpp4
-rw-r--r--include/mbgl/style/source.hpp4
-rw-r--r--include/mbgl/util/any.hpp10
-rw-r--r--include/mbgl/util/unique_any.hpp275
-rw-r--r--platform/darwin/src/MGLStyle.mm4
-rw-r--r--test/util/unique_any.test.cpp186
12 files changed, 469 insertions, 21 deletions
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 <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/any.hpp>
+#include <mbgl/util/unique_any.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/style/layer_type.hpp>
#include <mbgl/style/types.hpp>
@@ -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 <mbgl/util/noncopyable.hpp>
#include <mbgl/util/optional.hpp>
-#include <mbgl/util/any.hpp>
+#include <mbgl/util/unique_any.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/style/types.hpp>
@@ -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 <linb/any.hpp>
-
-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 <typeinfo>
+#include <type_traits>
+#include <stdexcept>
+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<T>`)
+ * 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<T>()` methods work similar to `std::any_cast<T>()` 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<int>(u2);
+ *
+ * unique_any u2;
+ * u2 = std::unique_ptr<int>(new int);
+ * std::unique_ptr<int> iPtr = any_cast<std::unique_ptr<int>>(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 ValueType,
+ typename _Vt = std::decay_t<ValueType>,
+ typename = std::enable_if_t<!std::is_same<_Vt, unique_any>::value> >
+ unique_any(ValueType&& value) {
+ create(std::forward<ValueType>(value));
+ }
+
+ ~unique_any() {
+ reset();
+ }
+
+ unique_any& operator=(unique_any&& rhs) {
+ unique_any(std::move(rhs)).swap(*this);
+ return *this;
+ }
+
+ template <class ValueType,
+ typename = std::enable_if_t<!std::is_same<std::decay_t<ValueType>, unique_any>::value> >
+ unique_any& operator=(ValueType&& rhs) {
+ unique_any(std::forward<ValueType>(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<void*>::value>;
+ Storage() = default;
+
+ void * dynamic { nullptr };
+ StackStorage stack;
+ };
+
+ template<typename T>
+ struct AllocateOnStack : std::integral_constant<bool,
+ sizeof(T) <= sizeof(Storage::stack)
+ && std::alignment_of<T>::value <= std::alignment_of<Storage::StackStorage>::value
+ && std::is_nothrow_move_constructible<T>::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 <typename ValueType>
+ 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<ValueType*>(s.dynamic);
+ }
+ s.dynamic = nullptr;
+ }
+
+ const std::type_info& type() override {
+ return typeid(ValueType);
+ }
+ };
+
+ template <typename ValueType>
+ struct VTableStack : public VTable {
+ void move(Storage&& src, Storage& dest) override {
+ auto srcValue = reinterpret_cast<ValueType&&>(src.stack);
+ new (static_cast<void*>(&dest.stack)) ValueType(std::move(srcValue));
+ srcValue.~ValueType();
+ }
+
+ void destroy(Storage& s) override {
+ reinterpret_cast<ValueType&>(s.stack).~ValueType();
+ }
+
+ const std::type_info& type() override {
+ return typeid(ValueType);
+ }
+ };
+
+ template <typename ValueType>
+ static VTable* vtableForType() {
+ using VTableType = std::conditional_t<AllocateOnStack<ValueType>::value, VTableStack<ValueType>, VTableHeap<ValueType> >;
+ static VTableType vtable;
+ return &vtable;
+ }
+
+ template <typename ValueType, typename _Vt>
+ std::enable_if_t<AllocateOnStack<_Vt>::value>
+ createStorage(ValueType&& value) {
+ new (static_cast<void*>(&storage.stack)) _Vt(std::forward<ValueType>(value));
+ }
+
+ template <typename ValueType, typename _Vt>
+ std::enable_if_t<!AllocateOnStack<_Vt>::value>
+ createStorage(ValueType&& value) {
+ storage.dynamic = static_cast<void*>(new _Vt(std::forward<ValueType>(value)));
+ }
+
+ template <typename ValueType>
+ void create(ValueType&& value) {
+ using _Vt = std::decay_t<ValueType>;
+ vtable = vtableForType<_Vt>();
+ createStorage<ValueType, _Vt>(std::forward<ValueType>(value));
+ }
+
+ VTable* vtable { nullptr };
+ Storage storage;
+
+protected:
+ template<class ValueType>
+ friend const ValueType* any_cast(const unique_any* operand) ;
+
+ template<class ValueType>
+ friend ValueType* any_cast(unique_any* operand) ;
+
+ template<typename ValueType, typename _Vt = std::decay_t<ValueType> >
+ ValueType* cast()
+ {
+ return reinterpret_cast<ValueType *>(
+ AllocateOnStack<_Vt>::value ? &storage.stack : storage.dynamic);
+ }
+};
+
+template<typename ValueType>
+inline const ValueType* any_cast(const unique_any* any)
+{
+ return any_cast<ValueType>(const_cast<unique_any *>(any));
+}
+
+template<typename ValueType>
+inline ValueType* any_cast(unique_any* any)
+{
+ if(any == nullptr || any->type() != typeid(ValueType))
+ return nullptr;
+ else
+ return any->cast<ValueType>();
+}
+
+template<typename ValueType, typename _Vt = std::decay_t<ValueType> >
+inline ValueType any_cast(const unique_any& any)
+{
+ static_assert(std::is_constructible<ValueType, const _Vt&>::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<ValueType>(*temp);
+}
+
+template<typename ValueType, typename _Vt = std::decay_t<ValueType> >
+inline ValueType any_cast(unique_any& any)
+{
+ static_assert(std::is_constructible<ValueType, const _Vt&>::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<ValueType>(*temp);
+}
+
+template<typename ValueType, typename _Vt = std::remove_cv_t<ValueType> >
+inline ValueType any_cast(unique_any&& any)
+{
+ auto temp = any_cast<_Vt>(&any);
+ if (temp == nullptr) {
+ throw bad_any_cast();
+ }
+ auto retValue = static_cast<ValueType>(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<SourceWrapper>(rawSource->peer).source) {
+ if (MGLSource *source = rawSource->peer.has_value() ? mbgl::util::any_cast<SourceWrapper>(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<LayerWrapper>(rawLayer->peer).layer) {
+ if (MGLStyleLayer *layer = rawLayer->peer.has_value() ? mbgl::util::any_cast<LayerWrapper>(&(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 <mbgl/test/util.hpp>
+
+#include <mbgl/util/unique_any.hpp>
+
+using namespace mbgl::util;
+
+class TestType {
+public:
+ TestType() : i1(0), i2(1) {
+ str[0] = 'a';
+ }
+
+ TestType(unique_any& p) : TestType() {
+ p = std::unique_ptr<TestType>(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<int>(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<int>(&i)));
+
+ auto iValue = any_cast<int>(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<float>(&f)));
+
+ const float fValue = any_cast<const float>(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<char>(&c)));
+
+ EXPECT_THROW(any_cast<float>(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<TestType>(&u1)));
+
+ //TestType should be moved into owning unique_any
+ EXPECT_EQ(any_cast<TestType>(&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<TestType>(&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<TestType>(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<TestType *>(&u1)));
+
+ //Only the pointer should be moved
+ TestType * t2 = *any_cast<TestType *>(&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<TestType *>(&u2);
+ EXPECT_EQ(t2->i1, 0);
+ delete t2;
+}
+
+
+TEST(UniqueAny, UniquePtr) {
+ auto t1 = std::make_unique<TestType>();
+ 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<TestType>));
+
+ EXPECT_TRUE(IsStackAllocated(u1, any_cast<std::unique_ptr<TestType>>(&u1)));
+
+ auto t2 = any_cast<std::unique_ptr<TestType> >(std::move(u1));
+ EXPECT_FALSE(u1.has_value());
+
+ unique_any u2;
+ TestType * t3 = new TestType();
+ u2 = std::unique_ptr<TestType>(t3);
+ EXPECT_TRUE(u2.has_value());
+ EXPECT_TRUE(any_cast<std::unique_ptr<TestType>>(&u2)->get() == t3);
+}
+
+TEST(UniqueAny, SharedPtr) {
+
+ std::shared_ptr<int> shared(new int(3));
+ std::weak_ptr<int> weak = shared;
+ unique_any u1 = 0;
+
+ EXPECT_THROW(any_cast<float>(u1), bad_any_cast);
+
+ EXPECT_EQ(weak.use_count(), 1);
+ unique_any u2 = shared;
+ EXPECT_EQ(weak.use_count(), 2);
+
+ EXPECT_EQ(any_cast<std::unique_ptr<int>>(&u1), nullptr);
+ EXPECT_FALSE(IsStackAllocated(u1, any_cast<std::shared_ptr<TestType>>(&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);
+}