summaryrefslogtreecommitdiff
path: root/ext/pybind11/tests/object.h
blob: 9235f19c20bff3afb59c6880a84c809205eff6ea (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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#if !defined(__OBJECT_H)
#define __OBJECT_H

#include <atomic>
#include "constructor_stats.h"

/// Reference counted object base class
class Object {
public:
    /// Default constructor
    Object() { print_default_created(this); }

    /// Copy constructor
    Object(const Object &) : m_refCount(0) { print_copy_created(this); }

    /// Return the current reference count
    int getRefCount() const { return m_refCount; };

    /// Increase the object's reference count by one
    void incRef() const { ++m_refCount; }

    /** \brief Decrease the reference count of
     * the object and possibly deallocate it.
     *
     * The object will automatically be deallocated once
     * the reference count reaches zero.
     */
    void decRef(bool dealloc = true) const {
        --m_refCount;
        if (m_refCount == 0 && dealloc)
            delete this;
        else if (m_refCount < 0)
            throw std::runtime_error("Internal error: reference count < 0!");
    }

    virtual std::string toString() const = 0;
protected:
    /** \brief Virtual protected deconstructor.
     * (Will only be called by \ref ref)
     */
    virtual ~Object() { print_destroyed(this); }
private:
    mutable std::atomic<int> m_refCount { 0 };
};

// Tag class used to track constructions of ref objects.  When we track constructors, below, we
// track and print out the actual class (e.g. ref<MyObject>), and *also* add a fake tracker for
// ref_tag.  This lets us check that the total number of ref<Anything> constructors/destructors is
// correct without having to check each individual ref<Whatever> type individually.
class ref_tag {};

/**
 * \brief Reference counting helper
 *
 * The \a ref refeference template is a simple wrapper to store a
 * pointer to an object. It takes care of increasing and decreasing
 * the reference count of the object. When the last reference goes
 * out of scope, the associated object will be deallocated.
 *
 * \ingroup libcore
 */
template <typename T> class ref {
public:
    /// Create a nullptr reference
    ref() : m_ptr(nullptr) { print_default_created(this); track_default_created((ref_tag*) this); }

    /// Construct a reference from a pointer
    ref(T *ptr) : m_ptr(ptr) {
        if (m_ptr) ((Object *) m_ptr)->incRef();

        print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer");

    }

    /// Copy constructor
    ref(const ref &r) : m_ptr(r.m_ptr) {
        if (m_ptr)
            ((Object *) m_ptr)->incRef();

        print_copy_created(this, "with pointer", m_ptr); track_copy_created((ref_tag*) this);
    }

    /// Move constructor
    ref(ref &&r) : m_ptr(r.m_ptr) {
        r.m_ptr = nullptr;

        print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this);
    }

    /// Destroy this reference
    ~ref() {
        if (m_ptr)
            ((Object *) m_ptr)->decRef();

        print_destroyed(this); track_destroyed((ref_tag*) this);
    }

    /// Move another reference into the current one
    ref& operator=(ref&& r) {
        print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this);

        if (*this == r)
            return *this;
        if (m_ptr)
            ((Object *) m_ptr)->decRef();
        m_ptr = r.m_ptr;
        r.m_ptr = nullptr;
        return *this;
    }

    /// Overwrite this reference with another reference
    ref& operator=(const ref& r) {
        print_copy_assigned(this, "pointer", r.m_ptr); track_copy_assigned((ref_tag*) this);

        if (m_ptr == r.m_ptr)
            return *this;
        if (m_ptr)
            ((Object *) m_ptr)->decRef();
        m_ptr = r.m_ptr;
        if (m_ptr)
            ((Object *) m_ptr)->incRef();
        return *this;
    }

    /// Overwrite this reference with a pointer to another object
    ref& operator=(T *ptr) {
        print_values(this, "assigned pointer"); track_values((ref_tag*) this, "assigned pointer");

        if (m_ptr == ptr)
            return *this;
        if (m_ptr)
            ((Object *) m_ptr)->decRef();
        m_ptr = ptr;
        if (m_ptr)
            ((Object *) m_ptr)->incRef();
        return *this;
    }

    /// Compare this reference with another reference
    bool operator==(const ref &r) const { return m_ptr == r.m_ptr; }

    /// Compare this reference with another reference
    bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; }

    /// Compare this reference with a pointer
    bool operator==(const T* ptr) const { return m_ptr == ptr; }

    /// Compare this reference with a pointer
    bool operator!=(const T* ptr) const { return m_ptr != ptr; }

    /// Access the object referenced by this reference
    T* operator->() { return m_ptr; }

    /// Access the object referenced by this reference
    const T* operator->() const { return m_ptr; }

    /// Return a C++ reference to the referenced object
    T& operator*() { return *m_ptr; }

    /// Return a const C++ reference to the referenced object
    const T& operator*() const { return *m_ptr; }

    /// Return a pointer to the referenced object
    operator T* () { return m_ptr; }

    /// Return a const pointer to the referenced object
    T* get_ptr() { return m_ptr; }

    /// Return a pointer to the referenced object
    const T* get_ptr() const { return m_ptr; }
private:
    T *m_ptr;
};

#endif /* __OBJECT_H */