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
23QT_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
106template <typename HandleTraits>
107class QUniqueHandle
108{
109public:
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
217private:
218 Type m_handle{ HandleTraits::invalidValue() };
219};
220
221// clang-format on
222
223QT_END_NAMESPACE
224
225#endif
226

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/corelib/tools/quniquehandle_p.h