`std :: filesystem :: path :: operator / (/ * args * /)` не работает должным образом - PullRequest
2 голосов
/ 08 мая 2019

У меня есть класс со списком инициализаторов в конструкторе, где одно из полей, которое я инициализирую, является std::filesystem::path, но, похоже, оно не инициализируется ожидаемым значением.

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

Используя отладчик, я вижу, что GetBasePath() возвращает именно то, что я ожидаю (возвращает std::filesystem::path с правильным путем), но оператор /, похоже, не дает эффекта.Оказавшись внутри тела конструктора, я вижу, что configFilePath настроен на результат GetBasePath() без добавления дополнительной информации.

Я использую MSVS-2019, у меня установлен стандарт языка C ++до C ++ 17 и в режиме отладки у меня отключены все оптимизации.

Я также проверил следующее в теле класса и все еще вижу path просто как результат GetBasePath() идополнительные элементы не добавляются.

{
    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"
}

На небольшом примечании стороны я также попробовал вышеупомянутый тест с += вместо /=, и я все еще вижу те же результаты.

Изменить

В соответствии с просьбой ниже приведен минимально полный и проверяемый пример.

#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.

1 Ответ

1 голос
/ 09 мая 2019

Я хотел бы поблагодарить @rustyx и @Frank за их предложения. Следуя этому совету, я обнаружил ошибку в способе создания начальной строки, которая передается конструктору пути (также @MM нашел точную ошибку, пока я печатал этот ответ)

Я создал функцию (которая используется в моем классе) std::string ExpandPath(const std::string& path), которая использует Windows API для расширения любых переменных среды в пути и возврата строки. Эта строка генерируется из char* и счетчика, который включает нулевой байт, поэтому при создании строки с использованием варианта конструктора std::string(char* cstr, size_t len) он включает нулевой байт в самой строке.

Поскольку я использовал отладчик для опроса переменных, он читает строки в стиле C и останавливается на нулевом байте. В моем исходном примере я также использую printf(), так как я просто предпочитаю эту функцию для вывода, но опять же это останавливает печать с нулевым байтом. Если я изменяю вывод на использование std::cout, я вижу, что выход имеет ожидаемый путь, но с дополнительным пробелом (нулевой байт печатается как пробел). Используя std::cout, я вижу, что мои пути приводят к следующему с каждым добавлением:

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

Резюме:

Ошибка в моем ExpandPath() где

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

Должно быть

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