Program access

I am seeking clarification on a problem that surprisingly is largely ignored by
every textbook on C++ that I have read so far.

C++ is very efficient at "blocking" unwanted input from interactive programs, for example a program receiving integer or double type variables may "block" alphabet letters from access so as to avoid unexpected results.

Sometimes it is necessary, in the case of required repetition, to permit a Yes/No option to be implemented as Y/N, y/n.

I would be grateful for an explanation of the theory and implementation of such a Manoeuvre. I am not interested in code at this time at least not until the theory is clarified.
What do you mean "theory"?
1
2
3
4
5
6
7
8
9
10
11
12
13
bool yes_no_input(){
    while (true){
        std::string line;
        std::getline(std::cin, line);
        if (!line.size())
            continue;
        char c = tolower(line[0]);
        if (c == 'y')
            return true;
        if (c == 'n')
            return false;
    }
}
Last edited on
Thanks for your effort and your response but you have completely misunderstood the request.

1. The Program accepts only numbers and blocks out alphabet characters/letters.
2. The programmer now wants to use the yes/ no in that environment by bypassing the protection against the use of characters/letters.
1. The Program accepts only numbers and blocks out alphabet characters/letters.
This is not possible, at least not with any functionality provided by standard libraries.
If you want to read an input from the user that could be either a number, or "yes", or "no", then you'll need to:

- read the user's input into a string, rather than a numerical type
- parse the string, to determine whether it contains any of the valid inputs
closed account (48T7M4Gy)
As a rudimentary starting point for double (or int etc)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

int main()
{
    
    double number = 0;
    std::cout << "Enter a number:";
    
    while (std::cin >> number) //and (double)(number) == number)
    {
        std::cout << number << '\n';
    }
    
    std::cout << "Ends\n";
    return 0;
}
Enter 23.5
23.5
we
Ends
Program ended with exit code: 0
Last edited on
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
#include <iostream>
#include <string>

int main(int argc, char **argv)
{
  int input = 0;
  std::string invalid_input;

  std::cout << "Enter a number: " << std::endl;

  int tries = 3;

  while (tries)
  {
    if (!(std::cin >> input))
    {
      std::cout << "Invalid input" << std::endl;
      std::cin.clear();
      std::getline(std::cin, invalid_input);
      std::cout << "\tEntered: " << invalid_input << " instead of a number" << std::endl;
    }
    else
    {
      std::cout << "Entered integer: " << input << std::endl;
    }
  tries--;
  }

}


std::cin >> integer will fail if something other than an integer was entered by the user. But you must now clear the failure and either retrieve the invalid input or discard it from the stream - that will not be taken care of for you.
1. The Program accepts only numbers and blocks out alphabet characters/letters.

Okay, so your original program may have looked something like
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>
int main()
{
  int n;
  for(std::cout << "Enter an integer\n"; !(std::cin >> n);)
  {
    std::cin.clear();
    std::string junk;
    std::getline(std::cin, junk);
    std::cout << "Sorry, but \"" << junk << "\" is not an integer\n";
  }
  std::cout << "Thank you for entering the integer " << n << '\n';
}

(note; this isn't quite correct since it doesn't handle EOF, just a sketch to demo the idea)

2. The programmer now wants to use the yes/ no in that environment by bypassing the protection against the use of characters/letters.

You already got some very practical advice on how to do this (accept any string and parse it)

But from computer science perspective what this means is that instead of reading a value of integer type (and rejecting inputs that don't represent such values), your program now reads a value of some *other* type. Namely, a product type that is either an integer (and if so, can hold any valid integer value) or a boolean (and if so, can hold any valid boolean value)

something like this

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
#include <iostream>
#include <string>
#include <cctype>
struct IntOrYN {
   enum {INT, BOOL} tag; // or use boost::variant aka C++17's std::variant
   union { int n; bool b; };
   friend std::istream& operator>>(std::istream& is, IntOrYN& val) {
     int c = std::tolower(is.get());
     if(c == EOF) return is;
     if(c == 'y' || c == 'n') {
       val.tag = BOOL;
       val.b = c == 'y';
       return is;
     }
     if(is.putback(c) >> val.n) val.tag = INT;
     return is;
   }
};
int main()
{
  IntOrYN val;
  for(std::cout << "Enter an integer, y, n, Y, or N\n"; !(std::cin >> val);)
  {
    std::cin.clear();
    std::string junk;
    std::getline(std::cin, junk);
    std::cout << "Sorry, but \"" << junk << "\" is not an integer, y, Y, n, or N\n";
  }
  switch(val.tag) {
    case IntOrYN::INT : std::cout << "Thank you for entering the integer " << val.n << '\n'; break;
    case IntOrYN::BOOL : std::cout << "Thank you for entering the choice " << (val.b?"yes":"no") << '\n'; break;
  }
}

Last edited on
closed account (48T7M4Gy)
Given all that it would appear that reading and parsing the stream is undoubtedly the best way since the more the code gets bloated trying to handle the problem the more it gets back to rewriting the original <iostream> code, albeit in more and more complex higher language. (Hence my deliberately stopping at 'rudimentary'.)
Many thanks for your generous and useful responses.I have found your comments useful even when it did not appear to deal with the question.

I have spent some trying to understand the code provided. I still need clarification on some of the comments and will attempt to follow the order in which the comments appear above from time to time as I complete the examination of the code samples.

*********************************************************************
@helios:

First the question: theory deals with things like: which header is required, what problems can be anticipated for a particular implementation (An example the known problem with cin buffer retention requiring a flush, another example would be whether an implementation can trigger an unexpected loop).So theory is about the rules concerning how things work.The other comments, including your second comment, represent the theory.

Code samples do show how things are done but not why. I learn better and faster and remember more if I know why things are done.

The code you supplied shows "while(true)" and embedded "return true" in an if statement. It seems to me that this will generate a continuous loop, so a break statement might be required. In addition I am not sure how the function (action to be undertaken when the if statement returns true) is "seen" in the if statement I need your guidance here.

You stated "This is not possible, at least not with any functionality provided by standard libraries." It is in fact possible and very effective. As I understands it "cin" is an iostream object and therefore very much from the Standard Library. The applicable code with #include <limits> could be:


 if (! cin)
	         {
		    cout <<  "Only numbers are allowed.\n";
		    cin.clear();	
		    system("pause");
	            cin.ignore(std::numeric_limits<streamsize>::max(), '\n') ;	            
	         }


The above code works efficiently at the end of a for(;;;) in which a switch statement is embedded with all of the above embedded in try/catch operating in main().

An improved code sample for the above would be appreciated.


The code you supplied shows "while(true)" and embedded "return true" in an if statement. It seems to me that this will generate a continuous loop, so a break statement might be required.
If you think this, you need to continue learning the language.

ou stated "This is not possible, at least not with any functionality provided by standard libraries." It is in fact possible and very effective. As I understands it "cin" is an iostream object and therefore very much from the Standard Library. The applicable code with #include <limits> could be: [code]
Your statement seemed to imply that the program is filtering non-numeric characters in a global manner, perhaps by not reading from stdin, or by somehow filtering stdin.
The code you've posted is compatible with my suggestion.

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
#include <iostream>
#include <limits>

bool yes_no_input(){
    while (true){
        std::string line;
        std::getline(std::cin, line);
        if (!line.size())
            continue;
        char c = tolower(line[0]);
        if (c == 'y')
            return true;
        if (c == 'n')
            return false;
    }
}

int main(){
    do{
        int a;
        while (true){
            std::cout << "Enter a number: ";
            std::cin >> a;
            if (!std::cin){
                std::cout << "Only numbers are allowed.\n";
                std::cin.clear();
                std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                continue;
            }
            break;
        }
        std::cout << a << std::endl
            << "Continue? ";
    }while (yes_no_input());
    return 0;
}
I appreciate your advise, in fact I consider myself to be still on the ground floor with respect to C++, hence the reason for raising questions at this site.

I have attempted to implement your code. While the terminology that I used to describe my concern may have been incorrect, the problem I anticipated did in fact materialize. It turns out that only "true" is recognized from the while loop; 'y' and 'n' produce the same result.

The following code sample is what I attempted.

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
bool yes_no_input()
{
	while(true)
	{
		std:: string line;
		std::getline(std::cin, line);
		if(!line.size())
			continue;
		char c = tolower(line[0]);
		if(c=='y')
		  return true;
		  
		if(c=='n')
		  return false;
			
	}

}



bool areaSquareCycle()
{
	
	cout << "Do you want to add another area ? (Y)es (N)o  ";
			
	yes_no_input();
	
	if(true)
	   processArea();
	   
	else if(false)
	   selectArea();

}


I would appreciate you comments.
1
2
3
4
5
6
7
8
9
10
11
12
13
bool areaSquareCycle()
{
	
	cout << "Do you want to add another area ? (Y)es (N)o  ";
			
	
	if(yes_no_input())
	   processArea();
	   
	else
	   selectArea();

}

Previously you just had if (true) which will always be true, so you'll always go into processArea()
You need to store the result from the function somewhere, obviously. if (true) do_something(); has the same effect as do_something();. It will always take the true branch.

1
2
3
4
5
bool yes = yes_no_input();
if(yes)
    processArea();
else
    selectArea();

Alternatively:
1
2
3
4
if(yes_no_input())
    processArea();
else
    selectArea();
Many thanks for the info:

This ( the last two messages) works for 'y' only. I must enter 'n' twice for else to take effect.
Many thanks for allowing me to solve it on my own. The problem was in the selectArea() function.

I am indeed grateful for all the help. Many thanks again.
I have added the following improvements.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bool Materials::yes_no_input()
{
	while(true)
	{
		std:: string line;
		std::getline(std::cin, line);
		if(!line.size())
			continue;
		char c = tolower(line[0]);
		if(c=='y')
		  return true;
		  
		if(c=='n')
		  return false;
		  
		else
		  cout <<  "Enter y or n only  \n";
		  cin.clear();	
	}

}


Thanks again and hope others can benefit.
Topic archived. No new replies allowed.