4

Is it possible to read from named pipe (mkfifo) using c++ (stl) using a stream - thus not defining in advance char *buffer[MAX_SIZE] for the read operation?

I want to read till the buffer ends and put the result into std::string.

(Current method: bytes = read(fd, buffer, sizeof(buffer)); requires allocation some kind of buffer in advance.)

Jonathan Mee
  • 35,107
  • 16
  • 95
  • 241
Dani
  • 13,366
  • 11
  • 56
  • 99

2 Answers2

7

Named pipes created with mkfifo behave like regular files. Thus they can be accessed using std::ifstream and std::ofstream:

#include <fstream>
#include <iostream>

int main(int, char** argv) {
    std::ifstream file{argv[1]};
    std::string line;
    std::getline(file, line);
    std::cout << line << '\n';
}

Run:

mkfifo foobar
./main foobar

And elsewhere:

echo 'Hello world!' > foobar

… this will cause ./main to print “Hello world!” to the standard output.

Konrad Rudolph
  • 482,603
  • 120
  • 884
  • 1,141
  • 1
    Thanks, can I make it read till the buffer ends ? what happens if someone fills the buffer while I read ? – Dani Nov 22 '16 at 12:09
  • 1
    @WhozCraig Uh, fail. Fixed it. – Konrad Rudolph Nov 22 '16 at 12:26
  • 2
    @Dani You read it like any other stream (see [elsewhere](http://stackoverflow.com/q/116038/1968)). If your program is running while some other process is filling it the program will read until the other process closes the pipe. – Konrad Rudolph Nov 22 '16 at 12:29
  • 1
    @KonradRudolph - thanks again. will the getline version retrieve one line or all the content (lines) until the pipe closes ? I'm waiting to a large text message... – Dani Nov 22 '16 at 12:55
  • 1
    @Dani It will retrieve a single line. Check out the documentation and the Stack Overflow thread I linked in my last comment for solutions of how to read until the end of the stream. – Konrad Rudolph Nov 22 '16 at 12:57
  • @Dani If this solved your problem don't forget to accept. – Jonathan Mee Nov 22 '16 at 20:55
  • I will not - I will run the code hopefully today and accept :-) – Dani Nov 23 '16 at 06:23
  • Note 1: that your program doesn't need to take the name of the pipe as a parameter, and you don't need to create a file stream around it. An option is simply to feed the pipe into the program's stdin, and read from std::cin. Example: use std::getline(std::cin, line); to read in the program. And run the program like this: ./main – codesniffer Feb 03 '18 at 13:35
  • Note 2: On my system (Gentoo Linux kernel 3.14 with GCC 4.9), the first line that was echo'ed into the pipe was read fine by the program. But then the stream's EOF and FAIL flags got set and it would not read anything else from the pipe. I had to call "clear()" on the stream before each call to getline for it to work properly. – codesniffer Feb 03 '18 at 13:41
  • @codesniffer That entirely depends on how you fill the fifo, and shouldn’t depend on the system. The EOF flag is set (and needs to be cleared) whenever the end of a stream is reached; regardless of whether that’s a fifo, an unnamed pipe or (in principle) a regular file. – Konrad Rudolph Feb 03 '18 at 14:14
  • @ KonradRudolph I'm not sure that's true. The behavior I described happens when using C++ streams (both ifstream to open the FIFO by name on the file system, and std::cin to read from the FIFO being directed into the program's stdin). However the same program, using the same input method (echo from another terminal) does *not* exhibit this behavior if the input is read via fread. When I used fread I did not have to affect the input "FILE" in any way--each call to fread provided whatever was in the FIFO. So it seems C++ streams are interpreting something with the input and setting EOF. – codesniffer Feb 05 '18 at 17:33
  • @codesniffer Right, but C++ streams are *supposed to* interpret it that way, regardless of OS. But if the command filling the FIFO has finished by the time your program starts reading it, this won't happen. — I can't comment on `fread`, I never use it and I'd have to check its documentation. – Konrad Rudolph Feb 05 '18 at 18:46
  • @KonradRudolph If I recall correctly, the behavior I describe occurred regardless if data was sent to the FIFO before the program started or after. If you mean that all data that was sent to the FIFO before the program started was then available to the program, then yes, that's the behavior. I can't comment if that's the intended behavior, but for me it's a poor behavior because it does not align with the behavior of another pipe (namely stdin from a terminal). I was hoping it would not consider the FIFO to "end" until either the FIFO closed or an EOF char (like Ctrl+D issues) was seen. – codesniffer Feb 05 '18 at 19:03
4

There's nothing magical about piping to your program it just turns your cin reading from the stream instead of the user's input from the console: Linux terminal pipe to my C++ program

A simple glance at this question's edit history will show that this question has greatly improved from it's original version thanks to Konrad Rudolph (the other answerer on this question.) In a move of further dastardliness I'm going to scrape 2 of his solutions for slurping a stream into string:

istreambuf_iterator method:

const string mkfifo{ istreambuf_iterator<char>(cin), istreambuf_iterator<char>() };

stringbuf copy method:

istringstream temp;
temp << cin.rdbuf();
const auto mkfifo = temp.str();

You can read about the pros and cons of each on their respective posts. To use this code, say that your compiled program is named main you would pipe to it like this:

mkfifo named_pipe
echo "lorem ipsum" > named_pipe
./main named_pipe
Community
  • 1
  • 1
Jonathan Mee
  • 35,107
  • 16
  • 95
  • 241
  • This yields “no matching function for call `getline` since `EOF` is of the wrong type (`int`). – Konrad Rudolph Nov 22 '16 at 12:31
  • 2
    Regarding your edit: what happens if some other process writes `static_cast(EOF)` into the pipe, followed by more contents (genuine question: I have no idea what would happen and whether the behaviour is guaranteed, but I suspect that the stream would be prematurely truncated, and I don’t think casting `EOF` to `char` is a general solution). – Konrad Rudolph Nov 22 '16 at 12:35
  • 1
    @KonradRudolph Wow, I'd always just used `EOF`. After researching your comment I'm clearly not as clever as I thought :( – Jonathan Mee Nov 22 '16 at 13:16
  • 2
    Hmm. This may be a tad petty but [both](http://stackoverflow.com/a/116177/1968) of the [methods](http://stackoverflow.com/a/116220/1968) you’ve linked to have been suggested by me almost two years before the answers you refer to. Of course neither of them is interesting enough to warrant attribution but at the time they were quite novel. – Konrad Rudolph Nov 23 '16 at 10:42
  • @KonradRudolph I've updated to appropriately give you credit. A couple questions here: 1) Why hasn't [the question I linked](http://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring) been closed as a duplicate? (I have voted to close it.) 2) Is there a question asking for a comparison of these methods? They seem to be the best slurping methods available to us in C++ thus I'd like to know when to opt for what. – Jonathan Mee Nov 23 '16 at 12:21
  • 2
    Unfortunately I don’t know of a good comparison. The problem is compounded by the fact that different compilers/standard libraries do *very* different things here. For instance, with libstdc++, the iterator method is on par with `read`ing into a preallocated `std::string`. By contrast, with libc++, the latter is vastly more efficient. :-( – Konrad Rudolph Nov 23 '16 at 15:08