Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
quniquehandle_p.h
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
5#ifndef QUNIQUEHANDLE_P_H
6#define QUNIQUEHANDLE_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <QtCore/qtconfigmacros.h>
20#include <QtCore/qassert.h>
21#include <QtCore/qcompare.h>
22#include <QtCore/qfunctionaltools_impl.h>
23#include <QtCore/qswap.h>
24#include <QtCore/qtclasshelpermacros.h>
25
26#include <memory>
27#include <utility>
28#include <type_traits>
29
30QT_BEGIN_NAMESPACE
31
32/*! \internal QUniqueHandle is a general purpose RAII wrapper intended
33 for interfacing with resource-allocating C-style APIs, for example
34 operating system APIs, database engine APIs, or any other scenario
35 where resources are allocated and released, and where pointer
36 semantics does not seem a perfect fit.
37
38 QUniqueHandle does not support copying, because it is intended to
39 maintain ownership of resources that can not be copied. This makes
40 it safer to use than naked handle types, since ownership is
41 maintained by design.
42
43 The underlying handle object is described using a client supplied
44 HandleTraits object that is implemented per resource type. The
45 traits struct must describe two properties of a handle:
46
47 1) What value is considered invalid
48 2) How to close a resource.
49
50 Example 1:
51
52 struct InvalidHandleTraits {
53 using Type = HANDLE;
54
55 static Type invalidValue() {
56 return INVALID_HANDLE_VALUE;
57 }
58
59 static bool close(Type handle) {
60 return CloseHandle(handle) != 0;
61 }
62 }
63
64 using FileHandle = QUniqueHandle<InvalidHandleTraits>;
65
66 Usage:
67
68 // Takes ownership of returned handle.
69 FileHandle handle{ CreateFile(...) };
70
71 if (!handle.isValid()) {
72 qDebug() << GetLastError()
73 return;
74 }
75
76 ...
77
78 Example 2:
79
80 struct SqLiteTraits {
81 using Type = sqlite3*;
82
83 static Type invalidValue() {
84 return nullptr;
85 }
86
87 static bool close(Type handle) {
88 sqlite3_close(handle);
89 return true;
90 }
91 }
92
93 using DbHandle = QUniqueHandle<SqLiteTraits>;
94
95 Usage:
96
97 DbHandle h;
98
99 // Take ownership of returned handle.
100 int result = sqlite3_open(":memory:", &h);
101
102 ...
103
104 Example 3:
105
106 struct TempFileTraits {
107 using Type = FILE*;
108
109 static Type invalidValue() {
110 return nullptr;
111 }
112
113 static bool close(Type handle) {
114 return fclose(handle) == 0;
115 }
116 };
117
118 struct TempFileDeleter {
119 using Type = TempFileTraits::Type;
120
121 void operator()(Type handle) {
122 if (handle != TempFileTraits::invalidValue()) {
123 TempFileTraits::close(handle);
124 if (path)
125 remove(path);
126 }
127 }
128
129 const char* path{ nullptr };
130 };
131
132 using TempFileHandle = QUniqueHandle<TempFileTraits, TempFileDeleter>;
133
134 Usage:
135
136 TempFileHandle tempFile(fopen("temp.bin", "wb"), TempFileDeleter{ "my_temp.bin" });
137
138 ...
139
140 NOTE: The QUniqueHandle assumes that closing a resource is
141 guaranteed to succeed, and provides no support for handling failure
142 to close a resource. It is therefore only recommended for use cases
143 where failure to close a resource is either not an error, or an
144 unrecoverable error.
145*/
146
147// clang-format off
148
149namespace QtUniqueHandleTraits {
150
151template <typename HandleTraits>
153{
154 using Type = typename HandleTraits::Type;
155
156 void operator()(Type handle) const noexcept
157 {
158 if (handle != HandleTraits::invalidValue()) {
159 const bool success = HandleTraits::close(handle);
160 Q_ASSERT(success);
161 }
162 }
163};
164
165} // namespace QtUniqueHandleTraits
166
167template <typename HandleTraits, typename Deleter = QtUniqueHandleTraits::DefaultDeleter<HandleTraits>>
169{
171
172 template <typename D>
174
175public:
176 using Type = typename HandleTraits::Type;
177 static_assert(std::is_nothrow_default_constructible_v<Type>);
178 static_assert(std::is_nothrow_constructible_v<Type>);
179 static_assert(std::is_nothrow_copy_constructible_v<Type>);
180 static_assert(std::is_nothrow_move_constructible_v<Type>);
181 static_assert(std::is_nothrow_copy_assignable_v<Type>);
182 static_assert(std::is_nothrow_move_assignable_v<Type>);
183 static_assert(std::is_nothrow_destructible_v<Type>);
184 static_assert(std::is_nothrow_copy_constructible_v<Deleter>);
185 static_assert(std::is_nothrow_move_constructible_v<Deleter>);
186 static_assert(std::is_nothrow_copy_assignable_v<Deleter>);
187 static_assert(std::is_nothrow_move_assignable_v<Deleter>);
188 static_assert(std::is_nothrow_destructible_v<Deleter>);
189 static_assert(noexcept(std::declval<Type>() == std::declval<Type>()));
190 static_assert(noexcept(std::declval<Type>() != std::declval<Type>()));
191 static_assert(noexcept(std::declval<Type>() < std::declval<Type>()));
192 static_assert(noexcept(std::declval<Type>() <= std::declval<Type>()));
193 static_assert(noexcept(std::declval<Type>() > std::declval<Type>()));
194 static_assert(noexcept(std::declval<Type>() >= std::declval<Type>()));
195
196 template <if_default_constructible<Deleter> = true>
197 QUniqueHandle() noexcept
198 {}
199
200 template <if_default_constructible<Deleter> = true>
201 explicit QUniqueHandle(const Type &handle) noexcept
202 : m_handle{ handle }
203 {}
204
205 QUniqueHandle(const Type &handle, const Deleter &deleter) noexcept
207 {}
208
209 QUniqueHandle(const Type &handle, Deleter &&deleter) noexcept
211 {}
212
213 QUniqueHandle(QUniqueHandle &&other) noexcept
215 {
216 }
217
218 ~QUniqueHandle() noexcept
219 {
220 close();
221 }
222
223 void swap(QUniqueHandle &other) noexcept
224 {
225 qSwap(m_handle, other.m_handle);
226 qSwap(deleter(), other.deleter());
227 }
228
230
231 QUniqueHandle(const QUniqueHandle &) = delete;
233
234
235 [[nodiscard]] bool isValid() const noexcept
236 {
237 return m_handle != HandleTraits::invalidValue();
238 }
239
240 [[nodiscard]] explicit operator bool() const noexcept
241 {
242 return isValid();
243 }
244
245 [[nodiscard]] Type get() const noexcept
246 {
247 return m_handle;
248 }
249
250 [[nodiscard]] Deleter& deleter() noexcept
251 {
252 return Storage::object();
253 }
254
255 [[nodiscard]] const Deleter& deleter() const noexcept
256 {
257 return Storage::object();
258 }
259
260 void reset(const Type& handle = HandleTraits::invalidValue()) noexcept
261 {
262 if (handle == m_handle)
263 return;
264
265 close();
266 m_handle = handle;
267 }
268
269 [[nodiscard]] Type release() noexcept
270 {
271 return std::exchange(m_handle, HandleTraits::invalidValue());
272 }
273
274 [[nodiscard]] Type *operator&() noexcept // NOLINT(google-runtime-operator)
275 {
276 Q_ASSERT(!isValid());
277 return &m_handle;
278 }
279
280 void close() noexcept
281 {
282 if (!isValid())
283 return;
284
285 deleter()(m_handle);
286
287 m_handle = HandleTraits::invalidValue();
288 }
289
290private:
291 friend bool comparesEqual(const QUniqueHandle& lhs, const QUniqueHandle& rhs) noexcept
292 {
293 return lhs.get() == rhs.get();
294 }
295
297 const QUniqueHandle& rhs) noexcept
298 {
299 if constexpr (std::is_pointer_v<Type>)
300 return qCompareThreeWay(Qt::totally_ordered_wrapper{ lhs.get() },
301 Qt::totally_ordered_wrapper{ rhs.get() });
302 else
303 return qCompareThreeWay(lhs.get(), rhs.get());
304 }
305
307
309};
310
311// clang-format on
312
313template <typename Trait, typename Deleter>
314void swap(QUniqueHandle<Trait, Deleter> &lhs, QUniqueHandle<Trait, Deleter> &rhs) noexcept
315{
316 lhs.swap(rhs);
317}
318
319
320QT_END_NAMESPACE
321
322#endif
Type get() const noexcept
operator bool() const noexcept
friend Qt::strong_ordering compareThreeWay(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept
void swap(QUniqueHandle &other) noexcept
friend bool comparesEqual(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept
QUniqueHandle() noexcept
QUniqueHandle(QUniqueHandle &&other) noexcept
bool isValid() const noexcept
const Deleter & deleter() const noexcept
~QUniqueHandle() noexcept
QUniqueHandle(const Type &handle, const Deleter &deleter) noexcept
void reset(const Type &handle=HandleTraits::invalidValue()) noexcept
QUniqueHandle(const Type &handle, Deleter &&deleter) noexcept
QUniqueHandle & operator=(const QUniqueHandle &)=delete
Type * operator&() noexcept
Deleter & deleter() noexcept
void close() noexcept
QUniqueHandle(const Type &handle) noexcept
Type release() noexcept
typename HandleTraits::Type Type
void swap(QUniqueHandle< Trait, Deleter > &lhs, QUniqueHandle< Trait, Deleter > &rhs) noexcept
void operator()(Type handle) const noexcept
typename HandleTraits::Type Type