Question

I'm trying to remove a c-style cast from some code I'm working on, and I have concerns about the only alternative.

The original code was:

WPARAM param = (WPARAM)(GetDlgItem(IDC_WORKFLOW).m_hWnd);
this->PostMessage(WM_NEXTDLGCTL, param, TRUE);

If I use a static cast:

WPARAM param = static_cast<WPARAM>(GetDlgItem(IDC_WORKFLOW).m_hWnd);
this->PostMessage(WM_NEXTDLGCTL, param, TRUE);

I get the error, 'static_cast' : cannot convert from 'HWND' to 'WPARAM', because there's no valid conversion between the underlying types. This leaves me with "the devil's option":

WPARAM param = reinterpret_cast<WPARAM>(GetDlgItem(IDC_WORKFLOW).m_hWnd);
this->PostMessage(WM_NEXTDLGCTL, param, TRUE);

As I understand it, if a static_cast is impossible, and this isn't related to constness, the C-cast must be doing a reinterpret_cast anyway, which means that the underlying code must be casting back, which means this is safe (point 3 in remarks). But I'd like to confirm this, before just changing the code.

Is this cast safe in this specific instance, and how do I confirm this? If not, what is the alternative?

Was it helpful?

Solution

It is safe, because WPARAM is defined as:

typedef UINT_PTR            WPARAM;

and _PTR suffix means the type is big enough to hold a pointer.

while HWND is:

 typedef HANDLE HWND;

where HANDLE is:

typedef void *HANDLE;

so size of void* and UINT_PTR are always the same. If you would store it in 64bit application and try read in 32 bit application, then you would get in trouble.

if you are still concenred if that is safe to do such casts, you can search Visual Studio sources (in C:\Program Files (x86)\Microsoft Visual Studio 8\ folder), and you will find lots of lines with reinterpret_cast<LPARAM>(...) and reinterpret_cast<WPARAM>(...).

OTHER TIPS

Yes, this is fine, and is what reinterpret_cast is intended for, ie the "trust me I know what I'm doing" approach that C has to casting.

maybe now we can use std::bit_cast from C++20 as an alternative way, which should be more well-behaved.

under the hood, std::bit_cast uses std::memcpy, which I think you can also use to cast from HWND to WPARAM

It might work on your computer, with your compiler, and with a very specific set of compilation options for your compiler.

You are casting one pointer type to another. That is not "safe". Your code is violating the strict aliasing rule. This enables your compiler to interpret your code in ways very different from what you intended. Your code might work, it might not. You are invoking undefined behavior.

No as a general rule. The C style cast will try to do what is right. This normally means trying a static_cast and only if this is impossible doing a reinterpret_cast

Take a look at the following C++ code

#include <stdio.h> 
#include <new>

class Base
{
public:
    int base;
};

class Extension
{
public:
    int extension;
};

class Derived : public Base, public Extension
{
public:
    int derived;
};

int main()
{
    Derived* der = new Derived;
    der->base = 1;
    der->extension = 2;
    der->derived = 3;

    Extension* ext = (Extension*)der;
    printf ("The value of ext->extension = %d\n",ext->extension);
    ext = reinterpret_cast<Extension*>(der);  
    printf ("The value of ext->extension = %d\n",ext->extension);
}

The results are

c:\work>test
The value of ext->extension = 2    
The value of ext->extension = 1

In other words it is not safe to blindly replace C style casts with reinterpret_casts.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top