Char based loop interfering with getline()

* Pretext - i.e., it's homework, but I'm not looking for "the" answer *

I'm currently in my first C++ programming class and was looking for some help. I like to try to challenge myself on homework assignments by including things that aren't necessary but, realistically, are something you would want in a real world scenario. More specifically, with our homework assignments, all that is required is a single run of a program. However, for most programs, I like to insert a loop that allows you to continue the program without having to relaunch it. This week, we started learning about strings which is causing a conflict with the loop I had always used.

**** Problem ****

Basically, I would always loop my programs with a char variable as I can use toupper() to modify the input. However, now the first thing being executed in a loop is a getline(). On the first run, everything is fine. When I get to the "Would you like to play again" portion and I assign a "Y" to the playAgain, the loop repeats and the cout prior to the getline() displays twice. Technically the program still functions, but it obviously is not very aesthetically pleasing...

My understanding is that it is probably caused by the '/n' that is technically at the end of the char variable creating an invalid value for my getline(), executing it twice. The problem is, if I use cin.ignore before it, my program just displays completely blank.

Is there another way to get around this? Or do I need to start using a different solution for the loop?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int main()
{
	string origWord = "";
	char playAgain = 'Y';
	string displayWord = "";

	while (toupper(playAgain) != 'N')
	{
		if (toupper(playAgain) == 'Y' || toupper(playAgain) == 'N')
		{
			while (toupper(playAgain) == 'Y')
			{
				system("cls"); // Clears previous game in loop
				while (origWord.length() < 1)
				{
					cout << "Enter a word in uppercase: ";
					getline(cin, origWord);

				[*The rest of the actual "program" goes here*]

				} // End while

				origWord = ""; // Resets word length
				cout << endl << endl << "Would you like to play again? (Y/N): ";
				cin >> playAgain;

			} // End "Y" While
		} // End of "if" portion of if
		else
		{
			cout << endl << "Invalid selection, please try again." << endl;
			cout << "Would you like to play again? (Y/N): ";
			cin >> playAgain;

		} // End else
	}// End while
    return 0;
} // End of main function 
Last edited on
I like to think that it is the responsibility of the code that asks for the input to consume the whole line (including the end-of-line character). If not getline is used to read the input I would use the ignore function to skip the rest of the line.

1
2
3
cout << "Would you like to play again? (Y/N): ";
cin >> playAgain;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
I tried using that, but once I type "Y" and press enter, the line returns and the cursor just sits there blinking and I can't enter anything anymore (instead of restarting the program with "Enter a word in uppercase"). I'm unfamiliar with most of what you put inside of cin.ignore(). Is there something specific I needed to define?
The first argument to ignore is the maximum number of characters that you want to ignore. I used numeric_limits<streamsize>::max() because it's the biggest value possible but using any large value should be fine as long as the user doesn't input a line with more additional characters than that.

The second argument '\n' is used because we want the function to stop ignoring input when it finds the newline character. Note that you need to spell '\n' with a backslash. It will not work correctly if you use a forward slash /.
Last edited on
Ah! I did incorrectly use a forward slash. Everything works fine now!

Thank you so much, Peter! I had been trying to figure this out for hours... I'm so glad I posted here!
Peter87's advice is good.
For the particular usage you'll need to #include <limits>.
or just put a large number:
1
2
    cin >> playAgain;
    cin.ignore(1000, '\n');

The important thing is that the ignore() goes together with the cin >>, to clean up the remaining '\n' and anything else from the input buffer.


You might simplify the logic a bit too,,,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int main()
{
    char playAgain = 'Y';

    while (playAgain == 'Y')
    {
        system("cls"); // Clears previous game in loop
        
        string origWord;
        while (origWord.length() < 1)
        {
            cout << "Enter a word in uppercase: ";
            getline(cin, origWord);
        }
        
        //---------------------------------------------------
        //   [*The rest of the actual "program" goes here*]
        cout << "actual program " << origWord << '\n';
        //---------------------------------------------------

        cout << "\n\nWould you like to play again? (Y/N): ";
        cin >> playAgain;
        playAgain = toupper(playAgain);
        while (playAgain != 'Y' && playAgain != 'N')
        {
            cout << "\nInvalid selection, please try again.\n"
                 << "Would you like to play again? (Y/N): ";
            cin >> playAgain;
            playAgain = toupper(playAgain);
        }
        cin.ignore(1000, '\n');
    }
    
}
@ Peter87,

You put it up so I will ask you first.
cin.ignore(numeric_limits<streamsize>::max(), '\n');.

Every time I try to use this in my VS 2015 "max()" always gives me an error. First it shows me the #define for max then it says "expected an identifier". If I try putting parameters in the () I still gt an error.

Any thoughts?

Andy
@Chervil

Awesome tips for cleaning up the code and simplifying. Thank you!
Every time I try to use this in my VS 2015 "max()" always gives me an error. First it shows me the #define for max then it says "expected an identifier". If I try putting parameters in the () I still gt an error.


I'm not using VS 2015 so I can't attempt to replicate this.

However, two thoughts arise.
1. does your own code have a #define for max in it? If so, that could be the cause. Please show your code if that's the case.

2. Could you copy+paste the full text of the message from the compiler verbatim for this error, rather than describing it in your own words.
@Chervil

No there is no #define for max it is the only code in main. I also tried "#include <limits>, but that did not help.

The error listed are:
1
2
3
4
5
Severity Code	Description	                                Project         File                                                                                           Line
Error	C2059	syntax error : '::'	                        Test Code	c : \users\owner\documents\visual studio 2015\projects\test code\test code\test code.cpp        65
Error	C2059	syntax error : '::'	                        Test Code	c : \users\owner\documents\visual studio 2015\projects\test code\test code\test code.cpp	65
Warning	C4003	not enough actual parameters for macro 'max'	Test Code	c : \users\owner\documents\visual studio 2015\projects\test code\test code\test code.cpp	65
Error	C2589	'(': illegal token on right side of '::'	Test Code	c : \users\owner\documents\visual studio 2015\projects\test code\test code\test code.cpp	65
.

I thought it might be the lack of a header file, but I believe all header files that needs to be there are there. For now I just put a large number for the first parameter hoping some day I would figure out what is going wrong.

The code is simple:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <iomanip>  //  setw()
#include <conio.h>  // _getch()
#include <Windows.h>

int main()
{
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
    
    return 0;
}
.

I put this line at the beginning of main before an existing program, but the rest off the code was commented out, so it did not apply.

Andy
@Handy Andy

This may help:
http://stackoverflow.com/questions/6884093/warning-c4003-not-enough-actual-parameters-for-macro-max-visual-studio-2010

It suggests the solution is to put #undef max to undefine the max macro (apparently in windows.h).
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <limits>
#include <Windows.h>
#undef max

using namespace std;

int main()
{
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
}


Or maybe to put #define NOMINMAX before the includes.

I'm unable to test those options as the error doesn't arise with my compiler.
Last edited on
@Chervil,

Thank you the "#undef max" worked. I will put that to use the next time I ha to use it.

Andy
There exists a macro #define NOMINMAX exactly for this purpose, as windows.h has these nasty min and max macros.

NOMINMAX must be defined before windows.h is included:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <limits>
#define NOMINMAX
#include <windows.h>

int main() {

	auto count = std::numeric_limits<std::streamsize>::max();
	char delim = '\n';
	std::cin.ignore(count, delim);

	return 0;
}


Another way of avoiding the macro clash is to do:
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <limits>
#include <windows.h>

int main() {

	auto count = (std::numeric_limits<std::streamsize>::max)();
	char delim = '\n';
	std::cin.ignore(count, delim);

	return 0;
}

Notice, line 7 has changed. This prevents "max" from being interpreted incorrectly.
Last edited on
Topic archived. No new replies allowed.