При использовании кода Win32 в вашем современном приложении C ++, вы должны использовать правильное приведение? - PullRequest
4 голосов
/ 27 апреля 2009

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

(LPTSTR)&lpMsgBuf

Должен ли я преобразовать это в:

static_cast<LPTSTR>(&lpMsgBuf);

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

Ответы [ 6 ]

12 голосов
/ 27 апреля 2009

Новые приведения в стиле были введены по причине: они более безопасны, более объяснительны / комментируются, легче видны и легче для поиска.

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

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

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

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

6 голосов
/ 27 апреля 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);
}
3 голосов
/ 27 апреля 2009

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

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

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

3 голосов
/ 27 апреля 2009

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

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

0 голосов
/ 27 апреля 2009

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

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

0 голосов
/ 27 апреля 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 говорит вам писать плохой код.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...