Компиляция с MSVC: отсутствует __has_builtin

Вопрос или проблема

Я пытаюсь написать код, независимый от платформы, чтобы программно установить точку останова. Я нашел std::breakpoint, которая, конечно, не существует до C++26. Я изучил предложение и нашел референсную реализацию. Я немного адаптировал её, включив стандартную версию для цели будущей совместимости, но начал с первой части референса, которая содержит интересный кусок работы препроцессора с использованием __has_builtin, которую я раньше не видел. Вот к чему я пришел:

// Если версия выше C++23, мы можем использовать стандартную функцию точки останова.
#if defined(__cplusplus) && __cplusplus > 202302L
  #include 
  #define breakpoint() std::breakpoint_if_debugging()
#endif

// Проверяем, есть ли в компиляторе уже доступная встроенная функция прерывания для отладки.
#if !defined(breakpoint) && defined(__has_builtin) && !defined(__ibmxl__)
  #if __has_builtin(__builtin_debugtrap)
    #define breakpoint() __builtin_debugtrap()
  #elif __has_builtin(__debugbreak)
    #define breakpoint() __debugbreak()
  #endif
#endif

// Не удалось получить функцию точки останова.
#if !defined(breakpoint)
  #error Не найдена простая функция точки останова.
#endif

Вот оно, intellisense считает, что путь __has_builtin(__debug_break) будет всем, что мне нужно для определения breakpoint (по крайней мере для Windows). Я попытался скомпилировать, чтобы проверить, и сразу же попал в путь с ошибкой.

  • Я не могу найти ничего, что явно отмечает существование __has_builtin при использовании MSVC, но, как я понимаю, __debug_break — это встроенная функция только для MSVC. Я что-то неправильно понимаю? Это для clang на Windows?
  • Есть ли параметр компилятора, который нужно включить, чтобы получить __has_builtin?
  • При наведении на __has_builtin(__debugbreak) intellisense показывает, что это сводится к 1. Это просто классическая ложь intellisense? Также показывается, что __has_builtin — это “предопределённый динамический макрос”, что звучит как “магическая функция, с которой работает компилятор”.

Ответ или решение

Вопрос, который вы задали, охватывает несколько аспектов использования предопределенных макросов и встроенных функций для реализации платформонезависимого прерывания выполнения программы. Давайте разберем каждый из ваших вопросов и предложим возможные решения.

1. __has_builtin в MSVC

Макрос __has_builtin был введен в стандарт C++ и поддерживается компиляторами, такими как Clang и GCC, но в MSVC его поддержка не является гарантированной. В MSVC вы можете встретить выражение __has_builtin, но оно не всегда верно для конкретных встроенных функций. Это может стать причиной того, что вы получаете ошибку компиляции при использовании этого макроса с __debugbreak.

Решение: Чтобы обойти эту проблему, вы можете использовать другую конструкцию, основанную на проверке наличия __debugbreak без использования __has_builtin. Например:

#if defined(__cplusplus) && __cplusplus > 202302L
  #include <debugging>
  #define breakpoint() std::breakpoint_if_debugging()
#endif

#if !defined(breakpoint)
  #if defined(_MSC_VER)
    #define breakpoint() __debugbreak()
  #else
    #error "Unsupported compiler. Please define a breakpoint method."
  #endif
#endif

2. Компилятор и опции

Многие компиляторы не требуют специальных опций для использования встроенных функций, таких как __debugbreak. Однако для MSVC вам может потребоваться убедиться, что используете правильную версию компилятора (обычно поддержка является) и включены соответствующие стандартные библиотеки (например, /std:c++latest для включения новых возможностей).

3. Intellisense и его поведение

Интеллиссенс в Visual Studio может иногда вводить в заблуждение, показывая, что __has_builtin возвращает значение 1, когда это не так. Это может быть связано с его предустановленными настройками или кэшом. Важно понять, что результаты, которые предоставляет Intellisense, могут не всегда отражать реальное поведение компилятора при сборке кода. Поэтому разработка должна проверяться на реальном компиляционном процессе, чтобы получить актуальные результаты.

Итоговое решение

Ваша конечная реализация может выглядеть следующим образом:

#if defined(__cplusplus) && __cplusplus > 202302L
  #include <debugging>
  #define breakpoint() std::breakpoint_if_debugging()
#endif

#if !defined(breakpoint)
  #if defined(_MSC_VER)
    #define breakpoint() __debugbreak() // MSVC
  #else
    #ifdef __has_builtin
      #if __has_builtin(__builtin_debugtrap)
        #define breakpoint() __builtin_debugtrap() // GCC/Clang
      #elif __has_builtin(__debugbreak)
        #define breakpoint() __debugbreak() // GCC/Clang
      #endif
    #endif
  #endif
#endif

#if !defined(breakpoint)
  #error "Не удалось найти простой метод прерывания."
#endif

Это решение проверяет наличие современных возможностей компилятора, использует встроенные функции MSVC и предоставляет более управляемый способ обработки разных платформ и возможностей компилятора.

Оцените материал
Добавить комментарий

Капча загружается...