Существует ли простое одноразовое решение проблемы загрязнения пространства имен препроцессора, представленное WIN32 API? - PullRequest
5 голосов
/ 06 ноября 2010

Как мы все знаем, в том числе <windows.h> загрязняет все пространства имен в C ++, имея один препроцессор #define для каждой функции Win32 API, которая может принимать многобайтовый ввод или ввод UTF-16. Пример:

#ifdef UNICODE
#define CreateFont CreateFontW
#else
#define CreateFont CreateFontA
#endif

Я использовал родной Win32 API уже несколько лет, но я почти сдаюсь! В любом нетривиальном проекте достаточно столкновений имен, чтобы ваше лицо стало синим. Пожалуйста, о, пожалуйста, может кто-нибудь придумать решение, которое не требует, чтобы я #undef определял такие макросы в каждом отдельном случае после факта? Я хочу предпринять позитивные действия, прежде чем это вызовет какие-либо ошибки .

И я всегда использую Unicode / UTF-16, поэтому в случае CreateFont я бы вызвал CreateFontW непосредственно в моем коде; Я бы не использовал макрос определения. Есть ли кто-нибудь, у кого есть решение этой проблемы, например, полный заголовок с # undef для переноса вместе с windows.h включает?

Примером ошибки, которая появляется повсюду, является тот факт, что используется общее имя, такое как «GetMessage ()».

font.cpp(78) : error C2039: 'GetMessageW' : is not a member of 'FontManager'

Когда все, что вы хотите, это чтобы ваш класс имел функцию-член void GetMessage (). Так расстраивает.

Ответы [ 2 ]

11 голосов
/ 06 ноября 2010

Самое чистое решение - не вызывать функции Windows API непосредственно в вашей программе, а вместо этого написать слой абстракции, который может вызывать нужные вам функции. Таким образом, вам нужно всего лишь включить заголовки Windows API в несколько исходных файлов, и макросы намного более управляемы.

Это также имеет дополнительное преимущество, заключающееся в улучшении переносимости вашего кода, поскольку только небольшая его часть будет полагаться на сам Windows API, а все остальное будет вызывать ваш уровень абстракции.

Даже если вы не заботитесь о переносимости, уровень абстракции все еще имеет много преимуществ: ваш код будет легче читать и легче обслуживать, потому что сложность выполнения вызовов API будет инкапсулирована в уровне абстракции (некоторые из функций Windows API есть много функций, которые вам обычно не нужны).

Поскольку вы используете C ++, уровень абстракции также позволяет преобразовывать коды ошибок Windows API в исключения, если вы предпочитаете использовать исключения.

Уровень абстракции также помогает сделать ваш код более легко тестируемым (намного проще издеваться над уровнем абстракции, чем над частью или всем Windows API).

4 голосов
/ 09 февраля 2014

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

  • Вы должны религиозно использовать pimpl idiom , чтобы сохранить все типы WinAPIиз заголовочных файлов уровня абстракции.Заголовки Windows должны быть включены только в файлы реализации (.cpp), а не в заголовки.
  • На практике вам приходится создавать множество собственных классов, которые по существу захватываютта же информация, что и базовый тип WinAPI (например, LOGFONT), потому что клиент интерфейса абстракции должен передать и получить ее.В конечном итоге вам понадобится много стандартного кода для преобразования между типами уровня абстракции и типами WinAPI.
  • Большой рефакторинг, необходимый для введения уровня абстракции, может быть большой работой в унаследованной кодовой базе, особенно если этокод используется многими проектами.

Реализация уровня абстракции требует включения заголовков окон.Предположим, что один из независимых от платформы типов, введенных вами для включения уровня абстракции, имеет метод, называемый GetFont.Независимый от платформы код, который опирается на него, увидит GetFont, потому что в этом блоке перевода не будет упоминания <windows.h>.Но файл реализации (файл .cpp), который реализует уровень абстракции в Windows, будет конфликтовать с макросом препроцессора GetFont, поскольку он должен включать <windows.h>.Таким образом, использование pimpl ограничивает масштаб проблемы, но не решает ее полностью.

То, что я начал делать, как в более новом коде, который использует хорошие уровни абстракции, так и в более старом коде, который не был хорошО предотвращении утечек в платформу имен для конкретных платформ является создание оболочек для заголовков WinAPI.Я заменяю все строки #include <windows.h> в коде, которым я управляю, на #include "apiwrappers/windows.h", который является ссылкой на файл, который выглядит примерно так:

// Prevents Windows headers from defining macros called min and max, which
// conflict with identifiers in the C++ standard library.
#ifndef NOMINMAX
#define NOMINMAX
#endif

// Distinguishes between different types of handles so that we get better
// error checking at compile time.
#ifndef STRICT
#define STRICT
#endif

#include <windows.h>  // the "real" <windows.h>

// Lowercase far and near are perfectly reasonable names for local variables
// and parameters, so we don't want redefinitions of them.
#undef far
#undef near

// But lots of other Win32 headers use FAR and NEAR, which are typically
// defined to far and near.  We need them to be empty.
#ifdef FAR
#undef FAR
#define FAR
#endif
#ifdef NEAR
#undef NEAR
#define NEAR
#endif

// Windows defines macros to select between the "ANSI" and "wide" versions
// of many API functions (e.g., GetMessage macro expands to GetMessageA or
// GetMessageW).  We undefine many of these here to avoid naming collisions.
// Call the specific -W functions explicitly instead of relying on these
// macros.
#undef GetFont
#undef GetMessage
// ...

Я увеличиваю список #undefS, как я разрешаю столкновения.У меня нет исчерпывающего предложения.

Для других заголовков WinAPI я делаю нечто подобное, но с остальными они гораздо проще.

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