web-dev-qa-db-fra.com

Littéraux C ++ utf-8 dans GCC et MSVC

Ici, j'ai un code simple:

#include <iostream>
#include <cstdint>

    int main()
    {
         const unsigned char utf8_string[] = u8"\xA0";
         std::cout << std::hex << "Size: " << sizeof(utf8_string) << std::endl;
          for (int i=0; i < sizeof(utf8_string); i++) {
            std::cout << std::hex << (uint16_t)utf8_string[i] << std::endl;
          }
    }

Je vois un comportement différent ici avec MSVC et GCC. MSVC voit "\xA0" comme séquence unicode non codée, et l'encode en utf-8. Donc, dans MSVC, la sortie est:

C2A0

Qui est correctement encodé dans le symbole unicode utf8 U+00A0.

Mais en cas de GCC, rien ne se passe. Il traite la chaîne comme de simples octets. Il n'y a pas de changement même si je supprime u8 avant le littéral de chaîne.

Les deux compilateurs encodent en utf8 avec la sortie C2A0 si la chaîne est définie sur: u8"\u00A0";

Pourquoi les compilateurs se comportent-ils différemment et qui le fait réellement?

Logiciel utilisé pour le test:

GCC 8.3.0

MSVC 19.00.23506

C++ 11

6
toozyfuzzy

Ils ont tous les deux tort.

Pour autant que je sache, la norme C++ 17 dit ici que:

La taille d'un littéral de chaîne étroite est le nombre total de séquences d'échappement et d'autres caractères, plus au moins un pour le codage multi-octets de chaque nom de caractère universel, plus un pour le "\ 0" de fin.

Bien qu'il existe d'autres indices, cela semble être l'indication la plus forte que les séquences d'échappement ne sont pas multi-octets et que le comportement de MSVC est incorrect.

Il y a des billets pour cela qui sont actuellement marqués comme en cours d'enquête:

Cependant, il dit aussi ici à propos des littéraux UTF-8 que:

Si la valeur n'est pas représentable avec une seule unité de code UTF-8, le programme est mal formé.

Puisque 0xA0 n'est pas un caractère UTF-8 valide, le programme ne doit pas compiler.

Notez que:

  • Littéraux UTF-8 commençant par u8 sont définis comme étant étroits.
  • \xA0 est une séquence d'échappement
  • \u00A0 est considéré comme un nom de caractère universel et non comme une séquence d'échappement
2
AtnNn

Pourquoi les compilateurs se comportent-ils différemment et qui le fait réellement?

Les compilateurs se comportent différemment en raison de la façon dont ils ont décidé d'implémenter le standard C++:

  • GCC utilise des règles strictes et implémente la norme telle quelle
  • MSVC utilise des règles lâches et implémente la norme d'une manière plus pratique "dans le monde réel"

Donc, les choses qui échouent dans GCC fonctionneront généralement dans MSVC parce que c'est plus permis. Et MSVC gère automatiquement certains de ces problèmes.

Voici un exemple similaire: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=33167 . Il suit la norme, mais ce n'est pas ce à quoi vous vous attendez.

Quant à savoir qui fait le bien, cela dépend de votre définition du "bien".

1
Cosmin

Je ne peux pas vous dire quel chemin est conforme à la norme.

La façon dont MSVC le fait est au moins logiquement cohérente et facilement explicable. Les trois séquences d'échappement \x, \u, et \U se comportent de la même manière, sauf pour le nombre de chiffres hexadécimaux qu'ils tirent de l'entrée: 2, 4 ou 8. Chacun définit un point de code Unicode qui doit ensuite être codé en UTF-8. Incorporer un octet sans encodage conduit à la possibilité de créer une séquence UTF-8 invalide.

1
Mark Ransom