2

我在构造函数中有一个带有初始化列表的类,其中我正在初始化的字段之一是 astd::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.
4

1 回答 1

1

我要感谢@rustyx 和@Frank 的建议,遵循这个建议让我发现了在创建传递给路径构造函数的初始字符串的方式中的一个错误(还有@MM,他找到了确切的我输入这个答案时的错误)

我创建了一个函数(在我的类中使用)std::string ExpandPath(const std::string& path),它使用 Windows API 来扩展路径中的任何环境变量并返回一个字符串。该字符串由 achar*和一个计数生成,该计数包括空字节,因此在使用构造函数变体创建字符串时,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};
于 2019-05-09T08:18:02.347 回答