summaryrefslogtreecommitdiff
path: root/include/mbgl/util/peer.hpp
blob: a4abea0e883d5fc966d9b92b3226c8f3bf2c9e7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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