Xcode 10 вызов недоступной функции std :: visit - PullRequest
0 голосов
/ 13 сентября 2018

При компиляции следующей программы с Xcode 10 GM:

#include <iostream>
#include <string>
#include <variant>

void hello(int) {
    std::cout << "hello, int" << std::endl;
}

void hello(std::string const & msg) {
    std::cout << "hello, " << msg << std::endl;
}

int main(int argc, const char * argv[]) {
    // insert code here...
    std::variant< int, std::string > var;

    std::visit
    (
        []( auto parameter )
        {
            hello( parameter );
        },
        var
     );

    return 0;
}

Я получаю следующую ошибку:

main.cpp: 27: 5: вызов функции недоступности 'посещение': введено в macOS 10.14

Однако, если я изменил цель развертывания min на macOS 10.14, код скомпилируется нормально, и он работает, даже если я использую macOS 10.13.

Так какstd::visit является шаблоном функции и не должен зависеть от версии ОС (что я доказал, запустив код на более низкой версии Mac, чем фактически поддерживается), следует ли это рассматривать как ошибку и сообщать об этом Apple или это ожидаемое поведение?

То же самое происходит при компиляции для iOS (iOS 12 минимально ожидается).

Ответы [ 3 ]

0 голосов
/ 20 декабря 2018

Все функции 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: Поддерживает оригинальный интерфейс.Вероятно, будет работать на всех версиях ОС.

0 голосов
/ 20 декабря 2018

Хотя шаблоны обычно берутся из заголовков, это не означает, что цель времени выполнения не имеет значения.Эти шаблоны являются частью более широкой библиотеки, и они компилируются в код, который должен быть совместим с остальной частью этой библиотеки.Имеет смысл, чтобы стандартная библиотека whole была одной, единственной версией, и имеет смысл, чтобы эта версия работала на целевой машине.Можете ли вы представить себе хаос, который мог бы возникнуть в противном случае?

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

0 голосов
/ 13 сентября 2018

Это происходит потому, что std::visit выдает исключение bad_variant_access в случаях, описанных здесь , и поскольку реализация этого исключения зависит от более новой версии libc ++, вам необходимо использовать версии iOS и macOS, которые поставьте эту новую версию (macOS 10.14 и iOS 12).

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

P.S. Что касается случая, когда вы увеличили минимальную цель развертывания до 10,14 и все еще смогли нормально запустить программу на 10,13, я предполагаю, что вы столкнетесь с проблемами в тот момент, когда будет сработать это новое исключение (поскольку метод исключения, который опирается на более новая версия libc ++ не будет разрешена).

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