|
13 | 13 | #define OPENVPN_COMMON_CLEANUP_H |
14 | 14 |
|
15 | 15 | #include <utility> |
| 16 | +#include <optional> |
| 17 | +#include <concepts> |
| 18 | +#include <functional> |
| 19 | +#include <type_traits> |
16 | 20 |
|
17 | 21 | namespace openvpn { |
18 | 22 |
|
| 23 | +/** |
| 24 | + * @brief A scope guard that runs a callable on destruction unless dismissed or released. |
| 25 | + * @tparam F The type of the callable to run on destruction. |
| 26 | + * @details |
| 27 | + * The Cleanup class template provides a mechanism to ensure that a specified callable |
| 28 | + * is executed when the Cleanup object goes out of scope, unless it has been dismissed |
| 29 | + * or released. This is useful for resource management and cleanup tasks that need to |
| 30 | + * be performed when exiting a scope. |
| 31 | + * @todo Keep std::scope_exit in mind when moving to C++23. |
| 32 | + */ |
19 | 33 | template <typename F> |
| 34 | + requires std::invocable<F> |
20 | 35 | class CleanupType |
21 | 36 | { |
22 | 37 | public: |
23 | | - CleanupType(F method) noexcept |
| 38 | + explicit CleanupType(F method) noexcept(std::is_nothrow_move_constructible_v<F>) |
24 | 39 | : clean(std::move(method)) |
25 | 40 | { |
26 | 41 | } |
| 42 | + CleanupType(const CleanupType &) = delete; |
| 43 | + CleanupType &operator=(const CleanupType &) = delete; |
| 44 | + CleanupType(CleanupType &&other) noexcept |
| 45 | + : clean(std::exchange(other.clean, std::nullopt)) |
| 46 | + { |
| 47 | + } |
| 48 | + CleanupType &operator=(CleanupType &&other) noexcept |
| 49 | + { |
| 50 | + if (this != &other) |
| 51 | + { |
| 52 | + clean = std::exchange(other.clean, std::nullopt); |
| 53 | + } |
| 54 | + return *this; |
| 55 | + } |
27 | 56 |
|
28 | | - CleanupType(CleanupType &&) = default; |
| 57 | + /** |
| 58 | + * @brief Destructor that executes the cleanup action if not dismissed or released. |
| 59 | + * @details When the CleanupType object is destroyed, if the cleanup action has not |
| 60 | + * been dismissed or released, it will be invoked. Any exceptions thrown by the |
| 61 | + * cleanup action are caught and swallowed to prevent exceptions from escaping the |
| 62 | + * destructor. |
| 63 | + */ |
| 64 | + ~CleanupType() noexcept |
| 65 | + { |
| 66 | + if (clean) |
| 67 | + { |
| 68 | + try |
| 69 | + { |
| 70 | + std::invoke(*clean); |
| 71 | + } |
| 72 | + catch (...) |
| 73 | + { |
| 74 | + // swallow exceptions to avoid throwing from destructor |
| 75 | + } |
| 76 | + } |
| 77 | + } |
29 | 78 |
|
30 | | - ~CleanupType() |
| 79 | + public: |
| 80 | + /** |
| 81 | + * @brief Dismiss the cleanup action, preventing it from being executed on destruction. |
| 82 | + * @details After calling dismiss, the cleanup action will not be invoked when the |
| 83 | + * CleanupType object is destroyed. |
| 84 | + */ |
| 85 | + void dismiss() noexcept |
| 86 | + { |
| 87 | + clean.reset(); |
| 88 | + } |
| 89 | + /** |
| 90 | + * @brief Release the cleanup action, returning the callable and preventing it from being |
| 91 | + * executed on destruction. |
| 92 | + * @return An optional containing the callable if it was set, or std::nullopt if it was |
| 93 | + * dismissed or already released. |
| 94 | + * @details After calling release, the cleanup action will not be invoked when the |
| 95 | + * CleanupType object is destroyed, and the caller takes ownership of the callable. This |
| 96 | + * allows the caller to manage the callable's lifetime independently, including calling it |
| 97 | + * earlier or later than it might be otherwise called. |
| 98 | + */ |
| 99 | + std::optional<std::function<void()>> release() noexcept |
31 | 100 | { |
32 | | - clean(); |
| 101 | + if (clean) |
| 102 | + { |
| 103 | + std::function<void()> func = std::move(*clean); |
| 104 | + clean.reset(); |
| 105 | + return func; |
| 106 | + } |
| 107 | + return std::nullopt; |
33 | 108 | } |
34 | 109 |
|
35 | 110 | private: |
36 | | - CleanupType(const CleanupType &) = delete; |
37 | | - CleanupType &operator=(const CleanupType &) = delete; |
38 | | - |
39 | | - F clean; |
| 111 | + std::optional<F> clean; |
40 | 112 | }; |
41 | 113 |
|
| 114 | +/** |
| 115 | + * @brief Factory function to create a CleanupType object. |
| 116 | + * @tparam F The type of the callable to run on destruction. |
| 117 | + * @param method The callable to be executed on destruction. |
| 118 | + * @return A CleanupType object that will execute the callable on destruction. |
| 119 | + * @details This function simplifies the creation of CleanupType objects by deducing |
| 120 | + the type of the callable and forwarding it to the CleanupType constructor. |
| 121 | + */ |
42 | 122 | template <typename F> |
43 | | -inline CleanupType<F> Cleanup(F method) noexcept |
| 123 | + requires std::invocable<F> |
| 124 | +inline CleanupType<F> Cleanup(F method) noexcept(std::is_nothrow_move_constructible_v<F>) |
44 | 125 | { |
45 | 126 | return CleanupType<F>(std::move(method)); |
46 | 127 | } |
|
0 commit comments