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); +} +  | 
