1 | // Copyright (C) 2023 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QUNIQUEHANDLE_P_H |
5 | #define QUNIQUEHANDLE_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | |
18 | #include <QtCore/qtconfigmacros.h> |
19 | #include <QtCore/qassert.h> |
20 | |
21 | #include <memory> |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | /*! \internal QUniqueHandle is a general purpose RAII wrapper intended |
26 | for interfacing with resource-allocating C-style APIs, for example |
27 | operating system APIs, database engine APIs, or any other scenario |
28 | where resources are allocated and released, and where pointer |
29 | semantics does not seem a perfect fit. |
30 | |
31 | QUniqueHandle does not support copying, because it is intended to |
32 | maintain ownership of resources that can not be copied. This makes |
33 | it safer to use than naked handle types, since ownership is |
34 | maintained by design. |
35 | |
36 | The underlying handle object is described using a client supplied |
37 | HandleTraits object that is implemented per resource type. The |
38 | traits struct must describe two properties of a handle: |
39 | |
40 | 1) What value is considered invalid |
41 | 2) How to close a resource. |
42 | |
43 | Example 1: |
44 | |
45 | struct InvalidHandleTraits { |
46 | using Type = HANDLE; |
47 | |
48 | static Type invalidValue() { |
49 | return INVALID_HANDLE_VALUE; |
50 | } |
51 | |
52 | static bool close(Type handle) { |
53 | return CloseHandle(handle) != 0; |
54 | } |
55 | } |
56 | |
57 | using FileHandle = QUniqueHandle<InvalidHandleTraits>; |
58 | |
59 | Usage: |
60 | |
61 | // Takes ownership of returned handle. |
62 | FileHandle handle{ CreateFile(...) }; |
63 | |
64 | if (!handle.isValid()) { |
65 | qDebug() << GetLastError() |
66 | return; |
67 | } |
68 | |
69 | ... |
70 | |
71 | Example 2: |
72 | |
73 | struct SqLiteTraits { |
74 | using Type = sqlite3*; |
75 | |
76 | static Type invalidValue() { |
77 | return nullptr; |
78 | } |
79 | |
80 | static bool close(Type handle) { |
81 | sqlite3_close(handle); |
82 | return true; |
83 | } |
84 | } |
85 | |
86 | using DbHandle = QUniqueHandle<SqLiteTraits>; |
87 | |
88 | Usage: |
89 | |
90 | DbHandle h; |
91 | |
92 | // Take ownership of returned handle. |
93 | int result = sqlite3_open(":memory:", &h); |
94 | |
95 | ... |
96 | |
97 | NOTE: The QUniqueHandle assumes that closing a resource is |
98 | guaranteed to succeed, and provides no support for handling failure |
99 | to close a resource. It is therefore only recommended for use cases |
100 | where failure to close a resource is either not an error, or an |
101 | unrecoverable error. |
102 | */ |
103 | |
104 | // clang-format off |
105 | |
106 | template <typename HandleTraits> |
107 | class QUniqueHandle |
108 | { |
109 | public: |
110 | using Type = typename HandleTraits::Type; |
111 | |
112 | QUniqueHandle() = default; |
113 | |
114 | explicit QUniqueHandle(const Type &handle) noexcept |
115 | : m_handle{ handle } |
116 | {} |
117 | |
118 | QUniqueHandle(QUniqueHandle &&other) noexcept |
119 | : m_handle{ other.release() } |
120 | {} |
121 | |
122 | ~QUniqueHandle() noexcept |
123 | { |
124 | close(); |
125 | } |
126 | |
127 | QUniqueHandle& operator=(QUniqueHandle &&rhs) noexcept |
128 | { |
129 | if (this != std::addressof(rhs)) |
130 | reset(handle: rhs.release()); |
131 | |
132 | return *this; |
133 | } |
134 | |
135 | QUniqueHandle(const QUniqueHandle &) = delete; |
136 | QUniqueHandle &operator=(const QUniqueHandle &) = delete; |
137 | |
138 | |
139 | [[nodiscard]] bool isValid() const noexcept |
140 | { |
141 | return m_handle != HandleTraits::invalidValue(); |
142 | } |
143 | |
144 | [[nodiscard]] explicit operator bool() const noexcept |
145 | { |
146 | return isValid(); |
147 | } |
148 | |
149 | [[nodiscard]] Type get() const noexcept |
150 | { |
151 | return m_handle; |
152 | } |
153 | |
154 | void reset(const Type& handle) noexcept |
155 | { |
156 | if (handle == m_handle) |
157 | return; |
158 | |
159 | close(); |
160 | m_handle = handle; |
161 | } |
162 | |
163 | [[nodiscard]] Type release() noexcept |
164 | { |
165 | Type handle = m_handle; |
166 | m_handle = HandleTraits::invalidValue(); |
167 | return handle; |
168 | } |
169 | |
170 | [[nodiscard]] Type *operator&() noexcept // NOLINT(google-runtime-operator) |
171 | { |
172 | Q_ASSERT(!isValid()); |
173 | return &m_handle; |
174 | } |
175 | |
176 | void close() noexcept |
177 | { |
178 | if (!isValid()) |
179 | return; |
180 | |
181 | const bool success = HandleTraits::close(m_handle); |
182 | Q_ASSERT(success); |
183 | |
184 | m_handle = HandleTraits::invalidValue(); |
185 | } |
186 | |
187 | [[nodiscard]] friend bool operator==(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept |
188 | { |
189 | return lhs.get() == rhs.get(); |
190 | } |
191 | |
192 | [[nodiscard]] friend bool operator!=(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept |
193 | { |
194 | return lhs.get() != rhs.get(); |
195 | } |
196 | |
197 | [[nodiscard]] friend bool operator<(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept |
198 | { |
199 | return lhs.get() < rhs.get(); |
200 | } |
201 | |
202 | [[nodiscard]] friend bool operator<=(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept |
203 | { |
204 | return lhs.get() <= rhs.get(); |
205 | } |
206 | |
207 | [[nodiscard]] friend bool operator>(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept |
208 | { |
209 | return lhs.get() > rhs.get(); |
210 | } |
211 | |
212 | [[nodiscard]] friend bool operator>=(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept |
213 | { |
214 | return lhs.get() >= rhs.get(); |
215 | } |
216 | |
217 | private: |
218 | Type m_handle{ HandleTraits::invalidValue() }; |
219 | }; |
220 | |
221 | // clang-format on |
222 | |
223 | QT_END_NAMESPACE |
224 | |
225 | #endif |
226 | |