I was trying to read output from a child process using a pipe()
. I used dup2()
to get the pipe's output through stdin
, and cin.get(c)
in a loop to get the child's output. The first time I do this, everything works fine. After that, however, every time I try to do this again, cin.get()
returns EOF
.
I thought that perhaps cin.clear()
would help, since cin.get()
is setting the failbit
and eofbit
when it reaches EOF
... This does remove the flags, but the main problem remains: cin.get()
does not read anything.
Here is a simplified case that shows the problem:
#include <iostream>
#include <unistd.h>
int main()
{
int fd[2];
pipe(fd);
int pid = fork();
//FIRST TIME
if (pid == 0)
{
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
std::cout << "PROCESS 1 OUTPUT" << std::endl;
exit(0);
}
wait(NULL);
close(fd[1]);
int original = dup(0);
dup2(fd[0], 0);
close(fd[0]);
if (std::cin.fail())
std::cout << "FAIL1" << std::endl;
char c;
std::string output1;
while (std::cin.get(c))
{
output1 = c;
}
std::cin.clear(); // This helps with "FAIL"s, but doesn't fix the problem :(
if (std::cin.fail())
std::cout << "FAIL2" << std::endl;
dup2(original, 0);
close(original);
std::cout << "output: " << output1 << std::endl;
//SECOND TIME
int fd2[2];
pipe(fd2);
int pid2 = fork();
if (pid2 == 0)
{
close(fd2[0]);
dup2(fd2[1], 1);
close(fd2[1]);
std::cout << "PROCESS 2 OUTPUT" << std::endl;
exit(0);
}
wait(NULL);
close(fd2[1]);
int original2 = dup(0);
dup2(fd2[0], 0);
close(fd2[0]);
if (std::cin.fail())
std::cout << "FAIL3" << std::endl;
char c2;
std::string output2;
while (std::cin.get(c2))
{
output2 = c2;
}
if (std::cin.fail())
std::cout << "FAIL4" << std::endl;
dup2(original2, 0);
close(original2);
std::cout << "output2: " << output2 << std::endl;
return 0;
}
This program outputs the following:
output: PROCESS 1 OUTPUT
FAIL4
output2:
What I expected to get was the two outputs and no "FAIL". Do you have any idea why this happens?
I found an alternative solution using read()
and reading straight from the pipe's fd[0]
, however I would love to understand why this is happening when I use cin
.
CodePudding user response:
As William Pursell mentioned, using both clear()
and clearerr()
will solve your problem.
After reading around for a bit, I came to the conclusion that this is due to the fact that std::cin
and stdin
are two different things, and therefore you need to reset their error flags separately:
std::cin
is an object of classistream
, and its internal error state flags are set usingstd::cin.clear()
.stdin
is actually aFILE *
, and its error flags and the EOF indicator are reset usingstd::clearerr(stdin)
.std::cin
is associated with the standard C input streamstdin
(according to cppreference.com), but I guess you still need to reset both separately.
These StackOverflow questions and answers might also help:
CodePudding user response:
I'm going to provide this answer in the hopes of encouraging someone who understands the quirks of C better than I to downvote and provide a more correct answer. The behavior you want seems to be available if you do:
std::cin.clear();
std::clearerr(stdin);
That cin.clear()
does not also do clearerr(stdin)
seems bizarre to me, and this is probably why I continue to steer clear of C . Although, to be fair to C , the thing that ought to be avoided here is manipulation of the underlying file descriptors of the standard file streams.