Все функции std::variant
, которые могут выдавать std::bad_variant_access
, помечены как доступные, начиная с macOS 10.14 (и соответствующих iOS, tvOS и watchOS) в стандартных заголовочных файлах.Это связано с тем, что виртуальный метод std::bad_variant_access::what()
не является inline
и поэтому определен в libc++.dylib
(предоставляется ОС).
Существует несколько обходных путей (все технически неопределенное поведение ), упорядоченный по моим личным предпочтениям:
1) Захватить в реализацию
std::visit
только бросает, если один из вариантов аргумента valueless_by_exception
.Изучение реализации дает подсказку, как использовать следующий обходной путь (предполагая, что vs
является пакетом параметров):
if (... && !vs.valueless_by_exception() ) {
std::__variant_detail::__visitation::__variant::__visit_value(visitor, vs...);
} else {
// error handling
}
Con: Может прерваться в будущих версиях libc ++.Уродливый интерфейс.
Pro: Компилятор, вероятно, будет кричать на вас, когда он сломается и обходной путь может быть легко адаптирован.Вы можете написать оболочку против уродливого интерфейса.
2) Подавить ошибку компилятора доступности ...
Добавить _LIBCPP_DISABLE_AVAILABILITY
в настройку проекта Макросы препроцессора (GCC_PREPROCESSOR_DEFINITIONS
)
Con: Это также подавит других охранников доступности (shared_mutex
, bad_optional_access
и т. Д.).
2a) ... и просто используйтеон
Оказывается, он уже работает в High Sierra , а не только Mojave (я тестировал до 10.13.0).
В 10.12.6 и ниже вы получаете ошибку времени выполнения:
dyld: Symbol not found: __ZTISt18bad_variant_access
Referenced from: [...]/VariantAccess
Expected in: /usr/lib/libc++.1.dylib
in [...]/VariantAccess
Abort trap: 6
, где первая строка деформируется в _typeinfo for std::bad_variant_access
.Это означает, что динамический компоновщик (dyld
) не может найти виртуальную таблицу, указывающую на метод what()
, упомянутый во введении.
Con: Работает только на определенных версиях ОС, выузнайте только во время запуска, если он не работает.
Pro: Поддерживает оригинальный интерфейс.
2b) ... и предоставьте собственную реализацию исключений
Добавьте следующие строки в один из исходных файлов вашего проекта:
// Strongly undefined behaviour (violates one definition rule)
const char* std::bad_variant_access::what() const noexcept {
return "bad_variant_access";
}
Я протестировал это дляавтономный двоичный файл 10.10.0, 10.12.6, 10.13.0, 10.14.1, и мой пример кода работает даже тогда, когда вызывается выброс std::bad_variant_access
, перехват его на std::exception const& ex
и вызов виртуального ex.what()
.
Con: Я предполагаю, что этот трюк сломается при использовании RTTI или обработке исключений через двоичные границы (например, в разных библиотеках общих объектов).Но это только предположение, и именно поэтому я поставил этот обходной путь последним: я понятия не имею, когда он сломается и каковы будут симптомы.
Pro: Поддерживает оригинальный интерфейс.Вероятно, будет работать на всех версиях ОС.