`std::filesystem::path::operator/(/*args*/)` 没有按预期工作
`std::filesystem::path::operator/(/*args*/)` not working as expected
我在构造函数中有一个 class 和一个初始化程序列表,其中我正在初始化的字段之一是 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()
返回的正是我所期望的(returns std::filesystem::path
具有正确的路径)但是 /
运算符似乎没有产生影响。一旦进入构造函数的主体,我可以看到 configFilePath
设置为 GetBasePath()
的结果,没有附加额外信息。
我正在使用 MSVS-2019,我将 C++ 语言标准设置为 C++17,并且在调试模式下我禁用了所有优化。
我还在 class 的正文中测试了以下内容,我仍然认为 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.
我要感谢@rustyx 和@Frank 的建议,遵循这个建议让我发现了一个错误,在我创建传递给路径构造函数的初始字符串的方式中(还有@ M.M 谁在我输入此答案时发现了确切的错误)
我创建了一个函数(在我的 class 中使用)std::string ExpandPath(const std::string& path)
,它使用 Windows API 来扩展路径中的任何环境变量和 return一个字符串。该字符串是从 char*
和一个计数生成的,该计数包括空字节,因此在使用构造函数变体 std::string(char* cstr, size_t len)
创建字符串时,这包括字符串本身中的空字节。
因为我使用调试器询问它读取 C-style 字符串的变量并在空字节处停止。在我原来的示例中,我也使用 printf()
,因为我恰好更喜欢这个函数用于输出,但是这又一次在空字节处停止打印。如果我将输出更改为使用 std::cout
,我可以看到输出具有预期的路径,但带有额外的 space(空字节被打印为 space)。使用 std::cout
我看到每次追加时我的路径结果如下:
Path = C:\Users\Me\Desktop\Devices
Path = C:\Users\Me\Desktop\Devices 345
Path = C:\Users\Me\Desktop\Devices 345\#Bake.json
总结:
错误在 my ExpandPath()
where
return std::string{buffer.get(), setBufferLen};
应该是
return std::string{buffer.get(), setBufferLen - 1};
我在构造函数中有一个 class 和一个初始化程序列表,其中我正在初始化的字段之一是 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()
返回的正是我所期望的(returns std::filesystem::path
具有正确的路径)但是 /
运算符似乎没有产生影响。一旦进入构造函数的主体,我可以看到 configFilePath
设置为 GetBasePath()
的结果,没有附加额外信息。
我正在使用 MSVS-2019,我将 C++ 语言标准设置为 C++17,并且在调试模式下我禁用了所有优化。
我还在 class 的正文中测试了以下内容,我仍然认为 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.
我要感谢@rustyx 和@Frank 的建议,遵循这个建议让我发现了一个错误,在我创建传递给路径构造函数的初始字符串的方式中(还有@ M.M 谁在我输入此答案时发现了确切的错误)
我创建了一个函数(在我的 class 中使用)std::string ExpandPath(const std::string& path)
,它使用 Windows API 来扩展路径中的任何环境变量和 return一个字符串。该字符串是从 char*
和一个计数生成的,该计数包括空字节,因此在使用构造函数变体 std::string(char* cstr, size_t len)
创建字符串时,这包括字符串本身中的空字节。
因为我使用调试器询问它读取 C-style 字符串的变量并在空字节处停止。在我原来的示例中,我也使用 printf()
,因为我恰好更喜欢这个函数用于输出,但是这又一次在空字节处停止打印。如果我将输出更改为使用 std::cout
,我可以看到输出具有预期的路径,但带有额外的 space(空字节被打印为 space)。使用 std::cout
我看到每次追加时我的路径结果如下:
Path = C:\Users\Me\Desktop\Devices
Path = C:\Users\Me\Desktop\Devices 345
Path = C:\Users\Me\Desktop\Devices 345\#Bake.json
总结:
错误在 my ExpandPath()
where
return std::string{buffer.get(), setBufferLen};
应该是
return std::string{buffer.get(), setBufferLen - 1};