簡體   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