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 +#include +#include +#include +#include + +namespace nix { + +/** A pointer that's like Rust's Box: forwards comparisons to the inner class and is non-null */ +template +// FIXME: add custom deleter support +class box_ptr +{ + std::unique_ptr inner; + + template + friend class box_ptr; + + explicit box_ptr(std::unique_ptr p) + : inner(std::move(p)) + { + assert(inner != nullptr); + } + +public: + using pointer = typename std::unique_ptr::pointer; + + inline typename std::add_lvalue_reference::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 unsafeFromNonnull(std::unique_ptr p) + { + return box_ptr(std::move(p)); + } + + inline box_ptr & operator=(box_ptr && other) noexcept = default; + + // No copy for you. + box_ptr & operator=(const box_ptr &) = 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 && other) = default; + + /** Conversion operator */ + template + // n.b. the requirements here are the same as libstdc++ unique_ptr's checks but with concepts + requires std::convertible_to::pointer, pointer> &&(!std::is_array_v) + box_ptr(box_ptr && other) noexcept + : inner(std::move(other.inner)) + { + other.inner = nullptr; + } + + box_ptr(box_ptr & other) = delete; +}; + +template +requires std::equality_comparable +bool operator==(box_ptr const & x, box_ptr 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 +requires std::equality_comparable +bool operator!=(box_ptr const & x, box_ptr const & y) +{ + return *x != *y; +} + +#define MAKE_COMPARISON(OP) \ + template \ + requires std::totally_ordered \ + bool operator OP(box_ptr const & x, box_ptr const & y) \ + { \ + return *x OP * y; \ + } + +MAKE_COMPARISON(<); +MAKE_COMPARISON(>); +MAKE_COMPARISON(>=); +MAKE_COMPARISON(<=); + +#undef MAKE_COMPARISON + +template +requires std::three_way_comparable std::compare_three_way_result_t +operator<=>(box_ptr const & x, box_ptr const & y) +{ + return *x <=> *y; +} + +template +inline box_ptr make_box_ptr(Args &&... args) +{ + return box_ptr::unsafeFromNonnull(std::make_unique(std::forward(args)...)); +} +};