Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use cin between strings in C++?

Tags:

I'm fairly new to C++ and have been learning things like this:

cout << "My age is: "; cin >> age; 

Messing around with cin, I have come across a roadblock.

Say I wanted to write "I am x years old!".

"x" being cin >> age;

I write the code like this.

cout << "I am "; cin >> age; cout << "years old!"; 

The roadblock is that this produces a line break.

I don't want the line break.

How can I have all this stay on one line?

like image 904
JimGordon Avatar asked Dec 14 '15 23:12

JimGordon


1 Answers

I assume you actually want the input to be in line with with the eventual output. Otherwise, the solution is trivial: first read the value and then print it, e.g.:

std::cout << "how old are you? "; std::cin >> x; std::cout << "I am " << x << " years old\n"; 

Dealing with input which is in line with the output is a bit more tricky. The issue here is that the console handling is actually done by the system and not by the language. As a result, it is necessary to wait for a newline when entering data. The newline will be handled by the console and create a line break. To disable this behavior it is necessary to work around relying on the console behavior. There is no standard C++ way of doing so.

I don't know how to deal with console controls on Windows: I'm primarily a UNIX programmer. On a UNIX system you'd use tcgetattr() and tcsetattr() to set the input mode to be non-canonical (i.e., to clear the ICANON bit). The required file descriptor is simply the one for the standard input, i.e., 0.

Once the console is using non-canonical mode all key presses are immediately forwarded to application and no screen echo is done. As a result, the reading function will actually need to echo individual characters and then forward then to a parsing function. The easiest way to do is to create a filtering stream buffer which does the necessary echoing for std::cin to std::cout while forwarding the character. However, putting all of that together is a bit of work...

When cooking a demo I noticed that I also needed to deal with echoing (to avoid a newline when the user uses the enter key). It is also important to reset the console flags as some of them actually stay changed even after the program terminates. An implementation putting the bits together but still lacking error handling could look like this:

#include <iostream> #include <ctype.h> #include <termios.h>  struct non_canon_buf     : std::streambuf {     char buffer;     non_canon_buf() {         termios flags = {};         tcgetattr(0, &flags);         flags.c_lflag &= ~(ICANON | ECHO);         tcsetattr(0, TCSANOW, &flags);     }     ~non_canon_buf() {         termios flags = {};         tcgetattr(0, &flags);         flags.c_lflag |= ICANON | ECHO;         tcsetattr(0, TCSANOW, &flags);     }     int underflow() {         if (std::cin.get(buffer)) {             this->setg(&buffer, &buffer, &buffer + 1);             if (!std::isspace(static_cast<unsigned char>(buffer))) {                 std::cout << buffer << std::flush;             }             return std::char_traits<char>::to_int_type(buffer);         }         return std::char_traits<char>::eof();     } };  int main() {     non_canon_buf sbuf;     std::istream  in(&sbuf);     std::cout << "I am ";     int age = 0;     in >> age;     std::cout << " years old\n";     if (!in) {         std::cout << "ERROR: failed to enter a valid age!\n";     } } 
like image 149
Dietmar Kühl Avatar answered Oct 27 '22 22:10

Dietmar Kühl