3

What is the best way to send a float in a windows message using c++ casting operators?

The reason I ask is that the approach which first occurred to me did not work. For the record I'm using the standard win32 function to send messages:

PostWindowMessage(UINT nMsg, WPARAM wParam, LPARAM lParam)

What does not work:

  • Using static_cast<WPARAM>() does not work since WPARAM is typedef'ed to UINT_PTR and will do a numeric conversion from float to int, effectively truncating the value of the float.
  • Using reinterpret_cast<WPARAM>() does not work since it is meant for use with pointers and fails with a compilation error.

I can think of two workarounds at the moment:

  • Using reinterpret_cast in conjunction with the address of operator:
    float f = 42.0f;
    ::PostWindowMessage(WM_SOME_MESSAGE, *reinterpret_cast<WPARAM*>(&f), 0);
    
  • Using an union:
    union { WPARAM wParam, float f };
    f = 42.0f;
    ::PostWindowMessage(WM_SOME_MESSAGE, wParam, 0);
    

Which of these are preffered? Are there any other more elegant way of accomplishing this?

Yngve Hammersland
  • 1,634
  • 2
  • 14
  • 28
  • 1
    I'd not recommend to send pointers to temporaries via something called "PostWindowMessage" since it is supposed to be asynchronous. – Kirill V. Lyadvinsky Mar 23 '10 at 09:51
  • The pointer is dereferenced at the call (the star before the cast), no temporary address is sent to PostWindowMessage – Vincent Robert Mar 23 '10 at 10:09
  • 2
    @Kirill I'm not sending pointers to temporaries. I'm taking the address of the float, casting that address to a pointer to a WPARAM and resolving that address into a WPARAM which gets passed by value. The pointers are only used to trick the compiler into not converting the value. – Yngve Hammersland Mar 23 '10 at 10:10
  • @Vincent Robert, Ok, I see it now. – Kirill V. Lyadvinsky Mar 23 '10 at 10:13

4 Answers4

3

Use reinterpret_cast< WPARAM &>(f). This cast is not restricted to pointers, it also works with references.

MSalters
  • 173,980
  • 10
  • 155
  • 350
2

You could use memcpy:

#include <memory.h>
int main() {
    float f = 1.0;
    int n;
    memcpy( &n, &f, sizeof(f) );
}

I don't think there is an elegant solution to this, but whatever you do, I'd wrap it in a function to make it obvious what I was up to:

int FloatToInt( float f ) {
    int n;
    assert( sizeof(n) == sizeof(f) );
    memcpy( &n, &f, sizeof(f) );
    return n;
}
1

I would prefer the union. It is the clearest and easiest to understand. It also probably has the least amount of undefined behavior.

Gabe
  • 84,912
  • 12
  • 139
  • 238
  • Heh, nicely put: "least amount of undefined behaviour". :P – Yngve Hammersland Mar 23 '10 at 10:11
  • 1
    Using the union in this way (saving in one member and reading from another) evokes undefined behavior. – John Dibling Mar 23 '10 at 12:42
  • Please review [this post](https://stackoverflow.com/a/22026484/4975230), type punning is defined behavior in C, but not in C++. Something like `memcpy` is probably safer than the equivalent union code. – jrh Dec 26 '17 at 15:24
0

I'd go the simple way of taking a pointer to your float, casting it to a pointer to UINT, and then dereferencing the resulting pointer:

WPARAM wParam = *((UINT*)(&f));

To convert it back you do the opposite. Doing the cast at pointer-level makes sure the compiler won't try any "conversion" behind your back.

float f = *((float*)(&wParam))
Koro
  • 687
  • 3
  • 4
  • This answer is very likely to be okay, [because Visual Studio doesn't have strict aliasing](https://stackoverflow.com/questions/37176461/does-visual-c-support-strict-aliasing), but this sort of trick will fail if you try this on a compiler that supports [strict aliasing](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) like GCC. So this is probably fine for win32, but don't make this a habit for cross platform code. – jrh Dec 26 '17 at 15:34