1

I have a case where I need pipe the output of a child process to an ifstream.

I am trying both creating an ifstream from a file descriptor using the method here: How to construct a c++ fstream from a POSIX file descriptor?

and I am also trying to just use a pipe from the child stderr to my own stdin and using cin as my stream.

In both cases I am getting -1 when I call tellg.

Here is my code with the pipe from child stderr to parent stdin:

#include <iostream>

#include <unistd.h>
#include <sys/wait.h>

using namespace std;

int
main()
{
    int mypipe[2];

    pipe(mypipe);

    dup2(mypipe[0], STDIN_FILENO);
    dup2(mypipe[1], STDERR_FILENO);

    __pid_t pid = fork();

    if(pid == 0)
    {
        // this is a process that outputs stuff into std::err
        char* argv[] = {"copy-feats", nullptr};

        int ret = execvp(argv[0], argv);

        exit(ret);
    }

    int status;
    waitpid(pid, &status, WNOHANG);

    cin.clear(); // attempting to clear the error state. Not working.
    long size = cin.tellg();
    cout << size << endl;
}

so as I said the output of tellg is -1.

thought if I try to use getline(cin, some_string) I'll actually be able to see the output of the child program.

I tried creating a stream from a pipe, but it still gives me -1.

Here's the code I used:

#include <iostream>
#include <fstream>
#include <ext/stdio_filebuf.h>

#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>

#define READ_FD  0
#define WRITE_FD 1

using namespace std;

using FilebufType = __gnu_cxx::stdio_filebuf<std::ifstream::char_type>;

int
main()
{
    int mypipe[2];

    pipe(mypipe);

    __pid_t pid = fork();

    if(pid == 0)
    {
        dup2(mypipe[WRITE_FD], STDERR_FILENO);
        char* argv[] = {"copy-feats", nullptr};

        int ret = execvp(argv[0], argv);

        exit(ret);
    }

    int status;
    waitpid(pid, &status, WNOHANG);

    FilebufType filebuf(mypipe[READ_FD], std::ios::in);
    istream is(&filebuf);

    is.clear();
    auto size = is.tellg();
    cout << size << endl;
}

Thanks in advance.

Gerhardh
  • 11,688
  • 4
  • 17
  • 39
Adham Zahran
  • 1,973
  • 2
  • 18
  • 35
  • 3
    Of course you're getting -1 from `tellg()`. Pipes are not seekable, and there is no such thing as a "file position" when it comes to a pipe. Only actual physical files are physical, and provide a file position. Only `std::ifstream`s provide file position indication, when attached to a real file. – Sam Varshavchik Dec 30 '18 at 21:20
  • Look up what that return value means. – Pete Becker Dec 30 '18 at 22:03
  • @SamVarshavchik Thank you that answers my question. I suggest you post it as an answer, to make it clearer to future visitors. – Adham Zahran Dec 30 '18 at 22:45

1 Answers1

2

In practice tellg() returns an actual file position only when the input stream is a real file. That is, when the input stream is a std::ifstream, and then only when the underlying file is a plain file.

Pipes, and other non-plain files don't have a concept of a file position.

On Linux, which you are using, tellg() is typically implemented (indirectly, but that's not relevant here) by using lseek(2), and lseek(2)'s documentation explicitly specifies that it returns an ESPIPE error if the file descriptor is a pipe. And an error return, eventually, translates to tellg() returning -1.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148