From eb957de7f539c3f06df10028f09a58c4017ba4f0 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 19 Feb 2020 23:42:04 +0000 Subject: Add Handle, like unique_ptr but for non-pointers --- libadhocutil/handle.h | 88 +++++++++++++++++++++++++++++++++++ libadhocutil/unittests/Jamfile.jam | 8 ++++ libadhocutil/unittests/testHandle.cpp | 60 ++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 libadhocutil/handle.h create mode 100644 libadhocutil/unittests/testHandle.cpp diff --git a/libadhocutil/handle.h b/libadhocutil/handle.h new file mode 100644 index 0000000..7363183 --- /dev/null +++ b/libadhocutil/handle.h @@ -0,0 +1,88 @@ +#ifndef ADHOCUTIL_HANDLE_H +#define ADHOCUTIL_HANDLE_H + +#include +#include "c++11Helpers.h" + +namespace AdHoc { + /// A unique_ptr like construct for non-pointer objects. + /// Implements RAII. + template + class Handle { + public: + /// Constructs a Handle that owns t, to be tidied with d + Handle(T t, D d) noexcept : + inst(std::move(t)), + deleter(std::move(d)), + owning(true) + { + } + + /// Constructs a Handle that takes over ownership of h + Handle(Handle && h) noexcept : + inst(std::move(h.inst)), + deleter(std::move(h.deleter)), + owning(h.owning) + { + h.owning = false; + } + + ~Handle() + { + if (owning) { + deleter(inst); + } + } + + /// Standard special members + SPECIAL_MEMBERS_COPY(Handle, delete); + + /// Takes over ownership of h + Handle & operator=(Handle && h) noexcept + { + if (owning) { + deleter(inst); + } + inst = std::move(h.inst); + deleter = std::move(h.deleter); + owning = h.owning; + h.owning = false; + } + + /// Returns a reference to the managed object. + [[nodiscard]] T & get() noexcept { return inst; } + /// Returns a const reference to the managed object. + [[nodiscard]] const T & get() const noexcept { return inst; } + + /// Returns a pointer to the managed object. + [[nodiscard]] T * operator->() noexcept { return inst; } + /// Returns a const pointer to the managed object. + [[nodiscard]] const T * operator->() const noexcept { return inst; } + + /// Returns a reference to the managed object. + [[nodiscard]] T & operator*() noexcept { return inst; } + /// Returns a const reference to the managed object. + [[nodiscard]] const T & operator*() const noexcept { return inst; } + + /// Returns a reference to the managed object. + // NOLINTNEXTLINE(hicpp-explicit-conversions) + operator T &() noexcept { return inst; } + /// Returns a const reference to the managed object. + // NOLINTNEXTLINE(hicpp-explicit-conversions) + operator const T &() const noexcept { return inst; } + + private: + T inst; + D deleter; + bool owning; + }; + + template + Handle make_handle(D && d, Args && ... args) + { + return { T(std::forward(args)...), std::forward(d) }; + } +} + +#endif + diff --git a/libadhocutil/unittests/Jamfile.jam b/libadhocutil/unittests/Jamfile.jam index 642ff9b..d62f6da 100644 --- a/libadhocutil/unittests/Jamfile.jam +++ b/libadhocutil/unittests/Jamfile.jam @@ -304,3 +304,11 @@ run boost_utf ; +run + testHandle.cpp + : : : + BOOST_TEST_DYN_LINK + ..//adhocutil + boost_utf + ; + diff --git a/libadhocutil/unittests/testHandle.cpp b/libadhocutil/unittests/testHandle.cpp new file mode 100644 index 0000000..e7f52af --- /dev/null +++ b/libadhocutil/unittests/testHandle.cpp @@ -0,0 +1,60 @@ +#define BOOST_TEST_MODULE Handle +#include + +#include +#include +#include "handle.h" + +// Test case base on a file handle +using T = decltype(STDIN_FILENO); +using D = decltype(&close); +static_assert(std::is_trivially_move_constructible_v); +static_assert(std::is_trivially_move_constructible_v); + +using TestHandle = AdHoc::Handle; + +static_assert(std::is_nothrow_move_constructible_v); +static_assert(std::is_nothrow_move_assignable_v); +static_assert(!std::is_copy_constructible_v); +static_assert(!std::is_copy_assignable_v); + +BOOST_AUTO_TEST_CASE(values) +{ + TestHandle th { open("/dev/null", O_RDWR), &close }; + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); + BOOST_REQUIRE_EQUAL(1, write(th, "1", 1)); +} + +BOOST_AUTO_TEST_CASE(values_const) +{ + const TestHandle cth { open("/dev/null", O_RDWR), &close }; + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); + BOOST_REQUIRE_EQUAL(1, write(cth, "1", 1)); +} + +BOOST_AUTO_TEST_CASE(make) +{ + bool closed = false; + int fd = -2; + const auto doClose = [&](T lfd) { + BOOST_CHECK(!closed); + BOOST_CHECK_EQUAL(lfd, fd); + closed = true; + return close(lfd); + }; + { + auto th = AdHoc::make_handle(doClose, open("/dev/null", O_RDWR)); + BOOST_REQUIRE(!closed); + BOOST_REQUIRE_EQUAL(1, write(th, "1", 1)); + auto th2 = std::move(th); + BOOST_REQUIRE(!closed); + BOOST_REQUIRE_EQUAL(1, write(th2, "1", 1)); + fd = th2; + } + BOOST_REQUIRE(closed); + BOOST_REQUIRE_EQUAL(-1, write(fd, "1", 1)); + BOOST_REQUIRE_EQUAL(errno, EBADF); +} + -- cgit v1.2.3