[core] Replace unique_any with peer from mapbox-bindgen
This commit is contained in:
@@ -679,6 +679,7 @@ set(MBGL_CORE_FILES
|
||||
include/mbgl/util/logging.hpp
|
||||
include/mbgl/util/noncopyable.hpp
|
||||
include/mbgl/util/optional.hpp
|
||||
include/mbgl/util/peer.hpp
|
||||
include/mbgl/util/platform.hpp
|
||||
include/mbgl/util/premultiply.hpp
|
||||
include/mbgl/util/projection.hpp
|
||||
@@ -692,7 +693,6 @@ set(MBGL_CORE_FILES
|
||||
include/mbgl/util/traits.hpp
|
||||
include/mbgl/util/tuple.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
|
||||
|
||||
@@ -137,6 +137,7 @@ set(MBGL_TEST_FILES
|
||||
test/util/merge_lines.test.cpp
|
||||
test/util/number_conversions.test.cpp
|
||||
test/util/offscreen_texture.test.cpp
|
||||
test/util/peer.test.cpp
|
||||
test/util/position.test.cpp
|
||||
test/util/projection.test.cpp
|
||||
test/util/run_loop.test.cpp
|
||||
@@ -147,7 +148,6 @@ set(MBGL_TEST_FILES
|
||||
test/util/tile_range.test.cpp
|
||||
test/util/timer.test.cpp
|
||||
test/util/token.test.cpp
|
||||
test/util/unique_any.test.cpp
|
||||
test/util/url.test.cpp
|
||||
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/unique_any.hpp>
|
||||
#include <mbgl/util/peer.hpp>
|
||||
#include <mbgl/util/immutable.hpp>
|
||||
#include <mbgl/style/layer_type.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
@@ -132,7 +132,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.
|
||||
util::unique_any peer;
|
||||
util::peer peer;
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/optional.hpp>
|
||||
#include <mbgl/util/unique_any.hpp>
|
||||
#include <mbgl/util/peer.hpp>
|
||||
#include <mbgl/util/immutable.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
|
||||
@@ -77,7 +77,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.
|
||||
util::unique_any peer;
|
||||
util::peer peer;
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
|
||||
111
include/mbgl/util/peer.hpp
Normal file
111
include/mbgl/util/peer.hpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
class peer {
|
||||
public:
|
||||
peer() = default;
|
||||
peer(const peer&) = delete;
|
||||
|
||||
peer(peer&& other)
|
||||
: vtable(other.vtable)
|
||||
{
|
||||
if (vtable) {
|
||||
vtable->move(other.storage, storage);
|
||||
}
|
||||
other.vtable = nullptr;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
peer(T&& value) {
|
||||
using _Vt = std::decay_t<T>;
|
||||
vtable = get_vtable<_Vt>();
|
||||
new (&storage) _Vt(std::forward<T>(value));
|
||||
}
|
||||
|
||||
~peer() {
|
||||
reset();
|
||||
}
|
||||
|
||||
peer& operator=(peer&& rhs) {
|
||||
peer(std::move(rhs)).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (vtable) {
|
||||
vtable->destroy(storage);
|
||||
vtable = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void swap(peer& rhs) {
|
||||
if (this == &rhs) {
|
||||
return;
|
||||
} else {
|
||||
peer tmp(std::move(rhs));
|
||||
rhs.vtable = vtable;
|
||||
if (rhs.vtable) {
|
||||
rhs.vtable->move(storage, rhs.storage);
|
||||
}
|
||||
vtable = tmp.vtable;
|
||||
if (vtable) {
|
||||
vtable->move(tmp.storage, storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_value() const {
|
||||
return vtable != nullptr;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T& get() {
|
||||
return reinterpret_cast<T&>(storage);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T&& take() {
|
||||
reset();
|
||||
return std::move(get<T>());
|
||||
}
|
||||
|
||||
private:
|
||||
using storage_t = std::aligned_storage_t<2*sizeof(void*), alignof(void*)>;
|
||||
|
||||
struct vtable {
|
||||
virtual ~vtable() = default;
|
||||
virtual void move(storage_t&, storage_t&) = 0;
|
||||
virtual void destroy(storage_t&) = 0;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct vtable_impl : public vtable {
|
||||
static_assert(sizeof(T) <= sizeof(storage_t), "peer object is too big");
|
||||
|
||||
void move(storage_t& src, storage_t& dst) override {
|
||||
new (&dst) T(std::move(reinterpret_cast<T&>(src)));
|
||||
destroy(src);
|
||||
}
|
||||
|
||||
void destroy(storage_t& s) override {
|
||||
reinterpret_cast<T&>(s).~T();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
static vtable* get_vtable() {
|
||||
static vtable_impl<T> vtable;
|
||||
return &vtable;
|
||||
}
|
||||
|
||||
vtable* vtable = nullptr;
|
||||
storage_t storage;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
} // namespace mbgl
|
||||
@@ -1,271 +0,0 @@
|
||||
#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 {
|
||||
dest.dynamic = src.dynamic;
|
||||
src.dynamic = nullptr;
|
||||
}
|
||||
|
||||
void destroy(Storage& s) override {
|
||||
delete reinterpret_cast<ValueType*>(s.dynamic);
|
||||
}
|
||||
|
||||
const std::type_info& type() override {
|
||||
return typeid(ValueType);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ValueType>
|
||||
struct VTableStack : public VTable {
|
||||
void move(Storage&& src, Storage& dest) override {
|
||||
new (&dest.stack) ValueType(std::move(reinterpret_cast<ValueType&>(src.stack)));
|
||||
destroy(src);
|
||||
}
|
||||
|
||||
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 (&storage.stack) _Vt(std::forward<ValueType>(value));
|
||||
}
|
||||
|
||||
template <typename ValueType, typename _Vt>
|
||||
std::enable_if_t<!AllocateOnStack<_Vt>::value>
|
||||
createStorage(ValueType&& value) {
|
||||
storage.dynamic = 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
|
||||
@@ -51,7 +51,7 @@ namespace android {
|
||||
if (!coreSource.peer.has_value()) {
|
||||
coreSource.peer = createSourcePeer(env, coreSource, frontend);
|
||||
}
|
||||
return *mbgl::util::any_cast<std::unique_ptr<Source>>(&coreSource.peer)->get()->javaPeer;
|
||||
return *coreSource.peer.get<std::unique_ptr<Source>>()->javaPeer;
|
||||
}
|
||||
|
||||
Source::Source(jni::JNIEnv& env, mbgl::style::Source& coreSource, jni::Object<Source> obj, AndroidRendererFrontend& frontend)
|
||||
@@ -125,7 +125,7 @@ namespace android {
|
||||
|
||||
// Release the peer relationships. These will be re-established when the source is added to a map
|
||||
assert(ownedSource->peer.has_value());
|
||||
util::any_cast<std::unique_ptr<Source>>(&(ownedSource->peer))->release();
|
||||
ownedSource->peer.get<std::unique_ptr<Source>>().release();
|
||||
ownedSource->peer.reset();
|
||||
|
||||
// Release the strong reference to the java peer
|
||||
|
||||
@@ -177,7 +177,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles,
|
||||
}
|
||||
|
||||
- (MGLSource *)sourceFromMBGLSource:(mbgl::style::Source *)rawSource {
|
||||
if (MGLSource *source = rawSource->peer.has_value() ? mbgl::util::any_cast<SourceWrapper>(rawSource->peer).source : nil) {
|
||||
if (MGLSource *source = rawSource->peer.has_value() ? rawSource->peer.get<SourceWrapper>().source : nil) {
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles,
|
||||
{
|
||||
NSParameterAssert(rawLayer);
|
||||
|
||||
if (MGLStyleLayer *layer = rawLayer->peer.has_value() ? mbgl::util::any_cast<LayerWrapper>(&(rawLayer->peer))->layer : nil) {
|
||||
if (MGLStyleLayer *layer = rawLayer->peer.has_value() ? rawLayer->peer.get<LayerWrapper>().layer : nil) {
|
||||
return layer;
|
||||
}
|
||||
|
||||
|
||||
194
test/util/peer.test.cpp
Normal file
194
test/util/peer.test.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#include <mbgl/test/util.hpp>
|
||||
|
||||
#include <mbgl/util/peer.hpp>
|
||||
|
||||
using namespace mbgl::util;
|
||||
|
||||
class TestType {
|
||||
public:
|
||||
TestType() : i1(0), i2(1) {
|
||||
str[0] = 'a';
|
||||
}
|
||||
|
||||
//Detect moves
|
||||
TestType(TestType&& t): i1(t.i1+1), i2(t.i2+2) {
|
||||
str[0] = t.str[0]+1;
|
||||
}
|
||||
|
||||
TestType(const TestType&) = delete;
|
||||
TestType& operator=(const TestType&) = delete;
|
||||
|
||||
int i1;
|
||||
int i2;
|
||||
char str[256];
|
||||
};
|
||||
|
||||
bool IsStackAllocated (const peer& 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(peer));
|
||||
};
|
||||
|
||||
TEST(Peer, Empty) {
|
||||
EXPECT_FALSE(peer().has_value());
|
||||
}
|
||||
|
||||
TEST(Peer, BasicTypes) {
|
||||
peer i = 3;
|
||||
EXPECT_TRUE(i.has_value());
|
||||
EXPECT_TRUE(i.get<decltype(3)>() == 3);
|
||||
|
||||
auto iValue = i.get<decltype(3)>();
|
||||
EXPECT_TRUE(iValue == 3);
|
||||
|
||||
EXPECT_TRUE(peer(4).has_value());
|
||||
EXPECT_TRUE(peer(4).get<decltype(4)>() == 4);
|
||||
|
||||
peer f = 6.2f;
|
||||
EXPECT_TRUE(f.has_value());
|
||||
EXPECT_TRUE(f.get<decltype(6.2f)>() == 6.2f);
|
||||
|
||||
const float fValue = f.get<decltype(6.2f)>();
|
||||
EXPECT_TRUE(fValue == 6.2f);
|
||||
|
||||
EXPECT_TRUE(peer(1.0f).has_value());
|
||||
EXPECT_TRUE(peer(1.0f).get<decltype(1.0f)>() == 1.0f);
|
||||
|
||||
peer c = 'z';
|
||||
EXPECT_TRUE(c.has_value());
|
||||
EXPECT_TRUE(c.get<decltype('z')>() == 'z');
|
||||
|
||||
EXPECT_TRUE(peer('z').has_value());
|
||||
EXPECT_TRUE(peer('z').get<decltype('z')>() == 'z');
|
||||
}
|
||||
|
||||
TEST(Peer, BasicTypes_Move) {
|
||||
peer i = 3;
|
||||
EXPECT_TRUE(i.has_value());
|
||||
|
||||
peer f = 6.2f;
|
||||
EXPECT_TRUE(f.has_value());
|
||||
|
||||
f = std::move(i);
|
||||
EXPECT_FALSE(i.has_value());
|
||||
|
||||
EXPECT_TRUE(f.has_value());
|
||||
EXPECT_TRUE(f.get<decltype(3)>() == 3);
|
||||
}
|
||||
|
||||
TEST(Peer, SmallType) {
|
||||
struct T {
|
||||
T(int32_t* p_) : p(p_) {
|
||||
(*p)++;
|
||||
}
|
||||
|
||||
T(T&& t) noexcept : p(t.p) {
|
||||
(*p)++;
|
||||
}
|
||||
|
||||
~T() {
|
||||
(*p)--;
|
||||
}
|
||||
|
||||
T(const T&) = delete;
|
||||
T& operator=(const T&) = delete;
|
||||
|
||||
int32_t* p;
|
||||
};
|
||||
|
||||
int32_t p = 0;
|
||||
|
||||
{
|
||||
peer u1 = peer(T(&p));
|
||||
EXPECT_EQ(p, 1);
|
||||
|
||||
auto u2(std::move(u1));
|
||||
EXPECT_EQ(p, 1);
|
||||
}
|
||||
|
||||
EXPECT_EQ(p, 0);
|
||||
}
|
||||
|
||||
// peer is not able to receive large types, unless we increase storage_t
|
||||
// capacity.
|
||||
//TEST(Peer, LargeType) {
|
||||
// TestType t1;
|
||||
// peer u1 = peer(std::move(t1));
|
||||
// EXPECT_TRUE(u1.has_value());
|
||||
//
|
||||
// //TestType should be moved into owning peer
|
||||
// EXPECT_EQ(u1.get<TestType>().i1, 1);
|
||||
//
|
||||
// auto u2(std::move(u1));
|
||||
// EXPECT_FALSE(u1.has_value());
|
||||
//
|
||||
// //TestType should not be moved when owning peer is moved;
|
||||
// EXPECT_EQ(u2.get<TestType>().i1, 1);
|
||||
//
|
||||
// //TestType should be moved out of owning peer
|
||||
// // Note: two moves are involved in returning the moved value
|
||||
// // First out of the peer, and then in the return statement
|
||||
// auto t2 = std::move(u2.get<TestType>());
|
||||
// EXPECT_FALSE(u2.has_value());
|
||||
// EXPECT_EQ(t2.i1, 3);
|
||||
//}
|
||||
|
||||
TEST(Peer, Pointer) {
|
||||
auto t1 = new TestType();
|
||||
|
||||
auto u1 = peer(std::move(t1));
|
||||
EXPECT_TRUE(u1.has_value());
|
||||
|
||||
//Only the pointer should be moved
|
||||
TestType * t2 = u1.get<TestType *>();
|
||||
EXPECT_EQ(t2->i1, 0);
|
||||
|
||||
peer u2(4);
|
||||
std::swap(u2, u1);
|
||||
|
||||
EXPECT_TRUE(u1.has_value());
|
||||
|
||||
EXPECT_TRUE(u2.has_value());
|
||||
|
||||
t2 = u2.get<TestType *>();
|
||||
EXPECT_EQ(t2->i1, 0);
|
||||
delete t2;
|
||||
}
|
||||
|
||||
|
||||
TEST(Peer, UniquePtr) {
|
||||
auto t1 = std::make_unique<TestType>();
|
||||
auto u1 = peer(std::move(t1));
|
||||
|
||||
EXPECT_EQ(t1.get(), nullptr);
|
||||
EXPECT_TRUE(u1.has_value());
|
||||
|
||||
auto t2 = std::move(u1.take<TestType>());
|
||||
EXPECT_FALSE(u1.has_value());
|
||||
(void)t2;
|
||||
|
||||
peer u2;
|
||||
TestType * t3 = new TestType();
|
||||
u2 = std::unique_ptr<TestType>(t3);
|
||||
EXPECT_TRUE(u2.has_value());
|
||||
}
|
||||
|
||||
TEST(Peer, SharedPtr) {
|
||||
|
||||
std::shared_ptr<int> shared(new int(3));
|
||||
std::weak_ptr<int> weak = shared;
|
||||
peer u1 = 0;
|
||||
|
||||
EXPECT_EQ(weak.use_count(), 1);
|
||||
peer u2 = shared;
|
||||
EXPECT_EQ(weak.use_count(), 2);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
#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';
|
||||
}
|
||||
|
||||
//Detect moves
|
||||
TestType(TestType&& t): i1(t.i1+1), i2(t.i2+2) {
|
||||
str[0] = t.str[0]+1;
|
||||
}
|
||||
|
||||
TestType(const TestType&) = delete;
|
||||
TestType& operator=(const TestType&) = delete;
|
||||
|
||||
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, SmallType) {
|
||||
struct T {
|
||||
T(int32_t* p_) : p(p_) {
|
||||
(*p)++;
|
||||
}
|
||||
|
||||
T(T&& t) noexcept : p(t.p) {
|
||||
(*p)++;
|
||||
}
|
||||
|
||||
~T() {
|
||||
(*p)--;
|
||||
}
|
||||
|
||||
T(const T&) = delete;
|
||||
T& operator=(const T&) = delete;
|
||||
|
||||
int32_t* p;
|
||||
};
|
||||
|
||||
int32_t p = 0;
|
||||
|
||||
{
|
||||
unique_any u1 = unique_any(T(&p));
|
||||
EXPECT_EQ(p, 1);
|
||||
|
||||
auto u2(std::move(u1));
|
||||
EXPECT_EQ(p, 1);
|
||||
}
|
||||
|
||||
EXPECT_EQ(p, 0);
|
||||
}
|
||||
|
||||
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