Simple-Utility v2.3.1
Loading...
Searching...
No Matches
unique_handle.hpp
Go to the documentation of this file.
1// Copyright Dominic Koepke 2019 - 2023.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at
4// https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef SL_UNIQUE_HANDLE_HPP
7#define SL_UNIQUE_HANDLE_HPP
8
9#pragma once
10
11#include <compare>
12#include <concepts>
13#include <functional>
14#include <optional>
15#include <utility>
16
20
21// some of the std::optional interface hasn't been declared constexpr before
22#if __cpp_lib_optional >= 202106L
23#define SL_UNIQUE_HANDLE_FULL_CONSTEXPR constexpr
24#else
25#define SL_UNIQUE_HANDLE_FULL_CONSTEXPR
26#endif
27
28namespace sl
29{
106 // ReSharper disable once IdentifierTypo
108 { };
109
113 // ReSharper disable once IdentifierTypo
115
119 using bad_handle_access = std::bad_optional_access;
120
125 {
129 constexpr void operator ()(auto&) const noexcept
130 { }
131 };
132
133 namespace detail
134 {
135 template <class T>
136 struct value_validator
137 {
138 static_assert(std::movable<T>, "The value type must be movable.");
139 static_assert(concepts::not_same_as<std::remove_cvref_t<T>, nullhandle_t>, "The value type must not be sl::nullhandle_t.");
140
141 using type = T;
142 };
143
144 template <class T, class TDeleteAction>
145 struct delete_action_validator
146 {
147 static_assert(std::copyable<TDeleteAction>, "The delete action object must be copyable (capturing lambdas are not).");
148 static_assert(std::invocable<TDeleteAction, T&>, "The delete action object must be invokable by T&.");
149
150 using type = TDeleteAction;
151 };
152
153 template <class T>
154 using type_t = typename T::type;
155 }
156
160 template <class T>
161 concept value = std::movable<T> && concepts::not_same_as<T, nullhandle_t>;
162
166 template <class T, class TValue>
168 && std::invocable<T, TValue&>
169 && std::copyable<T>;
170
177 template <value T, delete_action_for<T> TDeleteAction = default_delete_action>
179 {
180 private:
181 template <value T2, delete_action_for<T2> TOtherDeleteAction>
182 friend class unique_handle;
183
184 public:
188 using value_type = T;
192 using delete_action_type = TDeleteAction;
193
197 constexpr unique_handle() noexcept = default;
198
202 constexpr ~unique_handle() noexcept
203 {
204 invoke_delete_action_if_necessary();
205 }
206
212 (
213 unique_handle&& other
214 ) noexcept(std::is_nothrow_move_constructible_v<T>
215 && std::is_nothrow_copy_constructible_v<TDeleteAction>)
216 : m_Value{ std::exchange(other.m_Value, std::nullopt) },
217 m_DeleteAction{ other.m_DeleteAction }
218 { }
219
226 (
227 unique_handle&& other
228 ) noexcept(std::is_nothrow_move_constructible_v<T>
229 && std::is_nothrow_move_assignable_v<T>
230 && std::is_nothrow_copy_constructible_v<TDeleteAction>
231 && std::is_nothrow_copy_assignable_v<TDeleteAction>)
232 {
233 if (this != &other)
234 {
235 invoke_delete_action_if_necessary();
236
237 m_Value = std::exchange(other.m_Value, std::nullopt);
238 m_DeleteAction = other.m_DeleteAction;
239 }
240 return *this;
241 }
242
247 constexpr void swap
248 (
249 unique_handle& other
250 ) noexcept(std::is_nothrow_move_constructible_v<T>
251 && std::is_nothrow_swappable_v<T>
252 && std::is_nothrow_move_constructible_v<TDeleteAction>
253 && std::is_nothrow_swappable_v<TDeleteAction>)
254 {
255 using std::swap;
256
257 swap(m_Value, other.m_Value);
258 swap(m_DeleteAction, other.m_DeleteAction);
259 }
260
264 unique_handle(const unique_handle&) = delete;
270
276 constexpr unique_handle(nullhandle_t, const delete_action_type& deleteAction = delete_action_type()) noexcept
277 : m_Value{ std::nullopt },
278 m_DeleteAction{ deleteAction }
279 { }
280
285 {
286 invoke_delete_action_if_necessary();
287
288 m_Value = std::nullopt;
289 return *this;
290 }
291
296 constexpr unique_handle(const delete_action_type& deleteAction) noexcept
297 : m_Value{ std::nullopt },
298 m_DeleteAction{ deleteAction }
299 { }
300
307 template <concepts::initializes<T> T2>
308 requires concepts::not_same_as<std::remove_cvref_t<T2>, unique_handle>
309 && concepts::not_same_as<std::remove_cvref_t<T2>, nullhandle_t>
310 explicit (!std::convertible_to<T2&&, T>)
311 constexpr unique_handle(T2&& value, const delete_action_type& deleteAction = delete_action_type{})
312 : m_Value{ std::forward<T2>(value) },
313 m_DeleteAction{ deleteAction }
314 { }
315
322 template <concepts::assignable_to<T&> T2>
323 requires concepts::not_same_as<std::remove_cvref_t<T2>, unique_handle>
324 && concepts::not_same_as<std::remove_cvref_t<T2>, nullhandle_t>
325 && concepts::initializes<std::remove_cvref_t<T2>, T>
327 {
328 invoke_delete_action_if_necessary();
329
330 m_Value = std::forward<T2>(value);
331 return *this;
332 }
333
339 template <class... TArgs>
340 requires std::constructible_from<T, TArgs...>
341 constexpr explicit unique_handle(std::in_place_t, TArgs&&... args)
342 : m_Value{ std::in_place, std::forward<TArgs>(args)... }
343 { }
344
352 template <class... TArgs>
353 requires std::constructible_from<T, TArgs...>
354 constexpr explicit unique_handle(std::in_place_t, const delete_action_type& deleteAction, TArgs&&... args)
355 : m_Value{ std::in_place, std::forward<TArgs>(args)... },
356 m_DeleteAction{ deleteAction }
357 { }
358
364 template <class... TArgs>
365 requires std::constructible_from<T, TArgs...>
367 {
368 m_Value.emplace(std::forward<TArgs>(args)...);
369 }
370
375 {
376 invoke_delete_action_if_necessary();
377
378 m_Value.reset();
379 }
380
386 [[nodiscard]]
387 constexpr const T& raw() const { return m_Value.value(); }
388
393 [[nodiscard]]
394 constexpr const T& operator *() const noexcept { return *m_Value; }
395
400 [[nodiscard]]
401 constexpr const T* operator ->() const noexcept { return &*m_Value; }
402
407 [[nodiscard]]
408 constexpr explicit operator bool() const noexcept { return m_Value.has_value(); }
409
414 [[nodiscard]]
415 constexpr bool is_valid() const noexcept { return m_Value.has_value(); }
416
421 [[nodiscard]]
422 constexpr const delete_action_type& delete_action() const noexcept { return m_DeleteAction; }
423
432 template <std::equality_comparable_with<T> T2, class TOtherDeleteAction>
433 [[nodiscard]]
434 constexpr bool operator ==(const unique_handle<T2, TOtherDeleteAction>& other) const
435 {
436 return m_Value == other.m_Value;
437 }
438
447 template <std::three_way_comparable_with<T> T2, class TOtherDeleteAction>
448 [[nodiscard]]
449 constexpr std::compare_three_way_result_t<T, T2> operator <=>(const unique_handle<T2, TOtherDeleteAction>& other) const
450 {
451 return m_Value <=> other.m_Value;
452 }
453
460 template <class T2>
462 // do not move into template arg list, as this will result in a template cycle
463 && std::three_way_comparable_with<T, T2>
464 [[nodiscard]]
465 constexpr std::compare_three_way_result_t<T, T2> operator <=>(const T2& other) const
466 {
467 return m_Value <=> other;
468 }
469
476 template <class T2>
478 // do not move into template arg list, as this will result in a template cycle
479 && std::equality_comparable_with<T, T2>
480 [[nodiscard]]
481 constexpr bool operator ==(const T2& other) const
482 {
483 return m_Value == other;
484 }
485
490 [[nodiscard]]
491 constexpr bool operator ==(nullhandle_t) const noexcept
492 {
493 return !is_valid();
494 }
495
500 [[nodiscard]]
501 constexpr std::strong_ordering operator <=>(nullhandle_t) const noexcept
502 {
503 return is_valid() <=> false;
504 }
505
506 private:
507 std::optional<T> m_Value{};
508
510 TDeleteAction m_DeleteAction{};
511
512 constexpr void invoke_delete_action_if_necessary() noexcept
513 {
514 if (m_Value)
515 std::invoke(m_DeleteAction, *m_Value);
516 }
517 };
518
525 template <class T, class TDeleteAction>
527 (
528 T,
529 TDeleteAction
530 ) -> unique_handle<detail::type_t<detail::value_validator<T>>, detail::type_t<detail::delete_action_validator<T, TDeleteAction>>>;
531
537 template <class T>
539
546 template <class T, class TDeleteAction>
547 struct nullables::traits<unique_handle<T, TDeleteAction>>
548 {
550 static constexpr nullhandle_t null{ nullhandle };
551 };
552
554}
555
556#endif
#define SL_UTILITY_NO_UNIQUE_ADDRESS
Definition: Config.hpp:21
This type models some kind of std::optional behaviour but resets itself on move operations.
Definition: unique_handle.hpp:179
SL_UNIQUE_HANDLE_FULL_CONSTEXPR void reset() noexcept
Resets the value and invokes the delete action if value was initialized.
Definition: unique_handle.hpp:374
TDeleteAction delete_action_type
Type of the used delete action.
Definition: unique_handle.hpp:192
constexpr unique_handle() noexcept=default
Default constructor. The value will be in an uninitialized stated and the delete action gets default ...
constexpr unique_handle(nullhandle_t, const delete_action_type &deleteAction=delete_action_type()) noexcept
Explicitly does not initialize the value. This overload is mainly used for convenience when returning...
Definition: unique_handle.hpp:276
constexpr const T * operator->() const noexcept
Immutable access to the value. No checks will be performed.
Definition: unique_handle.hpp:401
T value_type
Type of the stored value.
Definition: unique_handle.hpp:188
constexpr bool operator==(const unique_handle< T2, TOtherDeleteAction > &other) const
Equality-comparison operator overload between two unique_handles. operator != is implicitly defined.
Definition: unique_handle.hpp:434
friend class unique_handle
Definition: unique_handle.hpp:182
constexpr bool is_valid() const noexcept
Checks whether the value is initialized.
Definition: unique_handle.hpp:415
constexpr void swap(unique_handle &other) noexcept(std::is_nothrow_move_constructible_v< T > &&std::is_nothrow_swappable_v< T > &&std::is_nothrow_move_constructible_v< TDeleteAction > &&std::is_nothrow_swappable_v< TDeleteAction >)
Swaps the target and the source in a more efficient way.
Definition: unique_handle.hpp:248
constexpr unique_handle(const delete_action_type &deleteAction) noexcept
Constructor overload for explicitly providing a delete action.
Definition: unique_handle.hpp:296
constexpr std::compare_three_way_result_t< T, T2 > operator<=>(const unique_handle< T2, TOtherDeleteAction > &other) const
Three-way-comparison operator overload between two unique_handles.
Definition: unique_handle.hpp:449
unique_handle(const unique_handle &)=delete
Explicitly deleted copy constructor.
SL_UNIQUE_HANDLE_FULL_CONSTEXPR unique_handle(unique_handle &&other) noexcept(std::is_nothrow_move_constructible_v< T > &&std::is_nothrow_copy_constructible_v< TDeleteAction >)
Move constructor, which relocates the ownership of the value to the target and resets the source....
Definition: unique_handle.hpp:212
constexpr unique_handle(std::in_place_t, TArgs &&... args)
Constructor overload for directly initializing the value with a set of arguments.
Definition: unique_handle.hpp:341
constexpr unique_handle(std::in_place_t, const delete_action_type &deleteAction, TArgs &&... args)
Constructor overload for directly initializing the value with a set of arguments and also initializin...
Definition: unique_handle.hpp:354
constexpr const T & operator*() const noexcept
Immutable access to the value. No checks will be performed.
Definition: unique_handle.hpp:394
constexpr const delete_action_type & delete_action() const noexcept
Immutable access to the delete action.
Definition: unique_handle.hpp:422
constexpr const T & raw() const
Immutable access to the value. No checks will be performed.
Definition: unique_handle.hpp:387
SL_UNIQUE_HANDLE_FULL_CONSTEXPR void emplace(TArgs &&... args)
Constructor overload for directly initializing the value with a set of arguments.
Definition: unique_handle.hpp:366
!std ::convertible_to< T2 &&, T > constexpr unique_handle(T2 &&value, const delete_action_type &deleteAction=delete_action_type{})
Constructor overload for initializing the value.
Definition: unique_handle.hpp:311
SL_UNIQUE_HANDLE_FULL_CONSTEXPR unique_handle & operator=(unique_handle &&other) noexcept(std::is_nothrow_move_constructible_v< T > &&std::is_nothrow_move_assignable_v< T > &&std::is_nothrow_copy_constructible_v< TDeleteAction > &&std::is_nothrow_copy_assignable_v< TDeleteAction >)
Move assignment, which relocates the ownership of the value to the target and resets the source....
Definition: unique_handle.hpp:226
Checks whether the left-hand-side type is unequal to the right-hand-side type.
Definition: stl_extensions.hpp:69
Checks whether the given template type is usable as delete action type for unique_handle types.
Definition: unique_handle.hpp:167
Checks whether the given template type is usable as value type for unique_handle types.
Definition: unique_handle.hpp:161
unique_handle(T) -> unique_handle< T >
Deduction guide for unique_handle class.
constexpr nullhandle_t nullhandle
an object of type nullhandle_t
Definition: unique_handle.hpp:114
std::bad_optional_access bad_handle_access
exception type which indicates checked access to an uninitialized value
Definition: unique_handle.hpp:119
unique_handle(T, TDeleteAction) -> unique_handle< detail::type_t< detail::value_validator< T > >, detail::type_t< detail::delete_action_validator< T, TDeleteAction > > >
Deduction guide for unique_handle class.
Definition: operators.hpp:15
default delete action for unique_handle with an empty operator ()
Definition: unique_handle.hpp:125
constexpr void operator()(auto &) const noexcept
Empty invoke operator.
Definition: unique_handle.hpp:129
typename unique_handle< T, TDeleteAction >::value_type value_type
Definition: unique_handle.hpp:549
The main trait, which may be specialized from.
Definition: base.hpp:97
helper type for indicating unique_handles with uninitialized state
Definition: unique_handle.hpp:108
#define SL_UNIQUE_HANDLE_FULL_CONSTEXPR
Definition: unique_handle.hpp:25