Следует ли при использовании кода Win32 в вашем современном приложении на C ++ использовать правильное приведение?

Например, следующий состав можно найти в документации MSDN:

(LPTSTR)&lpMsgBuf

Стоит ли мне преобразовать это в:

static_cast<LPTSTR>(&lpMsgBuf);

Или я должен просто оставить все идиоматические части C-esque Win32, как они обычно встречаются в документации, и сохранить более идиоматический стиль / использование C ++ для остальной части моего кода?


person ApplePieIsGood    schedule 27.04.2009    source источник


Ответы (6)


Новые стили были введены не просто так: они более безопасны, содержат больше пояснений и комментариев, их легче увидеть и их легче найти.

Так что используйте их.

Говоря более пояснительно, мы имеем в виду, что вы не можете просто привести к чему-то, вы должны сказать почему вы выполняете приведение (я использую иерархию наследования (dynamic_cast), мое приведение определяется реализацией и, возможно, не переносимый (reinterpret_cast), я отбрасываю константу (const_cast) и т. д.).

Они намеренно длинные и уродливые, чтобы бросок бросался в глаза читателю (и препятствовать стилю программирования, в котором используется слишком много приведений).

Они безопаснее, потому что, например, вы не можете отказаться от константности, не сделав этого явно.

person tpdi    schedule 27.04.2009

Вместо того, чтобы добавлять в свой код приведение типов в старом или новом стиле, я бы воспользовался перегрузкой операторов C ++, чтобы добавить встроенные перегруженные версии функций Windows API, которые принимают параметры соответствующих типов. (Пока это задокументировано для новых разработчиков, надеюсь, это не будет слишком запутанным.)

Например, пятым параметром FormatMessage обычно является LPTSTR, но если вы передаете флаг FORMAT_MESSAGE_ALLOCATE_BUFFER, пятый параметр является указателем на LPTSTR. Поэтому я бы определил такую ​​функцию:

inline DWORD WINAPI FormatMessage(
  __in      DWORD dwFlags,
  __in_opt  LPCVOID lpSource,
  __in      DWORD dwMessageId,
  __in      DWORD dwLanguageId,
  __out     LPTSTR *lpBuffer,
  __in      DWORD nSize,
  __in_opt  va_list *Arguments
) {
    assert(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER);
    return FormatMessage(dwFlags, lpSource, dwMessageId, dwLanguageId, static_cast<LPTSTR>(&lpBuffer), nSize, Arguments);
}
person Josh Kelley    schedule 27.04.2009

Документация MSDN против стандарта C ++.

Я бы выбрал позднее. Я думаю, что это одно из правил, обсуждаемых и в эффективном C ++. Вы должны НЕ смешивать два стиля в одной программе.

person J.W.    schedule 27.04.2009

Вам вообще не нужно приведение типов - судя по всему, lpMsgBuf - это указатель на массив символов (из контекста неясно, являются ли они символами ANSI или широкими символами), который можно передавать напрямую различным Win32. функции.

Нет необходимости принимать адрес - если lpMsgBuf является статическим массивом (например, char lpMsgBuf[SIZE]), то получение его адреса является избыточным и эквивалентно адресу его первого элемента. Если это указатель, то взятие его адреса дает char** (или wchar_t**, если он широкий), что НЕ является тем, что вы хотите передать - если функция Win32 ожидает LPSTR (т.е. char*), и вы передаете ей char** приведите к char*, в результате будет много зла. Компилятор не позволит вам передать char** функции, ожидающей char* без приведения - использование приведения для отключения компилятора НЕПРАВИЛЬНО, поскольку компилятор пытается сказать вам что-то важное.

Если в этом случае вы не можете передать требуемый параметр без приведения, вы почти наверняка передаете его неправильно. Исправьте код, чтобы не было необходимости в приведении.

person Adam Rosenfield    schedule 27.04.2009
comment
Мы с тобой оба опоздали. Пока мы набирали текст, ответ был уже принят, в котором говорилось, что этот очень вероятный код с ошибками должен оставаться, скорее всего, с ошибками. Какая трата наших усилий. - person Windows programmer; 27.04.2009
comment
К сожалению, Windows API иногда действительно требует таких, казалось бы, плохих приведений. FormatMessage является примером. - person Josh Kelley; 27.04.2009
comment
@Windows программист: Верно, вздох @ Джош Келли: Верно, особенно в отношении некоторых сообщений Windows. WPARAM и LPARAM могут быть чем угодно под солнцем. Но без контекста конкретной функции, которую OP пытается вызвать, я предполагаю худшее. - person Adam Rosenfield; 27.04.2009
comment
Да, я просто привел это в качестве примера, вопрос больше о стиле, а не о конкретном примере, поэтому я думаю, что правильно отметил правильный ответ, но я поддержал его, так как он тоже полезен. Спасибо. - person ApplePieIsGood; 27.04.2009

Если lpMsgBuf уже является указателем на T-строку и если API ожидает, что указатель будет указывать непосредственно на T-строку, вам не следует использовать приведение вообще. Другими словами, если тип данных уже правильный, лучше не использовать ненужные преобразования. Причина в том, что чрезмерное использование приведений может скрыть от вас ошибки, в то время как приведение приводит к закрытию компилятора.

Если lpMsgBuf уже является указателем на T-строку и если API ожидает указатель на указатель на T-строку, но API ожидает, что этот указатель на указатель будет преобразован в указатель типа на T-строку (и API будет преобразовывать его назад при использовании), значит, вы все делаете правильно.

Если lpMsgBuf является указателем на что-то еще (правильного типа) и API ожидает указатель на указатель на ваш тип, но API ожидает эти игры с кастомами, значит, вы делаете это правильно.

Если lpMsgBuf является указателем на T-строку и если API ожидает указатель на T-строку, то при использовании оператора & вы создаете указатель на указатель на T-строку, которую API не ожидает, и используя приведение, вы говорите компилятору превратить этот указатель на указатель в указатель типа на T-строку, даже если значение по-прежнему указывает на указатель. Итак, вы успешно закрыли компилятор, но по-прежнему доставляете мусор вам или вашим клиентам.

Таким образом, без дополнительных сведений о том, какая страница MSDN сообщает вам создать указатель на указатель и играть в кастинговые игры для какого API, мы не можем угадать, правильно ли вы это делаете, или неправильно читаете MSDN, или MSDN сообщает писать плохой код.

Теперь, если приведение действительно правильное, то выбор используемого синтаксиса зависит от вашего стиля в остальной части вашей программы.

person Windows programmer    schedule 27.04.2009

По большей части пример кода MSDN Win32 совместим с C и C ++. Вполне допустимо (и когда-то было стандартной практикой) использовать простой старый C для написания программ Windows.

Поскольку приведение типов C ++ недоступно в C, изменение примеров для использования их преимуществ потребует поддержки двух копий образца кода. Поскольку код C работает так же, как и в C ++, проще просто оставить приведение в старом стиле.

person Eclipse    schedule 27.04.2009