2010-03-23 6 views
3

Quelle est la meilleure façon d'envoyer un flottant dans un message Windows à l'aide d'opérateurs de casting C++?Comment envoyer des flottants dans les messages de la fenêtre

La raison pour laquelle je pose la question est que l'approche qui m'est apparue en premier ne fonctionnait pas. Pour mémoire j'utilise la fonction standard de win32 pour envoyer des messages:

 
PostWindowMessage(UINT nMsg, WPARAM wParam, LPARAM lParam) 

Ce qui ne fonctionne pas:

  • En utilisant static_cast<WPARAM>() ne fonctionne pas depuis WPARAM est typedef à UINT_PTR et fera une conversion numérique de float à int, tronquant effectivement la valeur du float. L'utilisation de reinterpret_cast<WPARAM>() ne fonctionne pas car elle est destinée à être utilisée avec des pointeurs et échoue avec une erreur de compilation.

Je peux penser à deux solutions de contournement au moment:

  • En utilisant reinterpret_cast conjointement avec l'adresse de l'opérateur:
     
    float f = 42.0f; 
    ::PostWindowMessage(WM_SOME_MESSAGE, *reinterpret_cast<WPARAM*>(&f), 0); 
    
  • L'utilisation d'un syndicat:
     
    union { WPARAM wParam, float f }; 
    f = 42.0f; 
    ::PostWindowMessage(WM_SOME_MESSAGE, wParam, 0); 
    

Lequel de ceux-ci sont préférés? Y a-t-il une autre manière plus élégante d'accomplir ceci?

+1

Je ne recommanderais pas d'envoyer des pointeurs aux temporaires via quelque chose appelé "PostWindowMessage" car il est supposé être asynchrone. –

+0

Le pointeur est déréférencé lors de l'appel (l'étoile avant la distribution), aucune adresse temporaire n'est envoyée à PostWindowMessage –

+2

@Kirill Je n'émets pas de pointeurs sur les temporaires. Je prends l'adresse du flottant, en jetant cette adresse à un pointeur vers un WPARAM et en résolvant cette adresse dans un WPARAM qui est passé par la valeur. Les pointeurs sont seulement utilisés pour tromper le compilateur en ne convertissant pas la valeur. –

Répondre

3

Utilisez reinterpret_cast< WPARAM &>(f). Cette distribution n'est pas limitée aux pointeurs, elle fonctionne également avec des références.

+0

Merci! C'était exactement ce que je cherchais. –

2

Vous pouvez utiliser memcpy:

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

Je ne pense pas qu'il y ait une solution élégante à ce sujet, mais quoi que vous fassiez, je l'envelopper dans une fonction pour le rendre évident ce que je faisais à:

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

+1 C'est la manière la plus sûre de le faire. En théorie, utiliser 'reinterpret_cast' comme le fait l'OP est un comportement indéfini, bien qu'en pratique cela fonctionnera probablement ... –

+0

J'ajouterais' if (sizeof float == sizeof int) ' –

+0

@Kirill Je ne le ferais pas, mais Je pourrais ajouter une assertion. –

0

Je préférerais l'union. C'est le plus clair et le plus facile à comprendre. Il a aussi probablement le moins de comportement indéfini.

+0

Heh, joliment mis: "moindre quantité de comportement indéfini". : P –

+1

L'utilisation de l'union de cette manière (enregistrement dans un membre et lecture d'un autre) évoque un comportement indéfini. –

+0

S'il vous plaît examiner [cette publication] (https://stackoverflow.com/a/22026484/4975230), le type punning est défini comportement en C, mais pas en C++. Quelque chose comme 'memcpy' est probablement plus sûr que le code d'union équivalent. – jrh

0

J'irais la manière simple de prendre un pointeur sur votre float, jetant à un pointeur vers UINT et déréférencement le pointeur résultant:

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

Pour reconvertir vous faites le contraire. Faire le cast au niveau du pointeur s'assure que le compilateur n'essaiera pas de "conversion" derrière votre dos.

float f = *((float*)(&wParam)) 
+0

Cette réponse est très susceptible d'être correct, [parce que Visual Studio n'a pas d'aliasing strict] (https://stackoverflow.com/questions/37176461/does-visual-c-support-strict-aliasing), mais ce genre d'astuce échouera si vous essayez ceci sur un compilateur qui supporte [strict aliasing] (https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) comme GCC. Donc, c'est probablement bien pour win32, mais ne faites pas cela une habitude pour le code multi-plateforme. – jrh

Questions connexes