[core, ios, macos] Implement unique_any and remove linb::any

This commit is contained in:
Asheem Mamoowala
2017-11-16 18:38:27 -08:00
parent ba63d06cfc
commit 3067b77c65
12 changed files with 469 additions and 21 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -1,10 +0,0 @@
#pragma once
#include <linb/any.hpp>
namespace mbgl {
using linb::any;
using linb::any_cast;
} // namespace mbgl

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);
}