为什么在使用LLVM时,std :: ifstream的缓冲会“破坏”std :: getline?

问题描述 投票:12回答:1

我有一个简单的C ++应用程序,它应该从POSIX命名管道中读取行:

#include<iostream>
#include<string>
#include<fstream>

int main() {
    std::ifstream pipe;
    pipe.open("in");

    std::string line;
    while (true) {
        std::getline(pipe, line);
        if (pipe.eof()) {
            break;
        }
        std::cout << line << std::endl;
    }
}

脚步:

  • 我创建了一个命名管道:mkfifo in
  • 我使用g++ -std=c++11 test.cpp && ./a.out编译并运行C ++代码。
  • 我将数据提供给in管道:
sleep infinity > in &  # keep pipe open, avoid EOF
echo hey > in
echo cats > in
echo foo > in
kill %1                # this closes the pipe, C++ app stops on EOF

在Linux下执行此操作时,应用程序会在每个echo命令之后成功显示输出(g ++ 8.2.1)。

在macOS上尝试整个过程时,输出仅在关闭管道后显示(即在kill %1之后)。我开始怀疑某种缓冲问题,所以我试过禁用它:

std::ifstream pipe;
pipe.rdbuf()->pubsetbuf(0, 0);
pipe.open("out");

通过此更改,应用程序在第一个echo之后输出任何内容,然后在第二个echo(“嘿”)之后输出第一条消息,并且继续这样做,alwasy滞后于后面的消息并显示先前echo的消息而不是一个被执行。最后一条消息仅在关闭管道后显示。

我发现在macOS上g++基本上是clang++,因为g++ --version产生:“Apple LLVM版本10.0.1(clang-1001.0.46.3)”。在使用Homebrew安装真正的g ++之后,示例程序就像在Linux上一样工作。

我正在构建一个基于命名管道构建的简单IPC库,原因有多种,所以此时此工作正常对我来说非常重要。

使用LLVM时导致这种奇怪行为的原因是什么? (更新:这是由libc ++引起的)

这是一个错误吗?

这种方式是否适用于C ++标准以某种方式保证的g ++?

如何使用clang++使这段代码段正常工作?

更新:

这似乎是由getline()的libc ++实现引起的。相关链接:

问题仍然存在。

c++ macos pipe clang named-pipes
1个回答
0
投票

正如单独讨论的那样,boost::asio解决方案是最好的,但你的问题具体是关于getline是如何阻止的,所以我将与之对话。

这里的问题是std::ifstream并不是真正的FIFO文件类型。在getline()的情况下,它正在尝试进行缓冲读取,因此(在最初的情况下)它决定缓冲区没有足够的数据到达分隔符('\n'),在底层的underflow()上调用streambuf,这样做简单读取缓冲区长度的数据。这适用于文件,因为文件在某个时间点的长度是可知的长度,因此如果没有足够的数据来填充缓冲区,它可以返回EOF,如果有足够的数据,它只返回填充的缓冲区。但是,使用FIFO时,耗尽数据并不一定意味着EOF,因此在写入它的进程关闭之前它不会返回(这是你的无限sleep命令将其保持打开状态)。

更典型的方法是让编写器在读取和写入时打开和关闭文件。当poll() / epoll()这样功能更强大的东西可用时,这显然是浪费精力,但我正在回答你问的问题。

© www.soinside.com 2019 - 2024. All rights reserved.