Exetools  

Go Back   Exetools > General > Source Code

Notices

Reply
 
Thread Tools Display Modes
  #1  
Old 06-07-2024, 14:41
WhoCares's Avatar
WhoCares WhoCares is offline
who cares
 
Join Date: Jan 2002
Location: Here
Posts: 422
Rept. Given: 10
Rept. Rcvd 20 Times in 16 Posts
Thanks Given: 46
Thanks Rcvd at 172 Times in 70 Posts
WhoCares Reputation: 20
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.
Reply With Quote
The Following 3 Users Gave Reputation+1 to WhoCares For This Useful Post:
blue_devil (06-07-2024), Fyyre (06-11-2024), sendersu (06-11-2024)
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  
Old 06-07-2024, 18:05
atom0s's Avatar
atom0s atom0s is offline
Family
 
Join Date: Jan 2015
Location: 127.0.0.1
Posts: 423
Rept. Given: 26
Rept. Rcvd 128 Times in 65 Posts
Thanks Given: 54
Thanks Rcvd at 817 Times in 301 Posts
atom0s Reputation: 100-199 atom0s Reputation: 100-199
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:
const std::shared_ptr<voidhandle(::OpenProcess(PROCESS_VM_READFALSEpid), ::CloseHandle); 
Usage is then similar to your setup by just using 'handle.get()' to obtain the actual handle.
__________________
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.
Reply With Quote
The Following 3 Users Say Thank You to atom0s For This Useful Post:
MarcElBichon (06-07-2024), SofTw0rm (06-11-2024), tonyweb (06-08-2024)
  #3  
Old 06-07-2024, 19:56
WhoCares's Avatar
WhoCares WhoCares is offline
who cares
 
Join Date: Jan 2002
Location: Here
Posts: 422
Rept. Given: 10
Rept. Rcvd 20 Times in 16 Posts
Thanks Given: 46
Thanks Rcvd at 172 Times in 70 Posts
WhoCares Reputation: 20
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_;
    }
"explicit" can be omitted if you prefer convenience to safety.

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:
Originally Posted by atom0s View Post
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':

Code:
const std::shared_ptr handle(::OpenProcess(PROCESS_VM_READ, FALSE, pid), ::CloseHandle);
Usage is then similar to your setup by just using 'handle.get()' to obtain the actual handle.
__________________
AKA Solomon/blowfish.

Last edited by WhoCares; 06-07-2024 at 22:28.
Reply With Quote
The Following 2 Users Say Thank You to WhoCares For This Useful Post:
SofTw0rm (06-11-2024), tonyweb (06-08-2024)
  #4  
Old 06-08-2024, 05:35
atom0s's Avatar
atom0s atom0s is offline
Family
 
Join Date: Jan 2015
Location: 127.0.0.1
Posts: 423
Rept. Given: 26
Rept. Rcvd 128 Times in 65 Posts
Thanks Given: 54
Thanks Rcvd at 817 Times in 301 Posts
atom0s Reputation: 100-199 atom0s Reputation: 100-199
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:
template<typename T>
struct auto_cleanup_impl;

template<>
struct auto_cleanup_impl<HANDLE>
{
    static 
void cleanup(HANDLE value)
    {
        if (
value != INVALID_HANDLE_VALUE) ::CloseHandle(value);
    }
};

template<typename T>
class 
auto_cleanup
{
    
std::shared_ptr<voidvalue_;

public:
    
auto_cleanup(T value)
        : 
value_valueauto_cleanup_impl<T>::cleanup }
    {}
    ~
auto_cleanup(void)
    {}

    
T get(void) const
    {
        return 
this->value_.get();
    }
}; 
Then in the same manner can be used as:
PHP Code:
auto_cleanup val(::OpenProcess(PROCESS_VM_OPERATIONFALSE1)); 
To allow for direct value usage of the underlying shared pointer, one can add the overloads needed:

PHP Code:
    operator T() const { return value_.get(); }

    
auto_cleanup<T>& operator=(const& val)
    {
        
this->value_.reset(valauto_cleanup_impl<T>::cleanup);
        return *
this;
    } 
Then additional constructors can be added if you need default value handling or other kinds of initialization.

PHP Code:
    // Implementations will depend on the supported types etc.
    
auto_cleanup()
        : 
value_nullptrauto_cleanup_impl<T>::cleanup }
    {} 
To extend the types supported, you only need to add an auto_cleanup_impl entry for the given type. For example, to add support for a FILE pointer, it can be added via:

PHP Code:
template<>
struct auto_cleanup_impl<FILE*>
{
    static 
void cleanup(FILEvalue)
    {
        if (
value) ::fclose(value);
    }
}; 
Some examples of using this with a HANDLE type would be:

PHP Code:
    // Wrapped initialization; type determined by the return of the called function..
    
auto_cleanup val1(::OpenProcess(PROCESS_VM_OPERATIONFALSE1));

    
// Default initialization; type determined by the value type..
    
auto_cleanup val2INVALID_HANDLE_VALUE };

    
// Default initialization; type determined by explicit template usage..
    
auto_cleanup<HANDLEval3{};

    
// Value usage after creation..
    
uint8_t buffer[1024]{};
    
auto_cleanup val4INVALID_HANDLE_VALUE };
    
val4 = ::OpenProcess(PROCESS_VM_OPERATIONFALSE1);
    ::
ReadProcessMemory(val4reinterpret_cast<LPCVOID>(0x1234ABCD), &buffer1024nullptr); 
__________________
Personal Projects Site: https://atom0s.com
Reply With Quote
The Following 3 Users Say Thank You to atom0s For This Useful Post:
MarcElBichon (06-08-2024), SofTw0rm (06-11-2024), tonyweb (06-08-2024)
  #5  
Old 06-11-2024, 13:45
WhoCares's Avatar
WhoCares WhoCares is offline
who cares
 
Join Date: Jan 2002
Location: Here
Posts: 422
Rept. Given: 10
Rept. Rcvd 20 Times in 16 Posts
Thanks Given: 46
Thanks Rcvd at 172 Times in 70 Posts
WhoCares Reputation: 20
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.


Quote:
Originally Posted by atom0s View Post
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:
__________________
AKA Solomon/blowfish.
Reply With Quote
The Following 2 Users Say Thank You to WhoCares For This Useful Post:
sendersu (06-11-2024), SofTw0rm (06-11-2024)
  #6  
Old 06-12-2024, 13:41
chants chants is offline
VIP
 
Join Date: Jul 2016
Posts: 807
Rept. Given: 43
Rept. Rcvd 50 Times in 31 Posts
Thanks Given: 719
Thanks Rcvd at 1,114 Times in 515 Posts
chants Reputation: 51
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.
Reply With Quote
  #7  
Old 06-12-2024, 19:19
WhoCares's Avatar
WhoCares WhoCares is offline
who cares
 
Join Date: Jan 2002
Location: Here
Posts: 422
Rept. Given: 10
Rept. Rcvd 20 Times in 16 Posts
Thanks Given: 46
Thanks Rcvd at 172 Times in 70 Posts
WhoCares Reputation: 20
There is no silver bullet

Such unique/scope resource managers never consider dependencies.

There is also std::experimental::scope_exit() for RAII.


Quote:
Originally Posted by chants View Post
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.
__________________
AKA Solomon/blowfish.

Last edited by WhoCares; 06-12-2024 at 19:32.
Reply With Quote
The Following User Says Thank You to WhoCares For This Useful Post:
chants (06-13-2024)
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is On



All times are GMT +8. The time now is 18:23.


Always Your Best Friend: Aaron, JMI, ahmadmansoor, ZeNiX, chessgod101
( 1998 - 2024 )