1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
#ifndef RFL_REF_HPP_
#define RFL_REF_HPP_
#include <memory>
#include <stdexcept>
#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 T>
class Ref {
public:
/// The default way of creating new references is
/// Ref<T>::make(...) or make_ref<T>(...).
template <class... Args>
static Ref<T> make(Args&&... _args) {
return Ref<T>(std::make_shared<T>(std::forward<Args>(_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<Ref<T>> make(std::shared_ptr<T>&& _ptr) {
if (!_ptr) {
return error("std::shared_ptr was a nullptr.");
}
return Ref<T>(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<Ref<T>> make(const std::shared_ptr<T>& _ptr) {
if (!_ptr) {
return error("std::shared_ptr was a nullptr.");
}
return Ref<T>(_ptr);
}
Ref() : ptr_(std::make_shared<T>()) {}
Ref(const Ref<T>& _other) = default;
Ref(Ref<T>&& _other) = default;
template <class U>
Ref(const Ref<U>& _other) : ptr_(_other.ptr()) {}
template <class U>
Ref(Ref<U>&& _other) noexcept
: ptr_(std::forward<std::shared_ptr<U>>(_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<T>& ptr() { return ptr_; }
/// Returns the underlying shared_ptr
const std::shared_ptr<T>& ptr() const { return ptr_; }
/// Copy assignment operator.
template <class U>
Ref<T>& operator=(const Ref<U>& _other) {
ptr_ = _other.ptr();
return *this;
}
/// Move assignment operator
template <class U>
Ref<T>& operator=(Ref<U>&& _other) noexcept {
ptr_ = std::forward<std::shared_ptr<U>>(_other.ptr());
return *this;
}
/// Move assignment operator
Ref<T>& operator=(Ref<T>&& _other) noexcept = default;
/// Copy assignment operator
Ref<T>& operator=(const Ref<T>& _other) = default;
private:
/// Only make is allowed to use this constructor.
explicit Ref(std::shared_ptr<T>&& _ptr) : ptr_(std::move(_ptr)) {}
/// Only make is allowed to use this constructor.
explicit Ref(const std::shared_ptr<T>& _ptr) : ptr_(_ptr) {}
private:
/// The underlying shared_ptr_
std::shared_ptr<T> ptr_;
};
/// Generates a new Ref<T>.
template <class T, class... Args>
auto make_ref(Args&&... _args) {
return Ref<T>::make(std::forward<Args>(_args)...);
}
template <class T1, class T2>
inline auto operator<=>(const Ref<T1>& _t1, const Ref<T2>& _t2) {
return _t1.ptr() <=> _t2.ptr();
}
template <class CharT, class Traits, class T>
inline std::basic_ostream<CharT, Traits>& operator<<(
std::basic_ostream<CharT, Traits>& _os, const Ref<T>& _b) {
_os << _b.get();
return _os;
}
} // namespace rfl
namespace std {
template <class T>
struct hash<rfl::Ref<T>> {
size_t operator()(const rfl::Ref<T>& _r) const {
return hash<shared_ptr<T>>()(_r.ptr());
}
};
template <class T>
inline void swap(rfl::Ref<T>& _r1, rfl::Ref<T>& _r2) {
return swap(_r1.ptr(), _r2.ptr());
}
} // namespace std
#endif // RFL_REF_HPP_
|