From 0cd9f3df09b3b931b2de64a3a107029dac7b353e Mon Sep 17 00:00:00 2001 From: nikitalita <69168929+nikitalita@users.noreply.github.com> Date: Tue, 24 Oct 2023 15:50:24 -0700 Subject: [PATCH] feat: add msvc::unique_ptr, function, fill out type_info (#192) Ported from CommonLibF4. The only new thing is a new deleter, `AlignedDelete`, for `msvc::unique_ptr`. Not sure how this should be organized; if this should be left in one file, divided into multiple files, or moved to SFSE::impl --- CommonLibSF/include/RE/IDs.h | 10 + CommonLibSF/include/RE/M/msvc.h | 829 ++++++++++++++++++++++++++++ CommonLibSF/include/RE/RTTI.h | 15 +- CommonLibSF/include/SFSE/Impl/PCH.h | 2 +- 4 files changed, 841 insertions(+), 15 deletions(-) create mode 100644 CommonLibSF/include/RE/M/msvc.h diff --git a/CommonLibSF/include/RE/IDs.h b/CommonLibSF/include/RE/IDs.h index 171b0976..92d93f16 100644 --- a/CommonLibSF/include/RE/IDs.h +++ b/CommonLibSF/include/RE/IDs.h @@ -148,6 +148,16 @@ namespace RE::ID inline constexpr REL::ID Notify{ 107339 }; } + namespace msvc + { + namespace type_info + { + inline constexpr REL::ID name{ 248847 }; + inline constexpr REL::ID _RootNode{ 824582 }; + inline constexpr REL::ID compare{ 248848 }; + } + } + namespace MemoryManager { inline constexpr REL::ID GetSingleton{ 33961 }; diff --git a/CommonLibSF/include/RE/M/msvc.h b/CommonLibSF/include/RE/M/msvc.h new file mode 100644 index 00000000..4ab4b28b --- /dev/null +++ b/CommonLibSF/include/RE/M/msvc.h @@ -0,0 +1,829 @@ +#pragma once + +namespace stl +{ + namespace detail + { + // TODO: Have this be the default deleter if the object is allocated aligned + template + struct AlignedDelete + { + constexpr AlignedDelete() noexcept = default; + + template + AlignedDelete(const AlignedDelete&) noexcept // + requires(std::convertible_to) + {} + + void operator()(T* a_ptr) const { delete a_ptr; } + }; + } +} +namespace RE::msvc +{ + class type_info; + + template + class function; + + // class std::_Func_class + template + class function + { + public: + using result_type = R; + + [[nodiscard]] explicit operator bool() const noexcept { return good(); } + + result_type operator()(Args&&... a_args) const + { + assert(good()); + return _fn->do_call(std::forward(a_args)...); + } + + private: + // class std::_Func_base + class __declspec(novtable) proxy_t + { + public: + // add + virtual proxy_t* copy(void*) = 0; // 00 + virtual proxy_t* move(void*) = 0; // 01 + virtual result_type do_call(Args&&...) = 0; // 02 + virtual const type_info& target_type() const = 0; // 03 + virtual void delete_this(bool) = 0; // 04 + virtual ~proxy_t() = default; // 05 + virtual const void* get() const = 0; // 06 + }; + + [[nodiscard]] bool good() const noexcept { return _fn != nullptr; } + + std::aligned_storage_t<3 * sizeof(void*), alignof(long double)> _storage; // 00 + proxy_t* _fn; // 18 + }; + + template + struct default_delete; + + template > + class unique_ptr; + + namespace detail + { + // std::_Get_deleter_pointer_type + template + struct _deleter_pointer_type + { + using type = std::add_pointer_t>; + }; + + template + struct _deleter_pointer_type< + T, + Deleter, + std::void_t< + typename Deleter::pointer>> + { + using type = typename Deleter::pointer; + }; + + template + using deleter_pointer_type = _deleter_pointer_type< + T, + std::remove_reference_t< + Deleter>>; + + template + using deleter_pointer_type_t = typename deleter_pointer_type::type; + + // std::_Unique_ptr_base + + // default template + template + class unique_ptr + { + public: + using pointer = deleter_pointer_type_t; + using deleter_type = Deleter; + + template + unique_ptr(pointer a_ptr, Args... a_args) noexcept // + requires(std::is_nothrow_constructible_v) + : + _pointer(a_ptr), + _deleter(std::forward(a_args)...) + {} + + unique_ptr(unique_ptr&& a_rhs) noexcept // + requires(std::is_nothrow_move_constructible_v) + : + _pointer(std::move(a_rhs._pointer)), + _deleter(std::move(a_rhs._deleter)) + { + a_rhs._pointer = pointer(); + } + + // copy reference + template + unique_ptr(unique_ptr&& a_rhs) noexcept // + requires((std::is_reference_v && + std::is_nothrow_constructible_v)) + : + _pointer(std::move(a_rhs._pointer)), + _deleter(a_rhs._deleter) + { + a_rhs._pointer = typename unique_ptr::pointer(); + } + + // move non-reference + template + unique_ptr(unique_ptr&& a_rhs) noexcept // + requires(!std::is_reference_v && + std::is_nothrow_constructible_v) + : + _pointer(std::move(a_rhs._pointer)), + _deleter(std::move(a_rhs._deleter)) + { + a_rhs._pointer = typename unique_ptr::pointer(); + } + + [[nodiscard]] deleter_type& get_deleter() noexcept { return _deleter; } + [[nodiscard]] const deleter_type& get_deleter() const noexcept { return _deleter; } + + protected: + // members + pointer _pointer; + + private: + template + friend class unique_ptr; + + // members + deleter_type _deleter; + }; + + // empty deleter + template + class unique_ptr< + T, + Deleter, + std::enable_if_t< + std::disjunction_v< + std::is_empty< + Deleter>, + std::is_same< + default_delete, + Deleter>>>> : + public Deleter + { + private: + using super = Deleter; + + public: + using pointer = deleter_pointer_type_t; + using deleter_type = Deleter; + + template + unique_ptr(pointer a_ptr, Args... a_args) noexcept // + requires(std::is_nothrow_constructible_v) + : + super(std::forward(a_args)...), + _pointer(a_ptr) + {} + + unique_ptr(unique_ptr&& a_rhs) noexcept // + requires(std::is_nothrow_move_constructible_v) + : + super(std::move(a_rhs)), + _pointer(std::move(a_rhs._pointer)) + { + a_rhs._pointer = pointer(); + } + + // copy reference + template + unique_ptr(unique_ptr&& a_rhs) noexcept // + requires((std::is_reference_v && + std::is_nothrow_constructible_v)) + : + super(a_rhs), + _pointer(std::move(a_rhs._pointer)) + { + a_rhs._pointer = typename unique_ptr::pointer(); + } + + // move non-reference + template + unique_ptr(unique_ptr&& a_rhs) noexcept // + requires(!std::is_reference_v && + std::is_nothrow_constructible_v) + : + super(std::move(a_rhs)), + _pointer(std::move(a_rhs._pointer)) + { + a_rhs._pointer = typename unique_ptr::pointer(); + } + + [[nodiscard]] deleter_type& get_deleter() noexcept { return *this; } + [[nodiscard]] const deleter_type& get_deleter() const noexcept { return *this; } + + protected: + // members + pointer _pointer; + + private: + template + friend class unique_ptr; + }; + } + + // scalar + template + struct default_delete + { + public: + constexpr default_delete() noexcept = default; + + template + default_delete(const default_delete&) noexcept // + requires(std::convertible_to) + {} + + void operator()(T* a_ptr) const { delete a_ptr; } + }; + + // array + template + struct default_delete + { + public: + constexpr default_delete() noexcept = default; + + template + default_delete(const default_delete&) noexcept // + requires(std::convertible_to) + {} + + template + void operator()(U* a_ptr) const // + requires(std::convertible_to) + { + delete[] a_ptr; + } + }; + + // scalar + template + class unique_ptr : + public detail::unique_ptr + { + private: + using super = detail::unique_ptr; + using super::_pointer; + + public: + // member types + using pointer = typename super::pointer; + using element_type = T; + using deleter_type = typename super::deleter_type; + + // constructors + + // 1a + constexpr unique_ptr() noexcept // + requires(std::is_nothrow_default_constructible_v && + !std::is_pointer_v) + : + super(pointer()) + {} + + // 1b + constexpr unique_ptr(std::nullptr_t) noexcept // + requires(std::is_nothrow_default_constructible_v && + !std::is_pointer_v) + : + super(pointer()) + {} + + // 2 + explicit unique_ptr(pointer a_ptr) noexcept // + requires(std::is_nothrow_default_constructible_v && + !std::is_pointer_v) + : + super(a_ptr) + {} + + // 3a + unique_ptr(pointer a_ptr, const deleter_type& a_dtor) noexcept // + requires(!std::is_reference_v && + std::is_nothrow_copy_constructible_v) + : + super(a_ptr, a_dtor) + {} + + // 4a + unique_ptr(pointer a_ptr, deleter_type&& a_dtor) noexcept // + requires(!std::is_reference_v && + std::is_nothrow_move_constructible_v) + : + super(a_ptr, std::move(a_dtor)) + {} + + // 3b + unique_ptr(pointer a_ptr, deleter_type& a_dtor) noexcept // + requires(std::is_lvalue_reference_v && + !std::is_const_v) + : + super(a_ptr, a_dtor) + {} + + // 4b + unique_ptr(pointer, deleter_type&&) // + requires(std::is_lvalue_reference_v && + !std::is_const_v) + = delete; + + // 3c + unique_ptr(pointer a_ptr, const deleter_type& a_dtor) noexcept // + requires((std::is_lvalue_reference_v && std::is_const_v)) + : + super(a_ptr, a_dtor) + {} + + // 4c + unique_ptr(pointer, const deleter_type&&) // + requires((std::is_lvalue_reference_v && + std::is_const_v)) + = delete; + + // 5 + unique_ptr(unique_ptr&& a_rhs) noexcept // + requires(std::is_nothrow_move_constructible_v) + : + super(std::move(a_rhs)) + {} + + // 6 + template + unique_ptr(unique_ptr&& a_rhs) noexcept // + requires(std::convertible_to::pointer, pointer> && + !std::is_array_v && + std::same_as && + (std::is_reference_v ? + std::is_nothrow_constructible_v : + std::is_nothrow_constructible_v)) + : + super(std::move(a_rhs)) + {} + + // destructor + + ~unique_ptr() noexcept + { + if (_pointer) { + get_deleter()(_pointer); + } + } + + // assignment + + // 1a + unique_ptr& operator=(unique_ptr&& a_rhs) noexcept // + requires(((std::is_reference_v ? + std::is_nothrow_copy_assignable_v> : + std::is_nothrow_move_assignable_v)) && + std::is_move_assignable_v) + { + if (this != std::addressof(a_rhs)) { + reset(a_rhs.release()); + get_deleter() = std::forward(a_rhs.get_deleter()); + } + return *this; + } + + // 1b + template + unique_ptr& operator=(unique_ptr&& a_rhs) noexcept // + requires(((std::is_reference_v ? + std::is_nothrow_copy_assignable_v> : + std::is_nothrow_move_assignable_v)) && + !std::is_array_v && + std::convertible_to::pointer, pointer> && + std::is_assignable_v) + { + reset(a_rhs.release()); + get_deleter() = std::forward(a_rhs.get_deleter()); + return *this; + } + + // 2 + unique_ptr& operator=(std::nullptr_t) noexcept + { + reset(); + return *this; + } + + // modifiers + + pointer release() noexcept + { + auto old = _pointer; + _pointer = pointer(); + return old; + } + + void reset(pointer a_ptr = pointer()) noexcept + { + auto old = _pointer; + _pointer = a_ptr; + if (old) { + get_deleter()(old); + } + } + + void swap(unique_ptr& a_other) noexcept + { + using std::swap; + swap(_pointer, a_other._pointer); + swap(get_deleter(), a_other.get_deleter()); + } + + // observers + + [[nodiscard]] pointer get() const noexcept { return _pointer; } + + using super::get_deleter; + + [[nodiscard]] explicit operator bool() const noexcept { return good(); } + + [[nodiscard]] auto operator*() const + -> std::add_lvalue_reference_t + { + assert(good()); + return *get(); + } + + [[nodiscard]] pointer operator->() const noexcept + { + assert(good()); + return get(); + } + + private: + [[nodiscard]] constexpr bool good() const noexcept { return get() != pointer(); } + }; + + namespace detail + { + template + concept unique_ptr_array_convertible = + std::same_as || + std::same_as || + ((std::same_as && + std::is_pointer_v && + std::same_as (*)[], typename Ptr::element_type (*)[]>)); + } + + // array + template + class unique_ptr : + public detail::unique_ptr + { + private: + using super = detail::unique_ptr; + using super::_pointer; + + public: + // member types + using pointer = typename super::pointer; + using element_type = T; + using deleter_type = typename super::deleter_type; + + public: + // constructors + + // 1a + constexpr unique_ptr() noexcept // + requires(std::is_nothrow_default_constructible_v && + !std::is_pointer_v) + : + super(pointer()) + {} + + // 1b + constexpr unique_ptr(std::nullptr_t) noexcept // + requires(std::is_nothrow_default_constructible_v && + !std::is_pointer_v) + : + super(pointer()) + {} + + // 2 + template + explicit unique_ptr(U a_ptr) noexcept // + requires(std::is_nothrow_default_constructible_v && + !std::is_pointer_v && + detail::unique_ptr_array_convertible) + : + super(a_ptr) + {} + + // 3a + template + unique_ptr(U a_ptr, const deleter_type& a_dtor) noexcept // + requires(!std::is_reference_v && + std::is_nothrow_copy_constructible_v && + detail::unique_ptr_array_convertible) + : + super(a_ptr, a_dtor) + {} + + // 4a + template + unique_ptr(U a_ptr, deleter_type&& a_dtor) noexcept // + requires(!std::is_reference_v && + std::is_nothrow_move_constructible_v && + detail::unique_ptr_array_convertible) + : + super(a_ptr, std::move(a_dtor)) + {} + + // 3b + template + unique_ptr(U a_ptr, deleter_type& a_dtor) noexcept // + requires(std::is_lvalue_reference_v && + !std::is_const_v && + detail::unique_ptr_array_convertible) + : + super(a_ptr, a_dtor) + {} + + // 4b + template + unique_ptr(U, deleter_type&&) noexcept // + requires(std::is_lvalue_reference_v && + !std::is_const_v && + detail::unique_ptr_array_convertible) + = delete; + + // 3c + template + unique_ptr(U a_ptr, const deleter_type& a_dtor) noexcept // + requires((std::is_lvalue_reference_v && + std::is_const_v && + detail::unique_ptr_array_convertible)) + : + super(a_ptr, a_dtor) + {} + + // 4c + template + unique_ptr(U, const deleter_type&&) noexcept // + requires((std::is_lvalue_reference_v && + std::is_const_v && + detail::unique_ptr_array_convertible)) + = delete; + + // 5 + unique_ptr(unique_ptr&& a_rhs) noexcept // + requires(std::is_nothrow_move_constructible_v) + : + super(std::move(a_rhs)) + {} + + // 6 + template + unique_ptr(unique_ptr&& a_rhs) noexcept // + requires((std::is_array_v && + std::same_as && + std::same_as::pointer, typename unique_ptr::element_type*> && + std::convertible_to::element_type (*)[], element_type (*)[]> && + (std::is_reference_v ? + std::same_as && std::is_nothrow_constructible_v : + std::convertible_to && std::is_nothrow_constructible_v))) + : + super(std::move(a_rhs)) + {} + + // destructor + + ~unique_ptr() noexcept + { + if (_pointer) { + get_deleter()(_pointer); + } + } + + // assignment + + // 1a + unique_ptr& operator=(unique_ptr&& a_rhs) noexcept // + requires(((std::is_reference_v ? + std::is_nothrow_copy_assignable_v> : + std::is_nothrow_move_assignable_v)) && + std::is_move_assignable_v) + { + if (this != std::addressof(a_rhs)) { + reset(a_rhs.release()); + get_deleter() = std::forward(a_rhs.get_deleter()); + } + return *this; + } + + // 1b + template + unique_ptr& operator=(unique_ptr&& a_rhs) noexcept // + requires((std::is_array_v && + std::same_as && + std::same_as::pointer, typename unique_ptr::element_type*> && + std::convertible_to::element_type (*)[], element_type (*)[]> && + std::is_assignable_v)) + { + if (this != std::addressof(a_rhs)) { + reset(a_rhs.release()); + get_deleter() = std::forward(a_rhs.get_deleter()); + } + return *this; + } + + // 2 + unique_ptr& operator=(std::nullptr_t) noexcept + { + reset(); + return *this; + } + + // modifiers + + pointer release() noexcept + { + auto old = _pointer; + _pointer = pointer{}; + return old; + } + + // 3 + template + void reset(U a_ptr) noexcept // + requires(detail::unique_ptr_array_convertible) + { + auto old = _pointer; + _pointer = a_ptr; + if (old) { + get_deleter()(old); + } + } + + // 4 + void reset(std::nullptr_t = nullptr) noexcept { reset(pointer()); } + + void swap(unique_ptr& a_other) noexcept + { + using std::swap; + swap(_pointer, a_other._pointer); + swap(get_deleter(), a_other.get_deleter()); + } + + // observers + + [[nodiscard]] pointer get() const noexcept { return _pointer; } + + using super::get_deleter; + + [[nodiscard]] explicit operator bool() const noexcept { return good(); } + + [[nodiscard]] element_type& operator[](std::size_t a_idx) const + { + assert(good()); + return get()[a_idx]; + } + + private: + [[nodiscard]] constexpr bool good() const noexcept { return get() != pointer(); } + }; + + // 1 + template + [[nodiscard]] unique_ptr make_unique(Args&&... a_args) // + requires(!std::is_array_v) + { + return unique_ptr(new T(std::forward(a_args)...)); + } + + // 2 + template + [[nodiscard]] unique_ptr make_unique(std::size_t a_size) // + requires(std::is_unbounded_array_v) + { + return unique_ptr(new std::remove_extent_t[a_size]()); + } + + // 3 + template + void make_unique(Args&&...) // + requires(std::is_bounded_array_v) + = delete; + + // 4 + template + [[nodiscard]] unique_ptr make_unique_for_overwrite() // + requires(!std::is_array_v) + { + return unique_ptr(new T); + } + + // 5 + template + [[nodiscard]] unique_ptr make_unique_for_overwrite(std::size_t a_size) // + requires(std::is_unbounded_array_v) + { + return unique_ptr(new std::remove_extent_t[a_size]); + } + + // 6 + template + void make_unique_for_overwrite(Args&&...) // + requires(std::is_bounded_array_v) + = delete; + + // 1 + template + [[nodiscard]] bool operator==(const unique_ptr& a_lhs, const unique_ptr& a_rhs) + { + return a_lhs.get() == a_rhs.get(); + } + + // 7 + template + [[nodiscard]] auto operator<=>(const unique_ptr& a_lhs, const unique_ptr& a_rhs) + -> std::compare_three_way_result_t< + typename unique_ptr::pointer, + typename unique_ptr::pointer> + requires(std::three_way_comparable_with< + typename unique_ptr::pointer, + typename unique_ptr::pointer>) + { + return a_lhs.get() <=> a_rhs.get(); + } + + // 8 + template + [[nodiscard]] bool operator==(const unique_ptr& a_lhs, std::nullptr_t) noexcept + { + return !a_lhs; + } + + // 20 + template + [[nodiscard]] auto operator<=>(const unique_ptr& a_lhs, std::nullptr_t) + -> std::compare_three_way_result_t::pointer> + requires(std::three_way_comparable::pointer>) + { + return a_lhs.get() <=> nullptr; + } + + template + void swap(unique_ptr& a_lhs, unique_ptr& a_rhs) noexcept + { + a_lhs.swap(a_rhs); + } + + class type_info + { + public: + SF_RTTI_VTABLE(type_info); + virtual ~type_info(); // 00 + + [[nodiscard]] const char* name() const noexcept + { + using func_t = const char* (*)(const type_info*, __type_info_node*) noexcept; + REL::Relocation func{ ID::msvc::type_info::name }; + return (*func)(this, std::addressof(get_root_node())); + } + + [[nodiscard]] const char* mangled_name() const noexcept { return _name; } + + [[nodiscard]] bool operator==(const type_info& a_rhs) const noexcept { return compare(a_rhs) == 0; } + [[nodiscard]] bool operator!=(const type_info& a_rhs) const noexcept { return compare(a_rhs) != 0; } + + private: + [[nodiscard]] int compare(const type_info& a_rhs) const + { + using func_t = int (*)(const type_info*, const type_info*) noexcept; + REL::Relocation func{ ID::msvc::type_info::compare }; + return (*func)(this, &a_rhs); + } + + [[nodiscard]] static __type_info_node& get_root_node() noexcept + { + REL::Relocation<__type_info_node*> root{ ID::msvc::type_info::_RootNode }; + return *root; + } + + // members + char* _undecorated_name; // 08 + char _name[1]; // 10 + }; + static_assert(sizeof(type_info) == 0x18); + +} diff --git a/CommonLibSF/include/RE/RTTI.h b/CommonLibSF/include/RE/RTTI.h index 2e929646..5082017e 100644 --- a/CommonLibSF/include/RE/RTTI.h +++ b/CommonLibSF/include/RE/RTTI.h @@ -8,20 +8,7 @@ namespace RE { namespace msvc { - class __declspec(novtable) type_info - { - public: - virtual ~type_info(); // 00 - - [[nodiscard]] const char* mangled_name() const noexcept { return _name; } - - private: - // members - void* _data; // 08 - char _name[1]; // 10 - }; - - static_assert(sizeof(type_info) == 0x18); + class type_info; } namespace RTTI diff --git a/CommonLibSF/include/SFSE/Impl/PCH.h b/CommonLibSF/include/SFSE/Impl/PCH.h index a9481fca..80dcf342 100644 --- a/CommonLibSF/include/SFSE/Impl/PCH.h +++ b/CommonLibSF/include/SFSE/Impl/PCH.h @@ -759,5 +759,5 @@ namespace REL #include "RE/IDs_VTABLE.h" #include "RE/F/FormTypes.h" - +#include "RE/M/msvc.h" #undef cdecl // Workaround for Clang.