diff options
author | Shauren <shauren.trinity@gmail.com> | 2024-10-01 21:03:44 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2024-10-01 21:03:44 +0200 |
commit | b13b5142f1009a71ff06786ac8c8db92891f566a (patch) | |
tree | da6b6ab4b0b47e5ac9b219507c9049c4b59d798b /src/common | |
parent | 0d496b14d54d723090ea36760ee0e8d47e53891c (diff) |
Core/Utilities: Extend make_unique_ptr_with_deleter functionality to allow it to create deleters with compile time constant functions (reduces its size to just sizeof(void*))
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/Collision/Maps/MapTree.cpp | 10 | ||||
-rw-r--r-- | src/common/Collision/Models/GameObjectModel.cpp | 2 | ||||
-rw-r--r-- | src/common/Cryptography/Ed25519.cpp | 6 | ||||
-rw-r--r-- | src/common/Cryptography/RSA.cpp | 8 | ||||
-rw-r--r-- | src/common/Debugging/Windows/WheatyExceptionReport.cpp | 57 | ||||
-rw-r--r-- | src/common/Utilities/Memory.h | 143 | ||||
-rw-r--r-- | src/common/Utilities/StartProcess.cpp | 2 |
7 files changed, 176 insertions, 52 deletions
diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp index bde07b30065..d828326806c 100644 --- a/src/common/Collision/Maps/MapTree.cpp +++ b/src/common/Collision/Maps/MapTree.cpp @@ -196,11 +196,11 @@ namespace VMAP struct TileFileOpenResult { - using FileHandle = decltype(Trinity::make_unique_ptr_with_deleter<FILE>(nullptr, &::fclose)); + using FileDeleter = decltype(Trinity::unique_ptr_deleter<FILE*, &::fclose>()); std::string Name; - FileHandle TileFile = { nullptr, &::fclose }; - FileHandle SpawnIndicesFile = { nullptr, &::fclose }; + std::unique_ptr<FILE, FileDeleter> TileFile; + std::unique_ptr<FILE, FileDeleter> SpawnIndicesFile; int32 UsedMapId; explicit operator bool() const { return TileFile && SpawnIndicesFile; } @@ -239,7 +239,7 @@ namespace VMAP basePath.push_back('/'); std::string fullname = basePath + VMapManager2::getMapFileName(mapID); - auto rf = Trinity::make_unique_ptr_with_deleter(fopen(fullname.c_str(), "rb"), &::fclose); + auto rf = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fullname.c_str(), "rb")); if (!rf) return LoadResult::FileNotFound; @@ -263,7 +263,7 @@ namespace VMAP { TC_LOG_DEBUG("maps", "StaticMapTree::InitMap() : initializing StaticMapTree '{}'", fname); std::string fullname = iBasePath + fname; - auto rf = Trinity::make_unique_ptr_with_deleter(fopen(fullname.c_str(), "rb"), &::fclose); + auto rf = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fullname.c_str(), "rb")); if (!rf) return LoadResult::FileNotFound; diff --git a/src/common/Collision/Models/GameObjectModel.cpp b/src/common/Collision/Models/GameObjectModel.cpp index b32a5bdd44f..9fcf1b85e20 100644 --- a/src/common/Collision/Models/GameObjectModel.cpp +++ b/src/common/Collision/Models/GameObjectModel.cpp @@ -46,7 +46,7 @@ bool LoadGameObjectModelList(std::string const& dataPath) { uint32 oldMSTime = getMSTime(); - auto model_list_file = Trinity::make_unique_ptr_with_deleter(fopen((dataPath + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb"), &::fclose); + auto model_list_file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen((dataPath + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb")); if (!model_list_file) { TC_LOG_ERROR("misc", "Unable to open '{}' file.", VMAP::GAMEOBJECT_MODELS); diff --git a/src/common/Cryptography/Ed25519.cpp b/src/common/Cryptography/Ed25519.cpp index 6f286c74b3e..febd8237c57 100644 --- a/src/common/Cryptography/Ed25519.cpp +++ b/src/common/Cryptography/Ed25519.cpp @@ -68,7 +68,7 @@ bool Ed25519::LoadFromFile(std::string const& fileName) _key = nullptr; } - auto keyBIO = make_unique_ptr_with_deleter(BIO_new_file(fileName.c_str(), "r"), &BIO_free); + auto keyBIO = make_unique_ptr_with_deleter<&BIO_free>(BIO_new_file(fileName.c_str(), "r")); if (!keyBIO) return false; @@ -87,9 +87,9 @@ bool Ed25519::LoadFromString(std::string const& keyPem) _key = nullptr; } - auto keyBIO = make_unique_ptr_with_deleter(BIO_new_mem_buf( + auto keyBIO = make_unique_ptr_with_deleter<&BIO_free>(BIO_new_mem_buf( const_cast<char*>(keyPem.c_str()) /*api hack - this function assumes memory is readonly but lacks const modifier*/, - keyPem.length() + 1), &BIO_free); + keyPem.length() + 1)); if (!keyBIO) return false; diff --git a/src/common/Cryptography/RSA.cpp b/src/common/Cryptography/RSA.cpp index 106eed27374..27a0f750fb0 100644 --- a/src/common/Cryptography/RSA.cpp +++ b/src/common/Cryptography/RSA.cpp @@ -297,7 +297,7 @@ bool RsaSignature::LoadKeyFromFile(std::string const& fileName) _key = nullptr; } - auto keyBIO = make_unique_ptr_with_deleter(BIO_new_file(fileName.c_str(), "r"), &BIO_free); + auto keyBIO = make_unique_ptr_with_deleter<&BIO_free>(BIO_new_file(fileName.c_str(), "r")); if (!keyBIO) return false; @@ -316,9 +316,9 @@ bool RsaSignature::LoadKeyFromString(std::string const& keyPem) _key = nullptr; } - auto keyBIO = make_unique_ptr_with_deleter(BIO_new_mem_buf( + auto keyBIO = make_unique_ptr_with_deleter<&BIO_free>(BIO_new_mem_buf( const_cast<char*>(keyPem.c_str()) /*api hack - this function assumes memory is readonly but lacks const modifier*/, - keyPem.length() + 1), &BIO_free); + keyPem.length() + 1)); if (!keyBIO) return false; @@ -333,7 +333,7 @@ bool RsaSignature::Sign(uint8 const* message, std::size_t messageLength, DigestG { std::unique_ptr<EVP_MD, DigestGenerator::EVP_MD_Deleter> digestGenerator = generator.GetGenerator(); - auto keyCtx = make_unique_ptr_with_deleter(EVP_PKEY_CTX_new_from_pkey(generator.GetLib(), _key, nullptr), &EVP_PKEY_CTX_free); + auto keyCtx = make_unique_ptr_with_deleter<&EVP_PKEY_CTX_free>(EVP_PKEY_CTX_new_from_pkey(generator.GetLib(), _key, nullptr)); EVP_MD_CTX_set_pkey_ctx(_ctx, keyCtx.get()); std::unique_ptr<OSSL_PARAM[]> params = generator.GetParams(); diff --git a/src/common/Debugging/Windows/WheatyExceptionReport.cpp b/src/common/Debugging/Windows/WheatyExceptionReport.cpp index 5cb29c56aae..8bdce95aee5 100644 --- a/src/common/Debugging/Windows/WheatyExceptionReport.cpp +++ b/src/common/Debugging/Windows/WheatyExceptionReport.cpp @@ -6,6 +6,7 @@ #include "WheatyExceptionReport.h" #include "Errors.h" #include "GitRevision.h" +#include "Memory.h" #include <stdexcept> #include <algorithm> #include <utility> @@ -411,6 +412,12 @@ BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax) return TRUE; } +template <std::derived_from<IUnknown> T> +using com_unique_ptr_deleter = decltype(Trinity::unique_ptr_deleter<T*, [](T* ptr) { ptr->Release(); }>()); + +template <std::derived_from<IUnknown> T> +using com_unique_ptr = std::unique_ptr<T, com_unique_ptr_deleter<T>>; + BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cntMax) { // Step 1: -------------------------------------------------- @@ -419,10 +426,7 @@ BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cn if (FAILED(hres)) return FALSE; - std::shared_ptr<void> com(nullptr, [](void*) - { - CoUninitialize(); - }); + auto com = Trinity::make_unique_ptr_with_deleter<[](void*) { CoUninitialize(); }>(&hres); // Step 2: -------------------------------------------------- // Set general COM security levels -------------------------- @@ -443,7 +447,7 @@ BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cn // Step 3: --------------------------------------------------- // Obtain the initial locator to WMI ------------------------- - std::shared_ptr<IWbemLocator> loc = []() -> std::shared_ptr<IWbemLocator> + com_unique_ptr<IWbemLocator> loc([] { IWbemLocator* tmp = nullptr; HRESULT hres = CoCreateInstance( @@ -453,11 +457,8 @@ BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cn IID_IWbemLocator, reinterpret_cast<LPVOID*>(&tmp)); - if (FAILED(hres)) - return nullptr; - - return { tmp, [](IWbemLocator* ptr) { if (ptr) ptr->Release(); } }; - }(); + return SUCCEEDED(hres) ? tmp : nullptr; + }()); if (!loc) return FALSE; @@ -466,7 +467,7 @@ BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cn // Connect to the root\cimv2 namespace with // the current user and obtain pointer pSvc // to make IWbemServices calls. - std::shared_ptr<IWbemServices> svc = [loc]() ->std::shared_ptr<IWbemServices> + com_unique_ptr<IWbemServices> svc([&] { IWbemServices* tmp = nullptr; HRESULT hres = loc->ConnectServer( @@ -480,11 +481,8 @@ BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cn &tmp // pointer to IWbemServices proxy ); - if (FAILED(hres)) - return nullptr; - - return { tmp, [](IWbemServices* ptr) { if (ptr) ptr->Release(); } }; - }(); + return SUCCEEDED(hres) ? tmp : nullptr; + }()); if (!svc) return FALSE; @@ -509,7 +507,7 @@ BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cn // Use the IWbemServices pointer to make requests of WMI ---- // For example, get the name of the operating system - std::shared_ptr<IEnumWbemClassObject> queryResult = [svc]() -> std::shared_ptr<IEnumWbemClassObject> + com_unique_ptr<IEnumWbemClassObject> queryResult([&] { IEnumWbemClassObject* tmp = nullptr; HRESULT hres = svc->ExecQuery( @@ -519,11 +517,8 @@ BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cn nullptr, &tmp); - if (FAILED(hres)) - return nullptr; - - return { tmp, [](IEnumWbemClassObject* ptr) { if (ptr) ptr->Release(); } }; - }(); + return SUCCEEDED(hres) ? tmp : nullptr; + }()); BOOL result = FALSE; // Step 7: ------------------------------------------------- @@ -532,11 +527,17 @@ BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cn { do { - IWbemClassObject* fields = nullptr; - - ULONG rows = 0; - queryResult->Next(WBEM_INFINITE, 1, &fields, &rows); - if (!rows) + auto [fields, rows] = [&] + { + IWbemClassObject* fields = nullptr; + ULONG rows = 0; + HRESULT hres = queryResult->Next(WBEM_INFINITE, 1, &fields, &rows); + return SUCCEEDED(hres) && rows + ? std::pair(com_unique_ptr<IWbemClassObject>(fields), rows) + : std::pair(com_unique_ptr<IWbemClassObject>(), ULONG(0)); + }(); + + if (!fields || !rows) break; VARIANT field; @@ -558,8 +559,6 @@ BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cn } VariantClear(&field); - fields->Release(); - result = TRUE; } while (true); } diff --git a/src/common/Utilities/Memory.h b/src/common/Utilities/Memory.h index 98622166b22..629099bf3e3 100644 --- a/src/common/Utilities/Memory.h +++ b/src/common/Utilities/Memory.h @@ -18,32 +18,157 @@ #ifndef TRINITY_MEMORY_H #define TRINITY_MEMORY_H +#include "CompilerDefs.h" +#include <concepts> #include <memory> +#if TRINITY_COMPILER == TRINITY_COMPILER_GNU +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-attributes" +#endif + namespace Trinity { namespace Impl { template<typename T, typename Del> -struct unique_ptr_deleter +struct stateful_unique_ptr_deleter { - using pointer = T*; - unique_ptr_deleter(Del deleter) : _deleter(std::move(deleter)) { } - - void operator()(pointer ptr) const { _deleter(ptr); } + using pointer = T; + explicit(false) stateful_unique_ptr_deleter(Del deleter) : _deleter(std::move(deleter)) { } + void operator()(pointer ptr) const { (void)_deleter(ptr); } private: Del _deleter; }; + +template<typename T, auto Del> +struct stateless_unique_ptr_deleter +{ + using pointer = T; + void operator()(pointer ptr) const + { + if constexpr (std::is_member_function_pointer_v<decltype(Del)>) + (void)(ptr->*Del)(); + else + (void)Del(ptr); + } +}; } -template<typename T, typename Del> -auto make_unique_ptr_with_deleter(T* ptr, Del&& deleter) +/** + * Convenience function to construct type aliases for std::unique_ptr stateful deleters (such as lambda with captures) + * @tparam Ptr Type of the pointer + * @tparam Del Type of object freeing function (deduced from argument) + * @param deleter Object deleter + * + * Example use + * @code + * void FreeV1(Resource*); + * void FreeV2(Resource*); + * + * using ResourceDeleter = decltype(Trinity::unique_ptr_deleter<Resource*>(&FreeV1)); + * + * std::unique_ptr<Resource, ResourceDeleter> resource = Trinity::make_unique_ptr_with_deleter(GetResourceV1(), &FreeV1); + * // do stuff ... + * // ... later get new resource + * resource = Trinity::make_unique_ptr_with_deleter(GetResourceV2(), &FreeV2); + * @endcode + */ +template <typename Ptr, typename Del> requires std::invocable<Del, Ptr> && std::is_pointer_v<Ptr> +Impl::stateful_unique_ptr_deleter<Ptr, Del> unique_ptr_deleter(Del deleter) +{ + return Impl::stateful_unique_ptr_deleter<Ptr, Del>(std::move(deleter)); +} + +/** + * Convenience function to construct type aliases for std::unique_ptr stateful deleters (such as lambda with captures) + * + * Main use is for forming struct/class members without the call to make_unique_ptr_with_deleter + * @tparam Ptr Type of the pointer + * @tparam Del The freeing function. This can be either a free function pointer that accepts T* as argument, pointer to a member function of T that accepts no arguments or a lambda with no captures + * + * Example use + * @code + * using FileDeleter = decltype(Trinity::unique_ptr_deleter<FILE*, &::fclose>()); + * + * class Resource + * { + * std::unique_ptr<FILE, FileDeleter> File; + * }; + * @endcode + */ +template <typename Ptr, auto Del> requires std::invocable<decltype(Del), Ptr> && std::is_pointer_v<Ptr> +Impl::stateless_unique_ptr_deleter<Ptr, Del> unique_ptr_deleter() +{ + return Impl::stateless_unique_ptr_deleter<Ptr, Del>(); +} + +/** + * Utility function to construct a std::unique_ptr object with custom stateful deleter (such as lambda with captures) + * @tparam Ptr Type of the pointer + * @tparam T Type of the pointed-to object (defaults to std::remove_pointer_t<Ptr>) + * @tparam Del Type of object freeing function (deduced from second argument) + * @param ptr Raw pointer to owned object + * @param deleter Object deleter + * + * Example use + * @code + * class Resource + * { + * }; + * class ResourceService + * { + * Resource* Create(); + * void Destroy(Resource*); + * }; + * + * ResourceService* service = GetResourceService(); + * + * // Lambda that captures all required variables for destruction + * auto resource = Trinity::make_unique_ptr_with_deleter(service->Create(), [service](Resource* res){ service->Destroy(res); }); + * @endcode + */ +template<typename Ptr, typename T = std::remove_pointer_t<Ptr>, typename Del> requires std::invocable<Del, Ptr> && std::is_pointer_v<Ptr> +std::unique_ptr<T, Impl::stateful_unique_ptr_deleter<Ptr, Del>> make_unique_ptr_with_deleter(Ptr ptr, Del deleter) { - using Deleter_t = Impl::unique_ptr_deleter<T, Del>; + return std::unique_ptr<T, Impl::stateful_unique_ptr_deleter<Ptr, Del>>(ptr, std::move(deleter)); +} - return std::unique_ptr<T, Deleter_t>(ptr, Deleter_t(std::forward<Del>(deleter))); +/** + * Utility function to construct a std::unique_ptr object with custom stateless deleter (function pointer, captureless lambda) + * @tparam Del The freeing function. This can be either a free function pointer that accepts T* as argument, pointer to a member function of T that accepts no arguments or a lambda with no captures + * @tparam Ptr Type of the pointer + * @tparam T Type of the pointed-to object (defaults to std::remove_pointer_t<Ptr>) + * @param ptr Raw pointer to owned object + * + * Example uses + * @code + * void DestroyResource(Resource*); + * class Resource + * { + * void Destroy(); + * }; + * + * // Free function + * auto free = Trinity::make_unique_ptr_with_deleter<&DestroyResource>(new Resource()); + * + * // Member function + * auto member = Trinity::make_unique_ptr_with_deleter<&Resource::Destroy>(new Resource()); + * + * // Lambda + * auto lambda = Trinity::make_unique_ptr_with_deleter<[](Resource* res){ res->Destroy(); }>(new Resource()); + * @endcode + */ +template<auto Del, typename Ptr, typename T = std::remove_pointer_t<Ptr>> requires std::invocable<decltype(Del), Ptr> && std::is_pointer_v<Ptr> +std::unique_ptr<T, Impl::stateless_unique_ptr_deleter<Ptr, Del>> make_unique_ptr_with_deleter(Ptr ptr) +{ + return std::unique_ptr<T, Impl::stateless_unique_ptr_deleter<Ptr, Del>>(ptr); } } +#if TRINITY_COMPILER == TRINITY_COMPILER_GNU +#pragma GCC diagnostic pop +#endif + #endif // TRINITY_MEMORY_H diff --git a/src/common/Utilities/StartProcess.cpp b/src/common/Utilities/StartProcess.cpp index d0fe3509ae7..9207a3e7a48 100644 --- a/src/common/Utilities/StartProcess.cpp +++ b/src/common/Utilities/StartProcess.cpp @@ -101,7 +101,7 @@ public: } // prepare file with only read permission (boost process opens with read_write) - auto inputFile = Trinity::make_unique_ptr_with_deleter(!input_file.empty() ? fopen(input_file.c_str(), "rb") : nullptr, &::fclose); + auto inputFile = Trinity::make_unique_ptr_with_deleter<&::fclose>(!input_file.empty() ? fopen(input_file.c_str(), "rb") : nullptr); std::error_code ec; |