[core, ios, macos] Implement unique_any and remove linb::any
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <linb/any.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
using linb::any;
|
||||
using linb::any_cast;
|
||||
|
||||
} // namespace mbgl
|
||||
275
include/mbgl/util/unique_any.hpp
Normal file
275
include/mbgl/util/unique_any.hpp
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
186
test/util/unique_any.test.cpp
Normal file
186
test/util/unique_any.test.cpp
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user