web-dev-qa-db-fra.com

Bogue de séparateur de chemin natif dans C ++ 17 std :: filesystem :: path?

J'ai rencontré un problème lors de la mise à niveau de #include <experimental/filesystem> à #include <filesystem>. Il semble que le std::filesystem::path::wstring la méthode ne renvoie pas la même chaîne que dans experimental::filesystem. J'ai écrit le petit programme de test suivant avec le résultat de sortie inclus.

#include <iostream>
#include <filesystem>
#include <experimental/filesystem>

namespace fs = std::filesystem;
namespace ex = std::experimental::filesystem;
using namespace std;

int main()
{
    fs::path p1{ L"C:\\temp/foo" };    
    wcout << "std::filesystem Native: " << p1.wstring() << "  Generic: " << p1.generic_wstring() << endl;

    ex::path p2{ L"C:\\temp/foo" };
    wcout << "std::experimental::filesystem Native: " << p2.wstring() << "  Generic: " << p2.generic_wstring() << endl;
}

/* Output:
std::filesystem Native: C:\temp/foo  Generic: C:/temp/foo
std::experimental::filesystem Native: C:\temp\foo  Generic: C:/temp/foo
*/

Selon https://en.cppreference.com/w/cpp/filesystem/path/string :

Valeur de retour

Chemin d'accès interne au format de chemin d'accès natif, converti en type de chaîne spécifié.

Le programme a fonctionné sur Windows 10 et a été compilé avec Visual Studio 2017 version 15.8.0. Je m'attendrais à ce que le chemin d'accès natif soit C:\temp\foo.

Question: Est-ce un bug dans std::filesystem::path?

18
Garland

Non, ce n'est pas un bug!

string() et al et c_str()/native() retourne le chemin d'accès interne dans format de chemin natif .

Que signifie natif

États MS , il utilise ISO/IEC TS 18822: 2015 . Le projet final définit le format de chemin d'accès natif au §4.11 comme suit:

Format de chemin d'accès dépendant du système d'exploitation accepté par le système d'exploitation hôte .

Sous Windows, native() renvoie le chemin d'accès sous la forme std::wstring().

Comment forcer l'utilisation de barres obliques inverses comme séparateur de répertoires dans Windows

La norme définit le terme séparateur préféré (voir aussi §8.1 (grammaire du format de chemin) ):

Un caractère de séparateur de répertoire dépendant du système d'exploitation.

Un chemin peut être converti (sur place) en séparateur préféré avec path::make_preferred . Sous Windows, il possède l'opérateur noexcept.

Pourquoi vous ne devriez pas vous inquiéter

Le documentation MS sur les chemins indique l'utilisation de / contre \

Les fonctions d'E/S de fichiers dans l'API Windows convertissent "/" en "\" dans le cadre de la conversion du nom en un nom de style NT, sauf lors de l'utilisation du préfixe "\? \" Comme détaillé dans les sections suivantes.

et dans la documentation sur la navigation dans les fichiers C++ , la barre oblique (connue sous le nom fallback-separator dans les versions plus récentes) est même utilisée directement après la root-name :

path pathToDisplay(L"C:/FileSystemTest/SubDir3/SubDirLevel2/File2.txt ");

Exemple pour VS2017 15.8 avec -std:C++17:

#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;

void output(const std::string& type, fs::path& p)
{
    std::cout
        << type << ":\n"
        << "- native: " << p.string() << "\n"
        << "- generic: " << p.generic_string() << "\n"
        << "- preferred-separator" << p.make_preferred() << "\n";
}

int main()
{
    fs::path local_win_path("c:/dir/file.ext");
    fs::path unc_path("//your-remote/dir/file.ext");

    output("local absolute win path", local_win_path);
    output("unc path", unc_path);

    unc_path = "//your-remote/dir/file.ext"; // Overwrite make_preferred applied above.
    if (fs::is_regular_file(unc_path))
    {
        std::cout << "UNC path containing // was understood by Windows std filesystem";
    }
}

Sortie possible (lorsque unc_path est un fichier existant sur une télécommande existante):

local absolute win path:
- native: c:/dir/file.ext
- generic: c:/dir/file.ext
- preferred-separator"c:\\dir\\file.ext"
unc path:
- native: //your-remote/dir/file.ext
- generic: //your-remote/dir/file.ext
- preferred-separator"\\\\your-remote\\dir\\file.ext"
UNC path containing // was understood by Windows std filesystem

Les transformations de chemin explicites vers le Preferred-Separator ne devraient donc être nécessaires que lorsque vous travaillez avec des bibliothèques qui imposent l'utilisation de ce séparateur pour leur interaction avec le système de fichiers.

6
Roi Danton

En gros, un bogue dans un compilateur se produit lorsqu'il présente un comportement interdit par la norme (explicitement ou implicitement), ou un comportement qui diffère de la documentation dudit compilateur.

La norme n'impose aucune restriction sur le format des chaînes de chemin natives, sauf que le format doit être accepté par le système d'exploitation sous-jacent (citation ci-dessous). Comment pourrait-il imposer de telles restrictions? Le langage n'a aucun mot à dire sur la façon dont les chemins sont gérés par le système d'exploitation hôte, et pour le faire en toute confiance, il devrait connaître chaque cible vers laquelle il peut être compilé, ce qui n'est clairement pas faisable.

[fs.class.path]

5 Un chemin d'accès est une chaîne de caractères qui représente le nom d'un chemin. Les chemins d'accès sont formatés selon la grammaire de format de chemin d'accès générique ([fs.path.generic]) ou selon un format de chemin d'accès natif dépendant du système d'exploitation accepté par le système d'exploitation hôte .

(c'est moi qui souligne)

La documentation de MSVC implique que la barre oblique est parfaitement acceptable comme séparateur:

La structure imposée à un chemin d'accès une fois que vous avez dépassé le nom racine est commune aux deux systèmes. Pour le chemin c: /abc/xyz/def.ext:

  • Le nom racine est c:.
  • Le répertoire racine est /.
  • Le chemin racine est c:/.
  • Le chemin relatif est abc/xyz/def.ext.
  • Le chemin parent est c:/abc/xyz.
  • Le nom de fichier est def.ext.
  • La tige est def.
  • L'extension est .ext.

Il mentionne un séparateur préféré, mais cela n'implique vraiment que le comportement de std::make_preferred, et non de la sortie du chemin par défaut:

Une différence mineure est le séparateur préféré , entre la séquence de répertoires d'un nom de chemin. Les deux systèmes d'exploitation vous permettent d'écrire une barre oblique /, mais dans certains contextes, Windows préfère une barre oblique inversée \.

La question de savoir s'il s'agit d'un bogue est donc facile: étant donné que la norme n'impose aucune restriction sur le comportement et que la documentation du compilateur n'implique aucun besoin obligatoire d'une barre oblique vers l'arrière, il ne peut y avoir de bogue.

Reste à savoir s'il s'agit d'un problème de qualité de mise en œuvre. Après tout, les implémenteurs de compilateurs et de bibliothèques sont censés connaître toutes les bizarreries de leur cible et implémenter les fonctionnalités en conséquence.

C'est à débattre qui slash ('\' ou '/') vous devez utiliser dans Windows, ou si cela importe vraiment, donc il ne peut y avoir de réponse faisant autorité. Toute réponse qui plaide pour l'un ou l'autre doit être très prudente afin de ne pas être trop basée sur l'opinion. De plus, la simple existence de path::make_preferred indique que le chemin natif n'est pas nécessairement le chemin préféré. Considérez le principe zéro overhead: Rendre le chemin toujours le meilleur entraînerait un surcoût pour les personnes qui n'ont pas besoin d'être aussi pédantes lors de la manipulation des chemins.

Finalement, le std::experimental l'espace de noms est ce qu'il dit sur la boîte: vous ne devez pas vous attendre à ce que la bibliothèque standardisée finale se comporte de la même manière que sa version expérimentale, ou même vous attendre à ce qu'une bibliothèque standardisée finale existe. C'est comme ça, quand il s'agit de choses expérimentales.

9
Cássio Renan

L'une ou l'autre de ces options peut être considérée comme "native" sur la plate-forme, donc l'une ou l'autre de ces options est également valable. L'API Filesystem ne garantit pas que la version "native" sera identique à la chaîne que vous lui avez donnée, quelle que soit la plate-forme. Il n'y a pas non plus de garantie que la chaîne "native" n'utilisera le séparateur de répertoire natif que si le caractère générique "/" lui est équivalent.

5
Nicol Bolas