【问题标题】:Return value of std::filesystem::create_directories() on paths with trailing slashstd::filesystem::create_directories() 在带有斜杠的路径上的返回值
【发布时间】:2020-02-08 20:15:03
【问题描述】:

GCC (10.0.1) 和 Clang (11.0.0)/MSVC (VS 16.4.3) 当带有斜杠的不存在路径给出为论据。

更准确地说,虽然所有三个编译器都确实创建了目录,但后两个在这种情况下返回 false,使得 std::filesystem::create_directories() 的返回值模棱两可(并且违反直觉)。

具体来说,如果路径“a/b/c”处不存在文件,则如下程序,

#include <filesystem>
#include <iostream>

int main() {
    std::cout << std::boolalpha << std::filesystem::create_directories("a/b/c/");
}

在该路径创建一个目录,但在 Clang/MSVC 下打印 false 而在 GCC 下打印 true。

正确的行为是什么?

【问题讨论】:

  • 您确定每种情况的前提条件都相同吗?也就是说,以太a/b/c 总是存在还是a/b/c 永远不存在? documentation 表示 "Return value: true if a directory was created for the directory p resolves to, false otherwise"。事实上,在我正在努力在同一路径上连续两次调用create_directories 的盒子上,第一次调用返回true,第二次调用返回false

标签: c++ gcc clang c++17 std-filesystem


【解决方案1】:

我可以确认,这不是先决条件问题,并且我认为 GCC 是正确的,但我的猜测是,在这一点上澄清标准会有所帮助。

长期以来,使用尾随分隔符标记目录是很常见的使用模式,以将它们与文件区分开来。如果要创建的路径在调用之前不存在,并且由于调用而在调用之后存在,我希望 std::filesystem::create_directories 的实现返回 true。

让我们稍微扩展一下这个例子:

#include <filesystem>
#include <iostream>
#include <system_error>

int main() {
    std::error_code ec;
    std::cout << std::boolalpha << std::filesystem::exists("a/b/c/") << "/";
    std::cout << std::filesystem::create_directories("a/b/c/", ec) << "/";
    std::cout << !!ec << "/" << std::filesystem::exists("a/b/c/");
}

现在我们可以使用godbolt了:https://godbolt.org/z/5883uY

它为 GCC/libstdc++ 显示 false/true/false/true,正如我们所料,它不存在,它是由 create_directories 调用创建的,没有错误,之后它存在。

现在 clang/libc++ 结果是false/false/false/true,所以它不存在,它告诉我们它没有创建它,但是它确实创建了它,没有错误,我们在调用后证明了。

查看 github 版本的 MSVC stl,它看起来像调用 CreateDirectoryW 和:aa\ba\b\c,最后是 a\b\c\,最后一个报告 ERROR_ALREADY_EXISTS,导致false 的回归,因为我们似乎没有做任何事情,但我认为这是一个从函数中流出的实现细节。 我的猜测是,Clang 代码的工作方式类似,但我还没有检查 libc++ 源代码。

标准如下:“返回:如果创建了新目录,则返回 true,否则返回 false。如果发生错误,带有参数 ec 的签名将返回 false。”

所以标准需要true 如果创建了一个目录,而不是最后一个,并且对于ec 变体false 表示错误,但clang 告诉我们通过ec 没有。

我可能仍然是错的,因为我不在任何 WG 中,但我的结论是:GCC 是正确的,它似乎是 MSVC 和 Clang 中的一个错误。

因为我在验证自己的实现时已经在实现之间收集了discrepancies,所以如果可以的话,我会“高兴地”将这个添加到我的集合中。

更新:我刚刚挖掘了其他来源,Clang/libc++ 使用基于递归 parent_path() 的机制,最终导致与 MSVC 相同的序列,其中最后一个 ::mkdir("a/b/c/") 是前面是::mkdir("a/b/c"),因此它报告错误,因为它假定它没有创建目录。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-16
    • 2019-02-27
    • 2013-01-22
    • 1970-01-01
    • 1970-01-01
    • 2016-11-06
    • 2021-11-30
    • 1970-01-01
    相关资源
    最近更新 更多