Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Questions regarding "warning C4312: 'type cast'"

Tags:

c++

64-bit

winapi

This is the code I have:

HWND WebformCreate(HWND hParent, UINT id)
{
    return CreateWindowEx(0, WEBFORM_CLASS, _T("about:blank"),
        WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 0, 0, 100, 100, hParent,
        (HMENU)id, GetModuleHandle(NULL), 0);
}

This is the warning I get:

warning C4312: 'type cast' : conversion from 'UINT' to 'HMENU' of greater size

These are the questions I have:

  1. Why does the compiler think it's a bad idea to cast to a bigger type?
  2. What's the best way to get rid of the warning? (I don't want to disable it.)
  3. Doing a double type cast like this: (HMENU)(UINT_PTR)id gets rid of the warning. Why/how?
  4. Disabling "Detect 64-bit Portability Issues" (Wp64) also gets rid of the warning. Why is Wp64 deprecated? Should I have it on?
like image 750
Tobbe Avatar asked Aug 25 '10 20:08

Tobbe


3 Answers

You're casting a 32bit UINT to a 64bit pointer. That's suicide - you're trying to point to something but forgot half it's location! You absolutely MUST take a UINT_PTR. When you cast a pointer to an int, the behaviour is only OK if the int has the same size as the pointer. Else, it's the end of your application's runtime courtesy of an access violation.

Edit:
Why does the compiler think it's a bad idea to cast to a bigger type?
R.E. above

What's the best way to get rid of the warning? (I don't want to disable it.)
Fix the problem. This code will almost certainly instacrash.

Doing a double type cast like this: (HMENU)(UINT_PTR)id gets rid of the warning. Why/how?
It happens because casting a UINT to a UINT_PTR is perfectly valid- UINT_PTR is just an integral type, there's no loss of data.

Disabling "Detect 64-bit Portability Issues" (Wp64) also gets rid of the warning. Why is Wp64 deprecated? Should I have it on?
It's deprecated because, actually, I can't quite remember why. I think it warns a little too readily. But for the basic "Don't cast integral types and pointers", you should definitely leave it on.

like image 130
Puppy Avatar answered Oct 21 '22 10:10

Puppy


The type that's hiding behind the HMENU name is actually a pointer type. The compiler is telling you that casing a smaller integer type to a larger pointer type makes no sense, since the resultant pointer value will be "incomplete", i.e. the higher order bits if the pointer value will be filled with zeros. The latter makes very little sense with pointer types.

In your particular case this is safe, since this HMENU value is not really supposed to be a pointer that points anywhere. However, the compiler does not know that, which is why it issues a warning. Use a larger integer type as an intermediate type in the cast and the warning shall go away (you suggested that yourself), since in this case you are doing two casts: smaller integer to a larger integer, and then larger integer to a pointer. Smaller integer to a larger integer is an arithmetic cast, for which it makes perfect sense to fill the higher-order bits with zeros (the represented value does not change), so there will be no warning for it.

like image 19
AnT Avatar answered Oct 21 '22 09:10

AnT


Why does the compiler think it's a bad idea to cast to a bigger type?

Casting between types of different size is generally a problematic operation, because the source type might not be able to represent all values needed for the target.

What's the best way to get rid of the warning? (I don't want to disable it.)

I'd say, use HMENU everywhere and declare your function as

HWND WebformCreate(HWND hParent, HMENU id)

Doing a double type cast like this: (HMENU)(UINT_PTR)id gets rid of the warning. Why/how?

UINT_PTR is an integer type large enough to hold all pointer values, therefore the warnings go away.

HMENU is a pointer type. This doesn't mean a value of a hMenu is actually a pointer, but this is a hack that disallows you to implicitly mix eg. HMENU and HWND (because HMENU and HWND are something like struct _hMENU* and struct _hWND*, respectively, which are not compatible, whereas UINTs would be).

like image 2
jpalecek Avatar answered Oct 21 '22 11:10

jpalecek