summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/include/ports/SkCFObject.h
blob: 20e86671b790413a562668290863828123485031 (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
176
177
178
179
180
/*
 * Copyright 2019 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkCFObject_DEFINED
#define SkCFObject_DEFINED

#ifdef __APPLE__

#include "include/core/SkTypes.h"

#include <cstddef>      // std::nullptr_t

#import <CoreFoundation/CoreFoundation.h>

/**
 * Wrapper class for managing lifetime of CoreFoundation objects. It will call
 * CFRetain and CFRelease appropriately on creation, assignment, and deletion.
 * Based on sk_sp<>.
 */
template <typename T> static inline T SkCFSafeRetain(T obj) {
    if (obj) {
        CFRetain(obj);
    }
    return obj;
}

template <typename T> static inline void SkCFSafeRelease(T obj) {
    if (obj) {
        CFRelease(obj);
    }
}

template <typename T> class sk_cfp {
public:
    using element_type = T;

    constexpr sk_cfp() {}
    constexpr sk_cfp(std::nullptr_t) {}

    /**
     *  Shares the underlying object by calling CFRetain(), so that both the argument and the newly
     *  created sk_cfp both have a reference to it.
     */
    sk_cfp(const sk_cfp<T>& that) : fObject(SkCFSafeRetain(that.get())) {}

    /**
     *  Move the underlying object from the argument to the newly created sk_cfp. Afterwards only
     *  the new sk_cfp will have a reference to the object, and the argument will point to null.
     *  No call to CFRetain() or CFRelease() will be made.
     */
    sk_cfp(sk_cfp<T>&& that) : fObject(that.release()) {}

    /**
     *  Adopt the bare object into the newly created sk_cfp.
     *  No call to CFRetain() or CFRelease() will be made.
     */
    explicit sk_cfp(T obj) {
        fObject = obj;
    }

    /**
     *  Calls CFRelease() on the underlying object pointer.
     */
    ~sk_cfp() {
        SkCFSafeRelease(fObject);
        SkDEBUGCODE(fObject = nil);
    }

    sk_cfp<T>& operator=(std::nullptr_t) { this->reset(); return *this; }

    /**
     *  Shares the underlying object referenced by the argument by calling CFRetain() on it. If this
     *  sk_cfp previously had a reference to an object (i.e. not null) it will call CFRelease()
     *  on that object.
     */
    sk_cfp<T>& operator=(const sk_cfp<T>& that) {
        if (this != &that) {
            this->reset(SkCFSafeRetain(that.get()));
        }
        return *this;
    }

    /**
     *  Move the underlying object from the argument to the sk_cfp. If the sk_cfp
     * previously held a reference to another object, CFRelease() will be called on that object.
     * No call to CFRetain() will be made.
     */
    sk_cfp<T>& operator=(sk_cfp<T>&& that) {
        this->reset(that.release());
        return *this;
    }

    explicit operator bool() const { return this->get() != nil; }

    T get() const { return fObject; }
    T operator*() const {
        SkASSERT(fObject);
        return fObject;
    }

    /**
     *  Adopt the new object, and call CFRelease() on any previously held object (if not null).
     *  No call to CFRetain() will be made.
     */
    void reset(T object = nil) {
        // Need to unref after assigning, see
        // http://wg21.cmeerw.net/lwg/issue998
        // http://wg21.cmeerw.net/lwg/issue2262
        T oldObject = fObject;
        fObject = object;
        SkCFSafeRelease(oldObject);
    }

    /**
     *  Shares the new object by calling CFRetain() on it. If this sk_cfp previously had a
     *  reference to an object (i.e. not null) it will call CFRelease() on that object.
     */
    void retain(T object) {
        if (fObject != object) {
            this->reset(SkCFSafeRetain(object));
        }
    }

    /**
     *  Return the original object, and set the internal object to nullptr.
     *  The caller must assume ownership of the object, and manage its reference count directly.
     *  No call to CFRelease() will be made.
     */
    T SK_WARN_UNUSED_RESULT release() {
        T obj = fObject;
        fObject = nil;
        return obj;
    }

private:
    T fObject = nil;
};

template <typename T> inline bool operator==(const sk_cfp<T>& a,
                                             const sk_cfp<T>& b) {
    return a.get() == b.get();
}
template <typename T> inline bool operator==(const sk_cfp<T>& a,
                                             std::nullptr_t) {
    return !a;
}
template <typename T> inline bool operator==(std::nullptr_t,
                                             const sk_cfp<T>& b) {
    return !b;
}

template <typename T> inline bool operator!=(const sk_cfp<T>& a,
                                             const sk_cfp<T>& b) {
    return a.get() != b.get();
}
template <typename T> inline bool operator!=(const sk_cfp<T>& a,
                                             std::nullptr_t) {
    return static_cast<bool>(a);
}
template <typename T> inline bool operator!=(std::nullptr_t,
                                             const sk_cfp<T>& b) {
    return static_cast<bool>(b);
}

/*
 *  Returns a sk_cfp wrapping the provided object AND calls retain on it (if not null).
 *
 *  This is different than the semantics of the constructor for sk_cfp, which just wraps the
 *  object, effectively "adopting" it.
 */
template <typename T> sk_cfp<T> sk_ret_cfp(T obj) {
    return sk_cfp<T>(SkCFSafeRetain(obj));
}

#endif  // __APPLE__
#endif  // SkCFObject_DEFINED