Daniel Maczak Daniel Maczak - 24 days ago 6
C++ Question

Cast DWORD_PTR to class and vice versa without c-style cast

According to Herb Sutter's C++ Coding Standards: 101 Rules, Guidelines, and Best Practices programmer should avoid c-style casting:


C-style casts have different (and often dangerous) semantics depending on context, all disguised behind a single syntax. Replacing C-style casts with C++-style casts helps guard against unexpected errors


I am trying to pass pointer
p_ctrl
to WinAPI callback function, for which I want to use DWORD_PTR parameter of callback function (below example is working, but contains C-style casts where commented):

WndCtrls* WndCtrls::Button ( WndCtrls* const p_ctrl, HWND hwnd, RECT const &rc )
{
p_ctrl->ctrl = CreateWindowEx (
0,
L"BUTTON",
p_ctrl->w_classNameButton.c_str (),
WS_VISIBLE | WS_CHILD | BS_OWNERDRAW,
rc.left,
rc.top,
rc.right,
rc.bottom,
hwnd,
0,
(HINSTANCE)GetWindowLongPtr ( hwnd, GWL_HINSTANCE ), // Problematic C-style cast for which I already know workaround
p_ctrl
);
SetWindowSubclass ( p_ctrl->ctrl, WndCtrls::CtrlProc, 0, (DWORD_PTR)p_ctrl ) ) // C-style cast

return p_ctrl;
}

LRESULT CALLBACK WndCtrls::CtrlProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
WndCtrls* const p_ctrl = (WndCtrls*)dwRefData; // Problematic C-style cast
switch ( message )
{
...
}
return DefSubclassProc ( hwnd, message, wParam, lParam );
}


I already tried dynamic cast, but that gives me errors.
reinterpret_cast
should not be used at all (according to Sutter).

Please is there a way to do those casts using c++ provided functions?

Answer

Sutter's advice is just that: advice. They aren't hard and fast rules; they're just suggestions that encourage you to write safer, more robust code.

This is one of those cases where the advice doesn't work.

In many places, the Windows API needs to be able to pass data that may or may not be a pointer. As such, it uses a pointer-sized integer, using a C-style cast (remember that the Windows API is primarily C-based) to turn the integer into a pointer. This is safe because the documentation requires it to be: if you give Windows a garbage pointer value, you're breaking that function's rules!

The standard C/C++ names for the pointer-sized integers are intptr_t (signed) and uintptr_t (unsigned). However, Windows predates C99 and C++11 (when these were introduced), so it uses its own names: LONG_PTR (signed) and DWORD_PTR and ULONG_PTR (unsigned). In addition, WPARAM, LPARAM, and LRESULT are also pointer-sized, since window messages often need to deal with pointers.

So go ahead and use that C-style cast or that reinterpret_cast<>, whichever one you prefer. The other casts won't work because you need to interpret an integer as a pointer, which the other casts won't let you do.

You may need these anyway because there are other places that, because the Windows API needs to not only be in C but also be usable from other languages, subclassing is replaced by having an object of the struct to derive from as the first element of the dervied struct. This is most apparent in the WM_NOTIFY message, where all possible notification structs do this with NMHDR. Just keep this in mind.