web-dev-qa-db-fra.com

Appel Xcode 10 à la fonction indisponible std :: visit

Lors de la compilation du programme suivant avec 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;
}

J'obtiens l'erreur suivante:

main.cpp: 27: 5: Appel à la fonction non disponible 'visite': introduit dans macOS 10.14

Cependant, si je change la cible de déploiement min en macOS 10.14, le code se compile correctement et cela fonctionne, même si j'utilise macOS 10.13.

Puisque std::visit est un modèle de fonction et ne devrait pas dépendre de la version du système d'exploitation (ce que j'ai prouvé en exécutant le code sur une version de mac inférieure à celle réellement prise en charge), si cela est considéré comme un bug et signalé à Apple ou est-ce un comportement attendu?

La même chose se produit lors de la compilation pour iOS (iOS 12 est peu attendu).

17
dCubelic

Cela se produit car std::visit lance un bad_variant_access exception dans les cas décrits ici et puisque l'implémentation de cette exception dépend d'une version plus récente de libc ++, vous devez utiliser des versions d'iOS et macOS livrées avec cette nouvelle version (macOS 10.14 et iOS 12) .

Heureusement, un chemin d'implémentation est disponible lorsque les exceptions c ++ sont désactivées qui ne dépend pas de la nouvelle libc ++, donc si possible, vous pouvez utiliser cette option.

P.S. À propos du cas où vous avez augmenté la cible de déploiement minimum à 10,14 et avez toujours pu exécuter le programme normalement sur 10,13, je suppose que vous rencontreriez des problèmes au moment où cette nouvelle exception serait déclenchée (puisque la méthode d'exception qui repose sur une version plus récente de libc ++ ne serait pas résolue).

10
m1h4

Toutes les fonctionnalités std::variant Susceptibles de lancer std::bad_variant_access Sont marquées comme disponibles à partir de macOS 10.14 (et iOS, tvOS et watchOS correspondants) dans les fichiers d'en-tête standard. En effet, la méthode virtuelle std::bad_variant_access::what() n'est pas inline et donc définie dans le libc++.dylib (Fourni par l'OS).

Il existe plusieurs solutions de contournement (toutes techniquement comportement indéfini), ordonnées par ma préférence personnelle:

1) Saisissez la mise en œuvre

std::visit N'est lancé que si l'un des arguments de variante est valueless_by_exception. L'examen de l'implémentation vous donne la clé pour utiliser la solution de contournement suivante (en supposant que vs est un ensemble de paramètres de variantes):

if (... && !vs.valueless_by_exception() ) {
  std::__variant_detail::__visitation::__variant::__visit_value(visitor, vs...);
} else {
  // error handling
}

Con: Peut rompre avec les futures versions de libc ++. Interface laide.

Pro: Le compilateur vous criera probablement quand il se cassera et la solution de contournement peut être facilement adaptée. Vous pouvez écrire un wrapper contre l'interface laide.

2) Supprimer l'erreur du compilateur de disponibilité ...

Ajoutez _LIBCPP_DISABLE_AVAILABILITY Aux paramètres du projet Macros de préprocesseur (GCC_PREPROCESSOR_DEFINITIONS)

Con: Cela supprimera également les autres gardes de disponibilité (shared_mutex, bad_optional_access Etc.).

2a) ... et il suffit de l'utiliser

Il s'avère que cela fonctionne déjà dans High Sierra , pas seulement Mojave (j'ai testé jusqu'à 10.13.0).

Dans 10.12.6 et ci-dessous, vous obtenez l'erreur d'exécution:

dyld: Symbol not found: __ZTISt18bad_variant_access
  Referenced from: [...]/VariantAccess
  Expected in: /usr/lib/libc++.1.dylib
 in [...]/VariantAccess
Abort trap: 6

où la première ligne se démêle en _typeinfo for std::bad_variant_access. Cela signifie que l'éditeur de liens dynamique (dyld) ne peut pas trouver la vtable pointant vers la méthode what() mentionnée dans l'introduction.

Con: Fonctionne uniquement sur certaines versions de système d'exploitation, vous ne savez au moment du démarrage que si cela ne fonctionne pas.

Pro: Maintient l'interface d'origine.

2b) ... et fournissez votre propre implémentation d'exception

Ajoutez les lignes suivantes à l'un de vos fichiers source de projet:

// Strongly undefined behaviour (violates one definition rule)
const char* std::bad_variant_access::what() const noexcept {
    return "bad_variant_access";
}

J'ai testé cela pour un binaire autonome sur 10.10.0, 10.12.6, 10.13.0, 10.14.1 et mon exemple de code fonctionne même quand un std::bad_variant_access Est lancé, l'attrapant par std::exception const& ex, Et en appelant la fonction virtuelle ex.what().

Con: Mon hypothèse est que cette astuce se cassera lors de l'utilisation de RTTI ou de la gestion des exceptions au-delà des frontières binaires (par exemple, différentes bibliothèques d'objets partagés). Mais ce n'est qu'une hypothèse et c'est pourquoi j'ai mis cette solution de contournement en dernier: je ne sais pas quand elle se brisera et quels seront les symptômes.

Pro: Maintient l'interface d'origine. Fonctionnera probablement sur toutes les versions du système d'exploitation.

9
Tobi