diff options
-rw-r--r-- | libadhocutil/handle.h | 88 | ||||
-rw-r--r-- | libadhocutil/unittests/Jamfile.jam | 8 | ||||
-rw-r--r-- | libadhocutil/unittests/testHandle.cpp | 60 |
3 files changed, 156 insertions, 0 deletions
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 <utility> +#include "c++11Helpers.h" + +namespace AdHoc { + /// A unique_ptr like construct for non-pointer objects. + /// Implements RAII. + template<typename T, typename D> + 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<typename T, typename D, typename... Args> + Handle<T, D> make_handle(D && d, Args && ... args) + { + return { T(std::forward<Args>(args)...), std::forward<D>(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 <library>boost_utf ; +run + testHandle.cpp + : : : + <define>BOOST_TEST_DYN_LINK + <library>..//adhocutil + <library>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 <boost/test/unit_test.hpp> + +#include <type_traits> +#include <fcntl.h> +#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<T>); +static_assert(std::is_trivially_move_constructible_v<D>); + +using TestHandle = AdHoc::Handle<T, D>; + +static_assert(std::is_nothrow_move_constructible_v<TestHandle>); +static_assert(std::is_nothrow_move_assignable_v<TestHandle>); +static_assert(!std::is_copy_constructible_v<TestHandle>); +static_assert(!std::is_copy_assignable_v<TestHandle>); + +BOOST_AUTO_TEST_CASE(values) +{ + TestHandle th { open("/dev/null", O_RDWR), &close }; + static_assert(std::is_same_v<T, std::remove_reference_t<decltype(th.get())>>); + static_assert(std::is_same_v<T, std::remove_reference_t<decltype(*th)>>); + 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<const T, std::remove_reference_t<decltype(cth.get())>>); + static_assert(std::is_same_v<const T, std::remove_reference_t<decltype(*cth)>>); + 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<T>(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); +} + |