Безопасно ли изменить этот c-cast на reinterpret_cast?

Я пытаюсь удалить приведение в стиле c из кода, над которым я работаю, и меня беспокоит единственная альтернатива.

Исходный код был:

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

Если я использую статическое приведение:

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

Я получаю сообщение об ошибке «static_cast»: невозможно преобразовать из «HWND» в «WPARAM», потому что нет допустимого преобразования между базовыми типами. Это оставляет мне «вариант дьявола»:

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

Насколько я понимаю, если static_cast невозможен, и это не связано с константностью, C-cast все равно должен выполнять reinterpret_cast, что означает, что базовый код должен возвращаться, , что означает, что это безопасно (пункт 3 в комментариях). Но я хотел бы подтвердить это, прежде чем просто менять код.

Безопасно ли это приведение в данном конкретном случае и как мне это подтвердить? Если нет, то какой альтернативный вариант?


person deworde    schedule 13.02.2014    source источник
comment
Да, это безопасно, однако вы можете просто продолжать использовать приведение в стиле C, которое распространено в коде Windows API / MFC, а IMO легче читать.   -  person dsi    schedule 13.02.2014
comment
Так же безопасно, как приведение целочисленного типа к void *.   -  person Michael Foukarakis    schedule 13.02.2014
comment
@MichaelFoukarakis WPARAM гарантированно будет той же ширины, что и указатель, поэтому здесь нет проблем   -  person dsi    schedule 13.02.2014
comment
@dsi Мне не нравится использовать C-Style, потому что он скрывает то, что вы делаете, но спасибо, что указали, что это общий шаблон в кодовой базе, с которой я работаю.   -  person deworde    schedule 13.02.2014
comment
@dsi Кстати, не могли бы вы дать ответ? Если не появится ничего лучше этого, этого будет достаточно для всех, кто задает такой вопрос.   -  person deworde    schedule 13.02.2014


Ответы (5)


Это безопасно, потому что WPARAM определяется как:

typedef UINT_PTR            WPARAM;

а суффикс _PTR означает, что тип достаточно большой, чтобы содержать указатель.

в то время как HWND:

 typedef HANDLE HWND;

где РУЧКА:

typedef void *HANDLE;

поэтому размер void * и UINT_PTR всегда одинаков. Если вы сохраните его в 64-битном приложении и попытаетесь прочитать в 32-битном приложении, у вас возникнут проблемы.

если вы все еще обеспокоены тем, безопасно ли выполнять такие приведения, вы можете выполнить поиск в источниках Visual Studio (в папке C: \ Program Files (x86) \ Microsoft Visual Studio 8 \), и вы найдете множество строк с reinterpret_cast<LPARAM>(...) и reinterpret_cast<WPARAM>(...) .

person marcinj    schedule 13.02.2014
comment
Вы можете безопасно хранить 64-битные HWND в 32-битных типах. См. stackoverflow.com/questions/18266626/ - person manuell; 13.02.2014
comment
Если STRICT определен, то HWND является typedef для struct HWND__* вместо _4 _ / _ 5_. - person Remy Lebeau; 13.02.2014
comment
@RemyLebeau да, но это все еще значение указателя, никто не разыменовывает HWND. Весь DECLARE_HANDLE предназначен только для того, чтобы добавить некоторую безопасность дескрипторам окон. - person marcinj; 13.02.2014

Да, это нормально, и это то, для чего reinterpret_cast предназначено, то есть подход «поверьте мне, я знаю, что я делаю», который C должен использовать.

person Sean    schedule 13.02.2014

возможно, теперь мы можем использовать std::bit_cast из C ++ 20 в качестве альтернативного способа, который должен быть более корректным.

под капотом std::bit_cast использует std::memcpy, который, я думаю, вы также можете использовать для преобразования с HWND на WPARAM

person haohaolee    schedule 14.07.2020

Он может работать на вашем компьютере, с вашим компилятором и с очень специфическим набором параметров компиляции для вашего компилятора.

Вы приводите один тип указателя к другому. Это не «безопасно». Ваш код нарушает строгое правило псевдонима. Это позволяет вашему компилятору интерпретировать ваш код совершенно иначе, чем вы планировали. Ваш код может работать, а может и нет. Вы вызываете неопределенное поведение.

person David Hammen    schedule 13.02.2014
comment
Хотя это правда, в этом конкретном случае, как говорили другие ответы, это единственный способ достичь необходимого результата. - person deworde; 13.02.2014
comment
Кроме того, это не undefined. Поведение четко определено для базового преобразования приведения в X и обратного преобразования из X, для чего и существует reinterpret_cast. На самом деле использование WPARAM без возврата к дескриптору - это совсем другое дело, поэтому я задал этот вопрос. - person deworde; 13.02.2014

Нет, как правило. Актеры в стиле C попытаются сделать то, что правильно. Обычно это означает попытку static_cast и только если это невозможно сделать reinterpret_cast

Взгляните на следующий код C ++

#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);
}

Результаты

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

Другими словами, слепо заменять приведение типов в стиле C на reinterpret_casts небезопасно.

person doron    schedule 13.02.2014
comment
Спасибо и хороший ответ для общего случая, но я осознаю риски того, что я назвал вариантом дьявола, я спрашиваю об этом конкретном случае, поэтому я не делаю это вслепую . - person deworde; 13.02.2014