summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libadhocutil/handle.h88
-rw-r--r--libadhocutil/unittests/Jamfile.jam8
-rw-r--r--libadhocutil/unittests/testHandle.cpp60
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);
+}
+