#1
|
||||
|
||||
UniqueResource, a C++ template class as a universal resource manager
https://github.com/z16166/UniqueResource
UniqueResource, a C++ template class which works as a universal resource manager to own and free all kinds of resources. C++14 and above, header-only. Made by me, inspired by std::experimental::unique_resource and similar projects.
__________________
AKA Solomon/blowfish. |
The Following 3 Users Gave Reputation+1 to WhoCares For This Useful Post: | ||
The Following 5 Users Say Thank You to WhoCares For This Useful Post: | ||
blue_devil (06-07-2024), Fyyre (06-11-2024), MarcElBichon (06-07-2024), sendersu (06-11-2024), tonyweb (06-08-2024) |
#2
|
||||
|
||||
You can also do something similar to this by using the built-in 'std::shared_ptr' type. It allows for initialization with a custom deleter function. With that, you can pass a type cleanup call for types that have one. (You can also just define your own callback to cleanup custom/more unique types as needed.)
For example, using a 'HANDLE' type, which is cleaned up using 'CloseHandle': PHP Code:
__________________
Personal Projects Site: https://atom0s.com Last edited by atom0s; 06-08-2024 at 04:49. Reason: Forum breaks C++ code using the normal code tags; changed to php tags instead. |
The Following 3 Users Say Thank You to atom0s For This Useful Post: | ||
#3
|
||||
|
||||
Yes. I have done that for Windows native RPC handles.
The problem is, we have to specify the custom deleter each time when we declare a new variable. It's tedious. Maybe a typedef or "using"? And, std::unique_ptr lacks abstraction for a default_value. Also, we can add custom conversion operator for UniqueResource to avoid explicitly using Get() method each time, just like "operator LPCSTR()", and "operator LPCWSTR()" of the ancient CString class from Microsoft. Code:
explicit operator ResourceType() const noexcept { return resource_; } samples of custom deleters for windows native RPC( "pointer" is defined for each functor): Code:
struct StringBindingDeleter { typedef RPC_WSTR pointer; void operator()(RPC_WSTR stringBinding) { [[maybe_unused]] RPC_STATUS status = RpcStringFree(&stringBinding); assert(status == RPC_S_OK); } }; struct RpcBindingDeleter { typedef RPC_BINDING_HANDLE pointer; void operator()(RPC_BINDING_HANDLE h) { [[maybe_unused]] RPC_STATUS status = RpcBindingFree(&h); assert(status == RPC_S_OK); } }; usage: Code:
RPC_WSTR stringBinding = nullptr; status = RpcStringBindingCompose(nullptr, (RPC_WSTR)L"ncalrpc", nullptr, (RPC_WSTR)L"9038a9dd-b0f3-4745-827e-d4109e662cea", nullptr, (RPC_WSTR *)&stringBinding); if (status != RPC_S_OK || !stringBinding) return E_FAIL; std::unique_ptr stringBindingOwner(stringBinding, StringBindingDeleter); RPC_BINDING_HANDLE rpcBinding = nullptr; status = RpcBindingFromStringBinding(stringBinding, &rpcBinding); if (status != RPC_S_OK) return E_FAIL; std::unique_ptr rpcBindingOwner(rpcBinding, RpcBindingDeleter); Quote:
__________________
AKA Solomon/blowfish. Last edited by WhoCares; 06-07-2024 at 22:28. |
#4
|
||||
|
||||
Using templates can remove the need to manually define the delete function. It can also be used to further extend into a custom wrapper to deal with the other parts you mentioned as well such as:
PHP Code:
PHP Code:
PHP Code:
PHP Code:
PHP Code:
PHP Code:
__________________
Personal Projects Site: https://atom0s.com |
The Following 3 Users Say Thank You to atom0s For This Useful Post: | ||
#5
|
||||
|
||||
Your coding a wrapper/aggregator for std::unique_ptr is a good choice.
Some types are same, but with different default values and different cleanup APIs. For OpenProcess()/OpenThreadToken() etc., the type is HANDLE, the default value is NULL, the cleanup API is CloseHandle(). For CreateFile()/FindFirstFile()/CreateToolhelp32Snapshot() etc., the type is also HANDLE, but the default value is INVALID_HANDLE_VALUE, and the cleanup API for FindFirstFile() is FindClose(), instead of CloseHandle(). So that's why I use traits/adapter to describe the difference. And, I need the operator&() override to auto reset to the default value when reusing the same UniqueResource variable.
__________________
AKA Solomon/blowfish. |
#6
|
|||
|
|||
Is it hard to abstract the whole Win API I would think without having a way to define dependencies. Resources sometimes form a hierarchy and have to be closed in that order e.g. CloseServiceHandle on the service then the manager. But it's hard to do as eventually you are doing reference dependency checking similar to a garbage collector. M$FT wrapped the Win API using MFC for that reason as they could construct the hierarchy and encapsulate handles. Not to say this technique isn't usable in some common and simple everyday scenarios.
|
#7
|
||||
|
||||
There is no silver bullet
Such unique/scope resource managers never consider dependencies. There is also std::experimental::scope_exit() for RAII. Quote:
__________________
AKA Solomon/blowfish. Last edited by WhoCares; 06-12-2024 at 19:32. |
The Following User Says Thank You to WhoCares For This Useful Post: | ||
chants (06-13-2024) |
Thread Tools | |
Display Modes | |
|
|