5

So, I've been trying to figure out, how to wait for data from a C++ stringstream (for instance), without being constantly checking if data is there, which is quite CPU consuming.

I'm perfectly able to read, for instance, from a serial device, and lock the process while no data arrives, but unfortunately I haven't been able to figure how to do that with C++ streams.

I'm sure I'm missing something, since cin does exactly that, i.e., waits for the return key to step out from istream reading, but how does it do it?

Thanks in advance for any light on the subject.

cvicente
  • 132
  • 3
  • 12
  • Have you tried e.g. [`std::getline`](http://en.cppreference.com/w/cpp/string/basic_string/getline)? – Some programmer dude Sep 10 '12 at 11:57
  • All streams work the same. If you create a stream from blocking file descriptor, it will be blocking (just like cin, it's also created that way). So what have you tried and does not work for you? – Jan Hudec Sep 10 '12 at 12:08
  • @JoachimPileborg Yes I did! Does it lock for you? – cvicente Sep 10 '12 at 12:11
  • @JanHudec All streams do _not_ work the same. Or rather, all `streambuf`s don't work the same. (The various streams just forward to a `streambuf`.) In particular, `filebuf` has the notion of `open`, and depends on the OS to tell it when there are no more characters to be read. This concept is absent in `stringbuf`. So it can't wait for the "output" side to be closed to declare end of file on the input. – James Kanze Sep 10 '12 at 12:16
  • @JanHudec It's quite simple, each time it reaches eof, it just quits whatever method I'm using to read the stream (getline, read...). So, but you're saying I should be able to create a C++ stream based on a descriptor without using any external libs? – cvicente Sep 10 '12 at 12:17
  • @cvicente: Each time it reaches eof, it just quits. That's how it's designed. But when reading blocking file descriptor, the underlying function will simply block, not reach eof. Can you please mention specific functions you are using to create the stream? Or better yet show a code sample? – Jan Hudec Sep 10 '12 at 12:20
  • @JamesKanze: To be more specific, `cin`, `cout`, and `fistream` and `fostream` all *do* work the same and so *do* their streambufs. They work the same, because the underlying system calls (or rather ANSI C library calls that file streams normally work on) are the same for those two cases. And as long as a device can be used with ANSI C functions (on POSIX, anything can, on Windows you are out of luck and have to write the streambuf yourself or get one somewhere), it can be used and will work out of the box. – Jan Hudec Sep 10 '12 at 12:29
  • @JanHudec `cin`, `cout`, `ifstream` and `ofstream` work the same, because they all use `filebuf`. The OP asks about `stringstream`, which uses a `stringbuf`. A lot of the time, of course, you'll be using some other `streambuf`. (And I've not noticed a great deal of difference between Unix and Windows in this respect. In both, `filebuf` are usually normally named files, but you can also open them on special devices which behave differently.) – James Kanze Sep 10 '12 at 16:52
  • @JamesKanze: Ok, right. I ignored the stringstream mention, because device was mentioned and stringstream wouldn't be used for reading device. – Jan Hudec Sep 11 '12 at 08:27

2 Answers2

3

Streams obtain data from an std::streambuf. For the case of std::cin it calls the system's read() function (or equivalent) which blocks until data is handed over from the operating system. The console normally sends complete lines. String streams have no concept of getting new data, i.e., they would fail when having reached the end of the current data. There isn't a concept of blocking.

You didn't quite say what you are doing but from the sounds of it try to communicate data between two threads: one reading and possibly blocking until data is available and one filling in more data. You could create a corresponding stream buffer quite easily: std::streambuf::underflow() would wait() on a condition variable if there is no data. std::streambuf::overflow() would set up the buffer appropriately and signal the condition variable. Obviously, there is some need of synchronization necessary. Most of the reading and writing isn't doing any synchronization, though. This effectively means that you will need two separate buffers for input and output and need to copy the data in std::streambuf::underflow().

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • I considered this. But wouldn't a `std::deque` using the basic binary data be more appropriate? The whole point about using threads (instead of separate processes) is that you don't have to marshal the data. (Alternatively, if I did want to use streams here, I think I'd go with two separate streambuf types, which collaborate.) – James Kanze Sep 10 '12 at 16:55
  • From the sounds of it the original request wanted to read from a stream and, presumably, write to it. If the logic to interact with a stream alread exists it may be reasonable to use streams to interact. Normally, I would communicate with something looking like a queue, possibly implemented in terms of a `std::deque`. – Dietmar Kühl Sep 10 '12 at 17:40
1

You can't. The only way std::stringstream can know that there isn't any further data is that there isn't any data in the stream.

It's an interesting thought, however. << on an std::stringstream only returns true if the write side has been closed. The only problem with the idea is that std::stringstream doesn't have any idea of open or closed.

The real question, however, is what you're trying to achieve. std::stringstream operates in process, and in that case, there's really no need for formatting (which is the abstraction of the iostreams in general). Just stuff the raw objects in an std::deque. The only time you should need to worry about formatting is when communicating with the exterior. std::istringstream is very useful, for example, when you're getting the string from std::getline on a file, or from some other source. Similarly, std::ostringstream is useful when formatting data for some external source which requires a string of some sort. But I've never found a use for std::stringstream; it's there more for reasons of orthogonality, I think.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Sure you can. If the `streambuf::uflow` blocks, the stream blocks. End of story. – Jan Hudec Sep 10 '12 at 12:40
  • 1
    @JanHudec The implementation of `stringbuf::uflow` doesn't block. End of story. – James Kanze Sep 10 '12 at 16:57
  • Right. The question is really mixed up though; the answer probably should be "whatever you are trying to do, it probably does _not_ involve stringstreams". – Jan Hudec Sep 11 '12 at 09:20
  • @JanHudec That's more or less my conclusion too. (I don't think I've ever uses `std::stringstream`.) – James Kanze Sep 11 '12 at 10:16
  • I use stringstreams often. They are useful for formatting and parsing. But not for passing data. – Jan Hudec Sep 11 '12 at 13:04
  • @JanHudec `istringstream` is useful for parsing, and `ostringstream` for formatting. I use those two often. But I've never found a use for the bi-directional `stringstream`; there's generally no point of formatting something only to reparse it in the same process. – James Kanze Sep 13 '12 at 08:30