Как мне разрешить конфликты имен с заголовками, которые используют препроцессор для переопределения имен общих функций? - PullRequest
0 голосов
/ 15 ноября 2018

РЕДАКТИРОВАТЬ: Я нашел похожий вопрос, и ответы в основном таковы, что windows.h плохой, и вы должны либо переименовать свои функции, либо #undef макросы: Библиотека других файлов # конфликт определения имен

Тем не менее, я считаю, что мой по-прежнему отличается из-за противоречивого поведения LoadLibrary при сборках отладки и выпуска.


Я программирую на Windows с использованием Visual Studio, и у меня возникли некоторые специфические проблемы с директивами препроцессора, используемыми в windows.h, и с заголовками, которые он включает.

Наш проект имел функцию в своем собственном пространстве имен, MyProject::FileManager::CreateFile(). После включения windows.h наш код не смог скомпилироваться из-за ошибки компоновщика, заявившего, что он не может разрешить MyProject :: FileManager :: CreateFileW (обратите внимание на W в конце имени функции). Это была не статическая функция, это была функция-член объекта FileManager, который вызывался с помощью file_manager.CreateFile(...).

При выделении функции в Visual Studio всплывающая подсказка отображала следующее:

#define CreateFile CreateFileW

Мы были озадачены, но просто переименовали функцию в качестве обходного пути. Однако позже мы столкнулись с похожей проблемой с функцией LoadLibrary, которую мы пытались использовать из Windows API. Компиляция в режиме отладки, LoadLibrary была определена как LoadLibraryW(), которая приняла LPCWSTR (широкая строка) в качестве параметра. Когда я пытался построить в режиме Release, эта функция была теперь определена как LoadLibraryA(), которая принимает обычный LPCSTR. Это сломало нашу сборку, потому что код был написан в предположении, что LoadLibrary принял LPCWSTR.

Итак, мой вопрос: как программист должен иметь с этим дело? Должен ли я просто обернуть свои вызовы в LoadLibrary с помощью проверки # ifdef для режима отладки или выпуска? Или есть более простое решение?

Кроме того, я нашел интересный заголовочный файл на github, который, кажется, был создан с единственной целью # undef'ing всех этих имен функций:

https://github.com/waTeim/poco/blob/master/include/Poco/UnWindows.h

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Проблема здесь в том, что windows.h пытается поддерживать две разные строковые модели: строки, состоящие из однобайтовых символов, и строки, закодированные в Unicode (определяется Microsoft как двухбайтовые символы). Почти все функции Windows API имеют две разные версии, одна из которых принимает однобайтовые строки символов, а другая - двухбайтовые строки символов. Вы должны написать свой код с общими именами (такими как CreateFile, LoadLibrary и т. Д.) И позволить windows.h позаботиться о сопоставлении этих имен с реальными функциями API. Для однобайтовых символов это два CreateFileA и LoadLibraryA; для двухбайтовых символов это CreateFileW и LoadLibraryW. И, конечно, есть еще несколько миллиардов. Вы выбираете модель во время компиляции, определяя макрос UNICODE в каждой единице компиляции.

Кстати, суффикс 'A' обозначает «ANSI», а суффикс 'W' обозначает «широкий символ».

Это особенно коварно, когда вы пишете код, который пытается изолировать зависимости Windows в несколько исходных файлов. Если вы напишите класс, который имеет функцию-член с именем CreateFile, это будет видно в исходных файлах, которые не используют windows.h как CreateFile, и в исходных файлах, которые действительно используют windows.h как CreateFileA или CreateFileW. Результат: ошибки компоновщика.

Существует несколько способов решения этой проблемы:

  1. всегда #include <windows.h> в каждом заголовочном файле; это убийство производительности компилятора, но оно будет работать. (windows.h был основным мотиватором для скомпилированных заголовков в ранних компиляторах C ++, ориентированных на windows)

  2. всегда используйте имя доктора, CreateFileA или CreateFileW; это будет работать, но за счет потери гибкости возможности изменять базовую строковую модель при выполнении вызовов API; зависит ли это от вас.

  3. не использовать ни одно из имен Windows API; потенциально больно, если вы используете то же соглашение об именах, что и в Windows; совсем не больно, если вы используете регистр «змея», т. е. все строчные буквы с подчеркиванием для разделения слов. Например, create_file. В качестве альтернативы используйте локальный префикс или суффикс: MyCreateFile или CreateFileMine.

0 голосов
/ 15 ноября 2018

Я обычно делаю несколько вещей, чтобы справиться с этим:

  • Изолировать все системные вызовы Windows в слое, специфичном для Windows.Например, если я работаю с API файловой системы, у меня обычно есть win/filesystem.h и win/filesystem.cpp, чтобы обернуть все вызовы.(Это также хорошее место для преобразования ошибок Win32 в std::system_error исключения, удаления ненужных / устаревших / зарезервированных параметров и, как правило, для повышения совместимости API Windows с C ++.)
  • Избегайте использования типов, специфичных для Windows.Разрешение таких определений, как DWORD, LPTSTR и BOOL для проникновения на все уровни вашего кода, значительно затрудняет работу с Windows.h.(И портирование тоже.) Единственными файлами, которые должны #include <Windows.h>, должны быть файлы C ++ оболочки.
  • Избегайте использования макросов перенаправления Windows самостоятельно.Например, ваш слой-обертка должен вызывать CreateFileW или CreateFileA напрямую, а не полагаться на макрос.Таким образом, вам не нужно зависеть от настроек проекта Unicode / Multi-byte.

Пример

win/filsystem.h может содержать такие определения:

namespace win32
{
  class FileHandle
  {
    void* raw_handle_;
  public:
    // The usual set of constructors, destructors, and accessors (usually move-only)
  };

  FileHandle CreateNewFile(std::wstring const& file_name);
  FileHandle OpenExistingFile(std::wstring const& file_name);
  // and so on...
}

Любая часть вашего кода может включать этот файл для доступа к API файловой системы.Поскольку win/filesystem.h сам по себе не включает <Windows.h>, клиентский код не будет загрязнен различными макросами Win32.

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