#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)...)); } };