aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2024-10-01 21:03:44 +0200
committerShauren <shauren.trinity@gmail.com>2024-10-01 21:03:44 +0200
commitb13b5142f1009a71ff06786ac8c8db92891f566a (patch)
treeda6b6ab4b0b47e5ac9b219507c9049c4b59d798b /src/common
parent0d496b14d54d723090ea36760ee0e8d47e53891c (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.cpp10
-rw-r--r--src/common/Collision/Models/GameObjectModel.cpp2
-rw-r--r--src/common/Cryptography/Ed25519.cpp6
-rw-r--r--src/common/Cryptography/RSA.cpp8
-rw-r--r--src/common/Debugging/Windows/WheatyExceptionReport.cpp57
-rw-r--r--src/common/Utilities/Memory.h143
-rw-r--r--src/common/Utilities/StartProcess.cpp2
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;