diff options
Diffstat (limited to 'include/cru/base/Guard.h')
| -rw-r--r-- | include/cru/base/Guard.h | 112 |
1 files changed, 103 insertions, 9 deletions
diff --git a/include/cru/base/Guard.h b/include/cru/base/Guard.h index 6b6cf851..65e2dee4 100644 --- a/include/cru/base/Guard.h +++ b/include/cru/base/Guard.h @@ -1,7 +1,11 @@ #pragma once +#include "Base.h" +#include "cru/base/Base.h" + #include <cstdlib> #include <functional> +#include <optional> namespace cru { struct Guard { @@ -25,18 +29,23 @@ struct Guard { ExitFunc on_exit; }; -template <typename T> -struct FreeLater { - FreeLater(T* ptr) : ptr(ptr) {} - ~FreeLater() { ::free(ptr); } +/** + * FreeFunc must handle nullptr correctly. + */ +template <typename T, void (*FreeFunc)(T*) noexcept> +struct TAutoFreePtr { + TAutoFreePtr(T* ptr) : ptr(ptr) {} + ~TAutoFreePtr() { FreeFunc(ptr); } - FreeLater(const FreeLater& other) = delete; - FreeLater& operator=(const FreeLater& other) = delete; + CRU_DELETE_COPY(TAutoFreePtr) - FreeLater(FreeLater&& other) : ptr(other.ptr) { other.ptr = nullptr; } - FreeLater& operator=(FreeLater&& other) { + TAutoFreePtr(TAutoFreePtr&& other) noexcept : ptr(other.ptr) { + other.ptr = nullptr; + } + + TAutoFreePtr& operator=(TAutoFreePtr&& other) noexcept { if (this != &other) { - ::free(ptr); + FreeFunc(ptr); ptr = other.ptr; other.ptr = nullptr; } @@ -49,4 +58,89 @@ struct FreeLater { T* ptr; }; +namespace details { +template <typename T> +inline void MyFree(T* p) noexcept { + ::free(p); +} +} // namespace details + +template <typename T> +using AutoFreePtr = TAutoFreePtr<T, details::MyFree<T>>; + +template <typename T, void (*DestructFunc)(T value) noexcept> +class AutoDestruct { + public: + AutoDestruct() : value_(std::nullopt), auto_destruct_(false) {} + + AutoDestruct(T value, bool auto_destruct = true) + : value_(std::move(value)), auto_destruct_(auto_destruct) {} + + CRU_DELETE_COPY(AutoDestruct) + + AutoDestruct(AutoDestruct&& other) noexcept + : value_(std::move(other.value_)), auto_destruct_(other.auto_destruct_) { + other.value_ = std::nullopt; + other.auto_destruct_ = false; + } + + AutoDestruct& operator=(AutoDestruct&& other) noexcept { + if (this != &other) { + DoDestruct(); + value_ = other.value_; + auto_destruct_ = other.auto_destruct_; + other.value_ = std::nullopt; + other.auto_destruct_ = false; + } + return *this; + } + + ~AutoDestruct() { DoDestruct(); } + + public: + bool IsValid() const { return value_.has_value(); } + + void CheckValid( + std::optional<std::string_view> additional_message = std::nullopt) const { + if (!IsValid()) { + std::string message("AutoDestruct contains no object."); + if (additional_message) { + message += " "; + message += *additional_message; + } + throw Exception(std::move(message)); + } + } + + const T& Get() const { + CheckValid(); + return *value_; + } + + T Release() { + CheckValid(); + auto value = std::move(*value_); + value_ = std::nullopt; + auto_destruct_ = false; + return value; + } + + void Reset(std::optional<T> value = std::nullopt) { + DoDestruct(); + value_ = std::move(value); + } + + explicit operator bool() const { return value_.has_value(); } + + private: + void DoDestruct() { + if (auto_destruct_ && value_) { + DestructFunc(*value_); + } + } + + private: + std::optional<T> value_; + bool auto_destruct_; +}; } // namespace cru |
