#ifndef RFL_REF_HPP_ #define RFL_REF_HPP_ #include #include #include "Result.hpp" namespace rfl { /// The Ref class behaves very similarly to the shared_ptr, but unlike the /// unique_ptr, it is 100% guaranteed to be filled at all times (unless the user /// tries to access it after calling std::move does something else that is /// clearly bad practice). template class Ref { public: /// The default way of creating new references is /// Ref::make(...) or make_ref(...). template static Ref make(Args&&... _args) { return Ref(std::make_shared(std::forward(_args)...)); } /// You can generate them from shared_ptrs as well, in which case it will /// return an Error, if the shared_ptr is not set. static Result> make(std::shared_ptr&& _ptr) { if (!_ptr) { return Error("std::shared_ptr was a nullptr."); } return Ref(std::move(_ptr)); } /// You can generate them from shared_ptrs as well, in which case it will /// return an Error, if the shared_ptr is not set. static Result> make(const std::shared_ptr& _ptr) { if (!_ptr) { return Error("std::shared_ptr was a nullptr."); } return Ref(_ptr); } Ref() : ptr_(std::make_shared()) {} Ref(const Ref& _other) = default; Ref(Ref&& _other) = default; template Ref(const Ref& _other) : ptr_(_other.ptr()) {} template Ref(Ref&& _other) noexcept : ptr_(std::forward>(_other.ptr())) {} ~Ref() = default; /// Returns a pointer to the underlying object T* get() const { return ptr_.get(); } /// Returns the underlying object. T& operator*() { return *ptr_; } /// Returns the underlying object. T& operator*() const { return *ptr_; } /// Returns the underlying object. T* operator->() { return ptr_.get(); } /// Returns the underlying object. T* operator->() const { return ptr_.get(); } /// Returns the underlying shared_ptr std::shared_ptr& ptr() { return ptr_; } /// Returns the underlying shared_ptr const std::shared_ptr& ptr() const { return ptr_; } /// Copy assignment operator. template Ref& operator=(const Ref& _other) { ptr_ = _other.ptr(); return *this; } /// Move assignment operator template Ref& operator=(Ref&& _other) noexcept { ptr_ = std::forward>(_other.ptr()); return *this; } /// Move assignment operator Ref& operator=(Ref&& _other) noexcept = default; /// Copy assignment operator Ref& operator=(const Ref& _other) = default; private: /// Only make is allowed to use this constructor. explicit Ref(std::shared_ptr&& _ptr) : ptr_(std::move(_ptr)) {} /// Only make is allowed to use this constructor. explicit Ref(const std::shared_ptr& _ptr) : ptr_(_ptr) {} private: /// The underlying shared_ptr_ std::shared_ptr ptr_; }; /// Generates a new Ref. template auto make_ref(Args&&... _args) { return Ref::make(std::forward(_args)...); } template inline auto operator<=>(const Ref& _t1, const Ref& _t2) { return _t1.ptr() <=> _t2.ptr(); } template inline std::basic_ostream& operator<<( std::basic_ostream& _os, const Ref& _b) { _os << _b.get(); return _os; } } // namespace rfl namespace std { template struct hash> { size_t operator()(const rfl::Ref& _r) const { return hash>()(_r.ptr()); } }; template inline void swap(rfl::Ref& _r1, rfl::Ref& _r2) { return swap(_r1.ptr(), _r2.ptr()); } } // namespace std #endif // RFL_REF_HPP_