2 votes

`std::filesystem::path::operator/(/*args*/)` ne fonctionne pas comme prévu

J'ai une classe avec une liste d'initialisation dans le constructeur où l'un des champs que j'initialise est un fichier std::filesystem::path mais il ne semble pas s'initialiser à la valeur attendue.

MyClass::MyClass(
    unsigned int deviceSerial,
    const std::string& processName
) :
    deviceSerial(deviceSerial),
    processName(processName),
    configFilePath(GetBasePath() / std::to_string(deviceSerial) / ("#" + processName + ".json"))
{
    /* Parameter checks */
}

En utilisant le débogueur, je peux voir que GetBasePath() renvoie exactement ce que j'attends (renvoie std::filesystem::path avec le chemin correct) mais le / L'opérateur ne semble pas avoir d'effet. Une fois dans le corps du constructeur, je peux voir que configFilePath est réglé sur le résultat de GetBasePath() sans les informations supplémentaires ajoutées.

J'utilise MSVS-2019, la norme de langage C++ est définie sur C++17 et, en mode débogage, toutes les optimisations sont désactivées.

J'ai également testé ce qui suit dans le corps de la classe et je vois toujours path comme étant simplement le résultat de GetBasePath() et les éléments supplémentaires ne sont pas ajoutés.

{
    auto path = GetBasePath();             // path = "C:/Users/Me/Desktop/Devices"
    path /= std::to_string(deviceSerial);  // path = "C:/Users/Me/Desktop/Devices"
    path /= ("#" + processName + ".json"); // path = "C:/Users/Me/Desktop/Devices"
}

En passant, j'ai aussi essayé le test ci-dessus avec += au lieu de /= et je vois toujours les mêmes résultats.

Modifier

Comme demandé, vous trouverez ci-dessous un exemple minimal complet et vérifiable.

#include <Windows.h>
#include <cstdio>
#include <filesystem>
#include <memory>
#include <string>

std::string ExpandPath(const std::string &str) {
  auto reqBufferLen = ExpandEnvironmentStrings(str.c_str(), nullptr, 0);

  if (reqBufferLen == 0) {
    throw std::system_error((int)GetLastError(), std::system_category(),
                            "ExpandEnvironmentStrings() failed.");
  }

  auto buffer = std::make_unique<char[]>(reqBufferLen);
  auto setBufferLen =
      ExpandEnvironmentStrings(str.c_str(), buffer.get(), reqBufferLen);

  if (setBufferLen != reqBufferLen - 1) {
    throw std::system_error((int)GetLastError(), std::system_category(),
                            "ExpandEnvironmentStrings() failed.");
  }

  return std::string{buffer.get(), setBufferLen};
}

int main() {
  unsigned int serial = 12345;
  std::string procName = "Bake";

  std::filesystem::path p(ExpandPath("%USERPROFILE%\\Desktop\\Devices"));
  std::printf("Path = %s\n", p.string().c_str());
  // p = C:\Users\Me\Desktop\Devices

  p /= std::to_string(serial);
  std::printf("Path = %s\n", p.string().c_str());
  // p = C:\Users\Me\Desktop\Devices

  p /= "#" + procName + ".json";
  std::printf("Path = %s\n", p.string().c_str());
  // p = C:\Users\Me\Desktop\Devices

  std::getchar();
}

I've also used this example and tested with `p.append()` and got the same result.

1voto

TheBeardedQuack Points 454

J'aimerais remercier @rustyx et @Frank pour leurs suggestions, suivre ces conseils m'a permis de découvrir un bug dans la façon dont je crée la chaîne initiale qui est passée au constructeur de chemin (également @M.M qui a trouvé le bug exact pendant que je tapais cette réponse).

J'ai créé une fonction (qui est utilisée dans ma classe) std::string ExpandPath(const std::string& path) qui utilise l'API Windows pour développer toute variable d'environnement dans un chemin et retourner une chaîne. Cette chaîne est générée à partir d'un char* et un compte, ce compte inclut l'octet nul, de sorte que lorsque l'on crée une chaîne de caractères en utilisant la variante du constructeur std::string(char* cstr, size_t len) cela inclut l'octet nul dans la chaîne elle-même.

Comme j'utilisais le débogueur pour interroger les variables, il lit des chaînes de caractères de style C et s'arrête à l'octet nul. Dans mon exemple original, j'utilise également printf() car je préfère cette fonction pour la sortie, mais là encore, l'impression s'arrête à l'octet nul. Si je modifie la sortie pour utiliser std::cout Je peux voir que la sortie a le chemin attendu mais avec un espace supplémentaire (l'octet nul étant imprimé comme un espace). En utilisant std::cout Je constate que mes chemins se présentent comme suit à chaque ajout :

Path = C:\Users\Me\Desktop\Devices
Path = C:\Users\Me\Desktop\Devices \12345
Path = C:\Users\Me\Desktop\Devices \12345\#Bake.json

Résumé :

Bug dans mon ExpandPath()

  return std::string{buffer.get(), setBufferLen};

Devrait être

  return std::string{buffer.get(), setBufferLen - 1};

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X