繁体   English   中英

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

[英]Why does the buffering of std::ifstream “break” std::getline when using LLVM?

我有一个简单的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之后打印出第一条消息(“嘿”),并继续这样做,始终将消息滞后并显示前一个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 ++实现引起的。 相关链接:

问题仍然存在。

如单独讨论的那样,最好使用boost::asio解决方案,但是您的问题是关于getline如何被阻塞的,因此我将与之讨论。

这里的问题是std::ifstream不是真正为FIFO文件类型创建的。 getline()的情况下,它试图进行缓冲读取,因此(在初始情况下)它确定缓冲区没有足够的数据到达定界符( '\\n' ),在接口上调用underflow()底层streambuf ,并且对缓冲区长度的数据进行简单读取。 这对于文件非常有用,因为文件在某个时间点的长度是一个已知的长度,因此,如果没有足够的数据来填充缓冲区,则可以返回EOF;如果有足够的数据,则仅返回已填充的缓冲区。 但是,对于FIFO,用完数据并不一定意味着EOF ,因此它不会返回,直到写入它的进程关闭(这是使它保持打开sleep的无限sleep命令)。

执行此操作的一种更典型的方法是使编写器在读写文件时打开和关闭文件。 当可以使用诸如poll() / epoll()类的更多功能时,这显然是在浪费精力。但是,我正在回答您要问的问题。

通过将POSIX getline()包装在简单的C API中并从C ++进行简单调用,我解决了这个问题。 代码是这样的:

typedef struct pipe_reader {
    FILE* stream;
    char* line_buf;
    size_t buf_size;
} pipe_reader;

pipe_reader new_reader(const char* pipe_path) {
    pipe_reader preader;
    preader.stream = fopen(pipe_path, "r");
    preader.line_buf = NULL;
    preader.buf_size = 0;
    return preader;
}

bool check_reader(const pipe_reader* preader) {
    if (!preader || preader->stream == NULL) {
        return false;
    }
    return true;
}

const char* recv_msg(pipe_reader* preader) {
    if (!check_reader(preader)) {
        return NULL;
    }
    ssize_t read = getline(&preader->line_buf, &preader->buf_size, preader->stream);
    if (read > 0) {
        preader->line_buf[read - 1] = '\0';
        return preader->line_buf;
    }
    return NULL;
}

void close_reader(pipe_reader* preader) {
    if (!check_reader(preader)) {
        return;
    }
    fclose(preader->stream);
    preader->stream = NULL;
    if (preader->line_buf) {
        free(preader->line_buf);
        preader->line_buf = NULL;
    }
}

这对libc ++或libstdc ++很好。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM