aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjade <lix@jade.fyi>2024-03-11 15:11:15 -0600
committerGerrit Code Review <gerrit@lix>2024-03-11 15:11:15 -0600
commitd9367da02780821316b012b34017a35cc60b751a (patch)
tree30a1969c7e27a14e673f19e01a6bdfa3cbb05ca6
parentf9701da8f67a96853cf78d13ed912f8a5d6e17f0 (diff)
parentaf515baf6e4f1c5aa284e464766438b4c8fadd14 (diff)
Merge "Add box_ptr: nonnull unique_ptr with value semantics" into main
-rw-r--r--src/libutil/box_ptr.hh121
1 files changed, 121 insertions, 0 deletions
diff --git a/src/libutil/box_ptr.hh b/src/libutil/box_ptr.hh
new file mode 100644
index 000000000..f7e4fd509
--- /dev/null
+++ b/src/libutil/box_ptr.hh
@@ -0,0 +1,121 @@
+#pragma once
+/// @file
+
+#include <concepts>
+#include <memory>
+#include <stdexcept>
+#include <type_traits>
+#include <assert.h>
+
+namespace nix {
+
+/** A pointer that's like Rust's Box: forwards comparisons to the inner class and is non-null */
+template<typename T>
+// FIXME: add custom deleter support
+class box_ptr
+{
+ std::unique_ptr<T> inner;
+
+ template<typename T2>
+ friend class box_ptr;
+
+ explicit box_ptr(std::unique_ptr<T> p)
+ : inner(std::move(p))
+ {
+ assert(inner != nullptr);
+ }
+
+public:
+ using pointer = typename std::unique_ptr<T>::pointer;
+
+ inline typename std::add_lvalue_reference<T>::type operator*() const noexcept
+ {
+ return *inner.get();
+ }
+
+ inline pointer operator->() const noexcept
+ {
+ return inner.get();
+ }
+
+ inline pointer get() const noexcept
+ {
+ return inner.get();
+ }
+
+ /**
+ * Create a box_ptr from a nonnull unique_ptr.
+ */
+ static inline box_ptr<T> unsafeFromNonnull(std::unique_ptr<T> p)
+ {
+ return box_ptr(std::move(p));
+ }
+
+ inline box_ptr<T> & operator=(box_ptr<T> && other) noexcept = default;
+
+ // No copy for you.
+ box_ptr<T> & operator=(const box_ptr<T> &) = delete;
+
+ // XXX: we have to set the other's insides to nullptr, since we cannot
+ // put a garbage object there, and we don't have any compiler
+ // enforcement to not touch moved-from values. sighh.
+ box_ptr(box_ptr<T> && other) = default;
+
+ /** Conversion operator */
+ template<typename Other>
+ // n.b. the requirements here are the same as libstdc++ unique_ptr's checks but with concepts
+ requires std::convertible_to<typename box_ptr<Other>::pointer, pointer> &&(!std::is_array_v<Other>)
+ box_ptr(box_ptr<Other> && other) noexcept
+ : inner(std::move(other.inner))
+ {
+ other.inner = nullptr;
+ }
+
+ box_ptr(box_ptr<T> & other) = delete;
+};
+
+template<typename T>
+requires std::equality_comparable<T>
+bool operator==(box_ptr<T> const & x, box_ptr<T> const & y)
+{
+ // Although there could be an optimization here that compares x == y, this
+ // is unsound for floats with NaN, or anything else that violates
+ // reflexivity.
+ return *x == *y;
+}
+
+template<typename T>
+requires std::equality_comparable<T>
+bool operator!=(box_ptr<T> const & x, box_ptr<T> const & y)
+{
+ return *x != *y;
+}
+
+#define MAKE_COMPARISON(OP) \
+ template<typename T> \
+ requires std::totally_ordered<T> \
+ bool operator OP(box_ptr<T> const & x, box_ptr<T> const & y) \
+ { \
+ return *x OP * y; \
+ }
+
+MAKE_COMPARISON(<);
+MAKE_COMPARISON(>);
+MAKE_COMPARISON(>=);
+MAKE_COMPARISON(<=);
+
+#undef MAKE_COMPARISON
+
+template<typename T>
+requires std::three_way_comparable<T> std::compare_three_way_result_t<T, T>
+operator<=>(box_ptr<T> const & x, box_ptr<T> const & y)
+{
+ return *x <=> *y;
+}
+
+template<typename T, typename... Args>
+inline box_ptr<T> make_box_ptr(Args &&... args)
+{
+ return box_ptr<T>::unsafeFromNonnull(std::make_unique<T>(std::forward<Args>(args)...));
+}
+};