Repositories / jai.git

defer.h

Clone (read-only): git clone http://git.guha-anderson.com/git/jai.git

Branch
2544 bytes · 4952f028c74a
// -*-C++-*- #pragma once #include "argtype.h" #include "move_only_function.h" #include <concepts> #include <functional> #include <utility> template<typename T, typename... Ts> concept is_one_of = (std::same_as<T, Ts> || ...); // Note that Destroy generally should not throw, whether or not it is // declared noexcept. Only an explicit call to reset() will allow // exceptions to propagate. // // auto(Destroy) avoids warnings about ignored attributes on closedir. template<auto Destroy, typename T = UnaryType<decltype(auto(Destroy))>, auto Empty = T{}> struct RaiiHelper { T t_ = Empty; constexpr RaiiHelper() noexcept = default; RaiiHelper(T t) noexcept : t_(std::move(t)) {} RaiiHelper(RaiiHelper &&other) noexcept : t_(other.release()) {} ~RaiiHelper() { reset(); } template<typename Arg> requires is_one_of<std::decay_t<Arg>, T, decltype(Empty)> RaiiHelper &operator=(Arg &&arg) noexcept { reset(std::forward<Arg>(arg)); return *this; } RaiiHelper &operator=(RaiiHelper &&other) noexcept { return *this = other.release(); } explicit operator bool() const noexcept { return t_ != Empty; } T &operator*() & noexcept { return t_; } const T &operator*() const & noexcept { return t_; } T &&operator*() && noexcept { return std::move(t_); } const T &&operator*() const && noexcept { return std::move(t_); } // For legacy libraries that want a T**, return that type for & template<std::same_as<T> U = T> requires std::is_pointer_v<U> auto operator&() noexcept { return std::addressof(t_); } // Make it easier to use RaiiHelper with pointers in C libraries template<std::same_as<T> U = T> requires std::is_pointer_v<U> operator U() const { return t_; } T operator->() const noexcept requires std::is_pointer_v<T> { return t_; } T release() noexcept { return std::exchange(t_, Empty); } template<typename Arg> requires is_one_of<std::decay_t<Arg>, T, decltype(Empty)> void reset(Arg &&arg) noexcept(noexcept(Destroy(std::move(t_)))) { if (auto destroy_me = std::exchange(t_, std::forward<Arg>(arg)); destroy_me != Empty) Destroy(std::move(destroy_me)); } void reset() noexcept(noexcept(reset(Empty))) { reset(Empty); } }; namespace detail { struct NullaryInvoker { template<typename F> static decltype(auto) operator()(F &&f) { return std::forward<F>(f)(); } }; } // namespace detail // Deferred cleanup action using Defer = RaiiHelper<detail::NullaryInvoker{}, move_only_function<void()>, nullptr>;