[core] Simplify util::peer
Remove custom vtable, base implementation on `std::unique_ptr`.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -8,102 +9,22 @@ 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;
|
||||
}
|
||||
peer() noexcept : storage(nullptr, noop_deleter) {}
|
||||
|
||||
template <class T>
|
||||
peer(T&& value) {
|
||||
using _Vt = std::decay_t<T>;
|
||||
vtable = get_vtable<_Vt>();
|
||||
new (&storage) _Vt(std::forward<T>(value));
|
||||
}
|
||||
peer(T&& value) noexcept : storage(new std::decay_t<T>(std::forward<T>(value)), cast_deleter<std::decay_t<T>>) {}
|
||||
|
||||
~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;
|
||||
}
|
||||
bool has_value() const noexcept { return static_cast<bool>(storage); }
|
||||
|
||||
template <class T>
|
||||
T& get() {
|
||||
return reinterpret_cast<T&>(storage);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T&& take() {
|
||||
reset();
|
||||
return std::move(get<T>());
|
||||
}
|
||||
T& get() noexcept { return *reinterpret_cast<T*>(storage.get()); }
|
||||
|
||||
private:
|
||||
using storage_t = std::aligned_storage_t<2*sizeof(void*), alignof(void*)>;
|
||||
template <typename T>
|
||||
static void cast_deleter(void* ptr) noexcept { delete reinterpret_cast<T*>(ptr); }
|
||||
static void noop_deleter(void*) noexcept {}
|
||||
|
||||
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;
|
||||
using storage_t = std::unique_ptr<void, void(*)(void*)>;
|
||||
storage_t storage;
|
||||
};
|
||||
|
||||
|
||||
@@ -133,7 +133,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());
|
||||
ownedSource->peer.get<std::unique_ptr<Source>>().release();
|
||||
ownedSource->peer.reset();
|
||||
ownedSource->peer = util::peer();
|
||||
|
||||
// Release the strong reference to the java peer
|
||||
assert(javaPeer);
|
||||
|
||||
@@ -109,29 +109,25 @@ TEST(Peer, SmallType) {
|
||||
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, 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 not be moved out of owning peer
|
||||
auto& t2 = u2.get<TestType>();
|
||||
EXPECT_TRUE(u2.has_value());
|
||||
EXPECT_EQ(t2.i1, 1);
|
||||
}
|
||||
|
||||
TEST(Peer, Pointer) {
|
||||
auto t1 = new TestType();
|
||||
@@ -163,9 +159,8 @@ TEST(Peer, UniquePtr) {
|
||||
EXPECT_EQ(t1.get(), nullptr);
|
||||
EXPECT_TRUE(u1.has_value());
|
||||
|
||||
auto t2 = std::move(u1.take<TestType>());
|
||||
u1 = peer();
|
||||
EXPECT_FALSE(u1.has_value());
|
||||
(void)t2;
|
||||
|
||||
peer u2;
|
||||
TestType * t3 = new TestType();
|
||||
@@ -185,7 +180,7 @@ TEST(Peer, SharedPtr) {
|
||||
|
||||
u1 = std::move(u2);
|
||||
EXPECT_EQ(weak.use_count(), 2);
|
||||
u2.swap(u1);
|
||||
std::swap(u2, u1);
|
||||
EXPECT_EQ(weak.use_count(), 2);
|
||||
u2 = 0;
|
||||
EXPECT_EQ(weak.use_count(), 1);
|
||||
|
||||
Reference in New Issue
Block a user