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