Reputation: 552
I have to write a reference counted wrapper class in C++11 for Win32 handles like HFONT, HWND, HMODULE and so on. I want to use a single WinHandle class that implicitly casts to all handle types (which are all a void* typedef). Unfortunately all handles have different functions to destroy the underlying object for example DestroyWindow(), CloseHandle(), DeleteObject() and so forth, thus i'd need a different class for every handle-type to implement the appropriate destroy-function. (that would be 47 classes + base class including user objects, gdi objects and kernel objects)
So is there a way to determine at runtime of which "type" the handle is or rather which function needs to be called? (In the documentation I only found the isWindow() function)
I already thought about using RTTI or calling all delete-functions until one of them succeeds. RTTI won't work because all HANDLE types are typedefs of void* and thus the same. The latter might work, but all handles must be unique for it to work properly (no GDI handle can ever have the same value as a user handle or kernel handle) otherwise it might cause bugs and memory leaks
Upvotes: 3
Views: 2790
Reputation: 21
Since the failure result for CreateFile is INVALID_HANDLE_VALUE (-1) I'm not certain this is a good solution all in all.
https://learn.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea
Rather than use something intended to refer to a memory address, implementing some sort of shared_resource or unique_resource type, which accepts template arguments or a single argument that encapsulates the characteristics of the wrapped type is probably the way to go.
Mine looks something like this:
template <typename TRAITS>
struct SharedHandle
{
....
}
and is used like this:
struct TestTraits
{
using HandleType = int;
static constexpr HandleType INVALID = 0;
static void Close(HandleType& h)
{
h = INVALID;
}
};
using SharedHandleTestType = SharedHandle<TestTraits>;
TEST(ClvLib_SharedHandle_Tests, SharedHandle_default)
{
auto tt = SharedHandleTestType();
EXPECT_FALSE(tt);
}
TEST(ClvLib_SharedHandle_Tests, SharedHandle_good)
{
auto tt = SharedHandleTestType(1);
EXPECT_TRUE(tt);
}
TEST(ClvLib_SharedHandle_Tests, SharedHandle_use)
{
auto tt = SharedHandleTestType(1);
auto result = [](int t)->int {return t + 1; }(tt);
auto expected = tt.Get() + 1;
EXPECT_EQ(expected, result);
}
TEST(ClvLib_SharedHandle_Tests, SharedHandle_copy1)
{
auto tt = SharedHandleTestType(1);
auto t2 = tt;
EXPECT_TRUE(tt);
EXPECT_TRUE(t2);
tt = 0;
EXPECT_FALSE(tt);
EXPECT_TRUE(t2);
}
TEST(ClvLib_SharedHandle_Tests, SharedHandle_copy2)
{
auto tt = SharedHandleTestType(1);
auto t2 = tt;
EXPECT_TRUE(tt);
EXPECT_TRUE(t2);
tt = 0;
EXPECT_FALSE(tt);
EXPECT_TRUE(t2);
t2.Release();
EXPECT_FALSE(tt);
EXPECT_FALSE(t2);
}
For Windows file handles:
struct WinHandleTraits_IHV
{
using HandleType = HANDLE;
static constexpr HandleType INVALID = INVALID_HANDLE_VALUE;
static void Close(HandleType& h)
{
CloseHandle(h);
}
};
using WinFileHandle = SharedHandle<WinHandleTraits_IHV>;
Upvotes: 2
Reputation: 2974
If the only thing you're looking for is a reference counted handle, why not just use shared_ptr
?
E.g :
shared_ptr<void> file( CreateFile(L"la.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL), CloseHandle);
DWORD written;
WriteFile(file.get(), //get handle
"ABC\r\n",
5,
&written,
NULL);
It doesn't have a big foot print int your code and you won't have to write 40 classes.
You can avoid passing the relevant close function each time by defining some function for each type of closing e.g :
auto make_handle_CloseHandle = [](HANDLE h){ return (shared_ptr<void>(h,CloseHandle)); };
auto file = make_handle_CloseHandle(CreateFile(L"la.txt", /*same ...*/));
DWORD written;
WriteFile( file.get(), //get handle
"ABC\r\n",
5,
&written,
NULL);
And put it in some header file under a relevant namespace that way you won't have to type the close function's name each time and users of those functions will know which function is called (according to the make_handle_*
name) which might make thing safer than trying to automagically identify the handle's type just by the handle.
Upvotes: 2