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).
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).
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:
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.
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.).
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.
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.