[core] Simplify util::peer

Remove custom vtable, base implementation on `std::unique_ptr`.
This commit is contained in:
Mikhail Pozdnyakov
2019-02-07 12:13:19 +02:00
parent 96ec18376f
commit 810999fadf
3 changed files with 31 additions and 115 deletions

View File

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

View File

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

View File

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