TL;DR
一个程序使用libc++
的版本getline
当函数从管道读取输入时,它将阻塞,直到管道的缓冲区已满。
相同的是NOT为真libstdc++
的版本getline
函数:这里,一旦输入行可用,该函数就会立即读取并返回一行输入。
我是否应该预料到两者之间的行为差异libstdc++
and libc++
? [EDIT:我不是在这里征求意见,我只是对管道了解不够,也不了解实现 C++ 标准库的困难。对我来说,这种行为上的差异肯定是was令人惊讶,但也许有人更了解并且可以向我保证这种差异是可以预料的,也许这只是一个实现细节?]
更重要的是,我能做些什么来让libc++
表现得像libstdc++
做?也就是说,getline
函数不应等到管道缓冲区已满,它应在可用时立即返回一行输入。
请参阅下面的代码示例,该示例显示了我如何读取和写入管道。
环境
- macOS 10.13.1(高塞拉利昂)
- Xcode 9.1
- Apple LLVM 版本 9.0.0 (clang-900.0.38)
我怀疑这个问题不仅限于 macOS,但我还没有 Linux 开发系统clang
我可以用它来测试。
测试准备
打开三个外壳,我们称它们为 A、B 和 C。
在 shell A 中:创建一个新文件pipe-test.cpp
并添加下面的源代码。编译源代码一次libstdc++
和一次与libc++
:
g++ -stdlib=libstdc++ -o pipe-test-libstdc++ pipe-test.cpp
g++ -stdlib=libc++ -o pipe-test-libc++ pipe-test.cpp
在 shell A 中:创建两个管道:
mkfifo input-pipe output-pipe
测试1,使用libstdc++
程序版本
- 在 shell A 中,运行以下命令:
pipe-test-libstdc++ input-pipe output-pipe
- 在 shell B 中,运行以下命令:
cat output-pipe
- 在 shell C 中,运行以下命令:
cat >input-pipe
- 在 shell C 中,键入行“foo”并按 ENTER
- 切换到 shell B:您将看到字符串“foo”。
- 发生了什么?在 shell A 中运行的程序已使用以下命令从输入管道读取了行“foo”
getline
函数并立即将该行打印到输出管道。
- 在 shell C 中,键入 CTRL+D。测试结束,所有 shell 现在都应该返回命令行。
测试2,使用libc++
程序版本
- 在 shell A 中,运行以下命令:
pipe-test-libc++ input-pipe output-pipe
- 在 shell B 中,运行以下命令:
cat output-pipe
- 在 shell C 中,运行以下命令:
cat >input-pipe
- 在 shell C 中,键入行“foo”并按 ENTER
- 切换到 shell B:你会NOT看到字符串“foo”。
- 发生了什么?在 shell A 中运行的程序仍然处于阻塞状态,
getline
函数尚未从输入管道接收到行“foo”,因为该管道的缓冲区尚未满。
- 在 shell C 中,键入 CTRL+D。
- 切换到外壳B:NOW你会看到字符串“foo”。
- 请注意,您也可以将大量文本粘贴到 shell C 中,而不是键入 CTRL+D。一旦管道的缓冲区已满,
getline
函数将开始逐行读取输入,直到清空管道的缓冲区。
源代码
#include <string>
#include <iostream>
#include <fstream>
int main(int argc, char** argv)
{
if (argc != 3)
{
std::cout
<< "Usage: pipe-test /path/to/input-pipe /path/to/output-pipe"
<< std::endl;
return 1;
}
std::string pathToInputPipe = argv[1];
std::string pathToOutputPipe = argv[2];
std::cout
<< "Input pipe = " << pathToInputPipe << std::endl
<< "Output pipe = " << pathToOutputPipe << std::endl;
std::ifstream inputPipeStream;
inputPipeStream.open(pathToInputPipe.c_str());
if (! inputPipeStream)
{
std::cout
<< "Failed to open input pipe " << pathToInputPipe
<< std::endl;
return 1;
}
else
{
std::cout
<< "Input pipe: result of eof() = " << inputPipeStream.eof() << std::endl
<< "Input pipe: result of bad() = " << inputPipeStream.bad() << std::endl
<< "Input pipe: result of good() = " << inputPipeStream.good() << std::endl
<< "Input pipe: result of fail() = " << inputPipeStream.fail() << std::endl;
}
std::ofstream outputPipeStream;
outputPipeStream.open(pathToOutputPipe.c_str());
if (! outputPipeStream)
{
std::cout
<< "Failed to open output pipe " << pathToOutputPipe
<< std::endl;
return 1;
}
else
{
std::cout
<< "Output pipe: result of eof() = " << outputPipeStream.eof() << std::endl
<< "Output pipe: result of bad() = " << outputPipeStream.bad() << std::endl
<< "Output pipe: result of good() = " << outputPipeStream.good() << std::endl
<< "Output pipe: result of fail() = " << outputPipeStream.fail() << std::endl;
}
int lineNumber = 0;
while (true)
{
lineNumber++;
std::string line;
bool getlineFailResult = getline(inputPipeStream, line).fail();
if (getlineFailResult)
{
std::cout << "Failed to read stream, stopping" << std::endl;
break;
}
else
{
std::cout << "Received line " << lineNumber << ": " << line << std::endl;
outputPipeStream << line << std::endl;
outputPipeStream.flush();
}
}
return 0;
}