diff options
| author | William A. Kennington III <wak@google.com> | 2018-11-02 17:28:35 -0700 |
|---|---|---|
| committer | William A. Kennington III <wak@google.com> | 2019-03-29 21:14:57 +0000 |
| commit | 7a5e23259a696bf240a14200535680b42c445096 (patch) | |
| tree | 9553542506b77601755b0f548c2fe48031198a62 /src | |
| parent | 1937ef65b505b1d856c74074d6042bdb336728af (diff) | |
| download | stdplus-7a5e23259a696bf240a14200535680b42c445096.tar.gz stdplus-7a5e23259a696bf240a14200535680b42c445096.zip | |
handle/managed: Implement non-copyable handle
This is a generic handle type that holds a resource and uses RAII to
call a user defined function when the resource is destroyed. A future
change will implement a smart file descriptor based on this interface.
A follow up change will implement the copyable version.
Tested:
Built and run through unit tests.
Change-Id: Ia8da1d662319e8fb58380ed4979bcf1b74f66dfb
Signed-off-by: William A. Kennington III <wak@google.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/meson.build | 4 | ||||
| -rw-r--r-- | src/stdplus/handle/managed.hpp | 183 |
3 files changed, 189 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 5e935f4..57af82a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,5 +4,7 @@ lib_LTLIBRARIES = libstdplus.la libstdplus_la_SOURCES = libstdplus_la_LIBADD = $(COMMON_LIBS) +nobase_include_HEADERS += stdplus/handle/managed.hpp + nobase_include_HEADERS += stdplus/signal.hpp libstdplus_la_SOURCES += stdplus/signal.cpp diff --git a/src/meson.build b/src/meson.build index fd56941..6b1fea9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -18,3 +18,7 @@ import('pkgconfig').generate( install_headers( 'stdplus/signal.hpp', subdir: 'stdplus') + +install_headers( + 'stdplus/handle/managed.hpp', + subdir: 'stdplus/handle') diff --git a/src/stdplus/handle/managed.hpp b/src/stdplus/handle/managed.hpp new file mode 100644 index 0000000..361c1ec --- /dev/null +++ b/src/stdplus/handle/managed.hpp @@ -0,0 +1,183 @@ +#pragma once +#include <cstdlib> +#include <optional> +#include <tuple> +#include <utility> + +namespace stdplus +{ + +/** @brief An RAII handle which takes an object and calls a user specified + * function on the object when it should be cleaned up. + * @details This is useful for adding RAII semantics to non-RAII things like + * file descriptors, or c structs allocated with special routines. + * We could make a simple file descriptor wrapper that is + * automatically closed: + * + * void closefd(int&& fd) { close(fd); } + * using Fd = Managed<int>::Handle<closefd>; + * + * void some_func() + * { + * Fd fd(open("somefile", 0)); + * char buf[4096]; + * int amt = read(*fd, buf, sizeof(buf)); + * printf("%.*s\n", amt, data); + * } + */ +template <typename T, typename... As> +struct Managed +{ + template <void (*drop)(T&&, As&...)> + class Handle + { + public: + /** @brief Creates a handle owning the object + * + * @param[in] maybeV - Maybe the object being managed + */ + template <typename... Vs> + constexpr explicit Handle(std::optional<T>&& maybeV, + Vs&&... vs) noexcept : + as(std::forward<Vs>(vs)...), + maybeT(std::move(maybeV)) + { + } + template <typename... Vs> + constexpr explicit Handle(T&& maybeV, Vs&&... vs) noexcept : + as(std::forward<Vs>(vs)...), maybeT(std::move(maybeV)) + { + } + + Handle(const Handle& other) = delete; + Handle& operator=(const Handle& other) = delete; + + constexpr Handle(Handle&& other) : + as(std::move(other.as)), maybeT(std::move(other.maybeT)) + { + other.maybeT = std::nullopt; + } + + constexpr Handle& operator=(Handle&& other) + { + if (this != &other) + { + reset(std::move(other.maybeT)); + as = std::move(other.as); + other.maybeT = std::nullopt; + } + return *this; + } + + virtual ~Handle() + { + try + { + reset(); + } + catch (...) + { + std::abort(); + } + } + + /** @brief Gets the managed object + * + * @return A pointer to the object + */ + constexpr const T* operator->() const noexcept + { + return &(*maybeT); + } + + /** @brief Gets the managed object + * + * @return A reference to the object + */ + constexpr const T& operator*() const& noexcept + { + return *maybeT; + } + + /** @brief Determine if we are managing an object + * + * @return Do we currently have an object + */ + constexpr explicit operator bool() const noexcept + { + return static_cast<bool>(maybeT); + } + + /** @brief Determine if we are managing an object + * + * @return Do we currently have an object + */ + constexpr bool has_value() const noexcept + { + return maybeT.has_value(); + } + + /** @brief Gets the managed object + * + * @throws std::bad_optional_access if it has no object + * @return A reference to the managed object + */ + constexpr const T& value() const& + { + return maybeT.value(); + } + + /** @brief Gets the managed object if it exists + * + * @throws std::bad_optional_access if it has no object + * @return A reference to the managed object + */ + constexpr const std::optional<T>& maybe_value() const& noexcept + { + return maybeT; + } + + /** @brief Resets the managed value to a new value + * The container takes ownership of the value + * + * @param[in] maybeV - Maybe the new value + */ + constexpr void reset(std::optional<T>&& maybeV) + { + maybeDrop(std::index_sequence_for<As...>()); + maybeT = std::move(maybeV); + } + constexpr void reset(T&& maybeV) + { + maybeDrop(std::index_sequence_for<As...>()); + maybeT = std::move(maybeV); + } + + /** @brief A shorthand reset function for convenience + * Same as calling reset(std::nullopt) + */ + constexpr void reset() + { + reset(std::nullopt); + } + + protected: + /* Hold the data parameterized for this container */ + std::tuple<As...> as; + + private: + /* Stores the managed object if we have one */ + std::optional<T> maybeT; + + template <size_t... Indices> + void maybeDrop(std::index_sequence<Indices...>) + { + if (maybeT) + { + drop(std::move(*maybeT), std::get<Indices>(as)...); + } + } + }; +}; + +} // namespace stdplus |

