Last active
June 27, 2024 21:10
-
-
Save Qyriad/68c9a7464a9e326f3b80c7ea8dcdc443 to your computer and use it in GitHub Desktop.
C++ non-nullable pointer wrapper specifically for out parameters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #pragma once | |
| #include <memory> // std::addressof | |
| #include <optional> | |
| #include <type_traits> // std::type_identity_t | |
| template<typename T> | |
| using NoDeduce = std::type_identity_t<T>; | |
| /** Non-nullable pointer, for making out-parameters explicit. | |
| * Unlike nix::ref, this neither enforces nor assumes any particular ownership semantics, | |
| * making it safe to use on things like references from dereferencing an std::unique_ptr. | |
| */ | |
| template<typename T> | |
| class RefMut | |
| { | |
| using Self = RefMut<T>; | |
| /** Changing *what* this points to is not allowed, as it would always be semantically | |
| * incorrect. | |
| */ | |
| T *const inner; | |
| public: | |
| constexpr explicit RefMut(T &value) noexcept | |
| : inner(std::addressof(value)) | |
| { | |
| // We use std::addressof() because T could have overloaded operator&. *sigh*. | |
| } | |
| constexpr inline T &operator*() noexcept | |
| { | |
| return *this->inner; | |
| } | |
| constexpr inline T *operator->() noexcept | |
| { | |
| return this->inner; | |
| } | |
| constexpr inline T &get() const noexcept | |
| { | |
| return *this->inner; | |
| } | |
| /** Returns the inner pointer, by value. | |
| * | |
| * Mutating the inner pointer (that is, changing *what* it points to), is not | |
| * allowed in this class as that behavior would always be incorrect. | |
| */ | |
| // No I will not overload operator&, fuck you. | |
| constexpr inline T *ptr() const noexcept | |
| { | |
| return this->inner; | |
| } | |
| /** Default construction is disallowed, as it would always be semantically incorrect. | |
| */ | |
| RefMut() = delete; | |
| /** operator= is disallowed, as its not obvious what it would do. | |
| * Use *foo = bar instead. | |
| */ | |
| Self operator=(Self const &rhs) = delete; | |
| /** Move assignment is disallowed, as it wwould always be semantically incorrect. | |
| * This class does not and cannot ever own a value. | |
| */ | |
| Self operator=(Self &&rhs) = delete; | |
| /** Copy construction is perfectly fine and does not deep copy, allowing functions to | |
| * take RefMut<T> by value like they can with shared_ptr<T>. | |
| */ | |
| constexpr RefMut(Self const &other) noexcept | |
| : inner(other.inner) | |
| { } | |
| /** Move construction is allowed but identical to copy construction. | |
| */ | |
| constexpr RefMut(Self &&other) noexcept | |
| : inner(other.inner) | |
| { } | |
| // Disable template type dedudction for CastTargetT, requring this function | |
| // to always require its template type argument to be specified, like | |
| // dynamic_cast<T> does. | |
| // e.g., RefMut<Bar> bar = foo.dynamic_cast() is disallowed; | |
| // like with dynamic_cast, you should write | |
| // auto bar = foo.dynamic_pointer_cast<Bar>() | |
| template<typename CastTargetT> | |
| constexpr std::optional<RefMut<NoDeduce<CastTargetT>>> dynamic_pointer_cast() | |
| const noexcept | |
| { | |
| CastTargetT const *raw_casted = dynamic_cast<CastTargetT *>(this->inner); | |
| if (raw_casted == nullptr) { | |
| return std::nullopt; | |
| } | |
| return RefMut<CastTargetT>(*raw_casted); | |
| } | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment