Avoiding goto

Pages: 12
I've set a tiny program that reads your name and then checks with you if it's right, but I either repeat some code, or I use goto and I can't find a good way to do it.

The repeated code example:
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
#include <iostream>
#include <string>

int main()
{
    std::string name, rightname;

    std::cout << "Brave adventurer, what is your name?" << "\n";
    do getline (std::cin, name);
    while (name == "" or name == " " or name == "  " or name == "   " or name == "    ");

    std::cout << "\n" << "Your name is " << name << " right?" << "\n";
    getline (std::cin, rightname);

    while (rightname != "Y" and rightname != "y" and rightname != "yes" and rightname != "Yes")
    {
        std::cout << "\n" << "Oh, I'm sorry, I'm a little deaf, must be my old age. So what is your name?" <<  "\n";
        do getline (std::cin, name);
        while (name == "" or name == " " or name == "  " or name == "   " or name == "    ");

        std::cout << "\n" << "Your name is " << name << " right?" << "\n";
        getline (std::cin, rightname);
    }

    return 0;
}


And the goto example:
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
#include <iostream>
#include <string>

int main()
{
    std::string name, rightname;

    std::cout << "Brave adventurer, what is your name?" << "\n";
    do getline (std::cin, name);
    while (name == "" or name == " " or name == "  " or name == "   " or name == "    ");

    checkname:
    std::cout << "\n" << "Your name is " << name << " right?" << "\n";
    getline (std::cin, rightname);

    while (rightname != "Y" and rightname != "y" and rightname != "yes" and rightname != "Yes")
    {
        std::cout << "\n" << "Oh, I'm sorry, I'm a little deaf, must be my old age. So what is your name?" <<  "\n";
        do getline (std::cin, name);
        while (name == "" or name == " " or name == "  " or name == "   " or name == "    ");

        goto checkname;
    }

    return 0;
}


What would be the correct (and most efficient!) way to do it.
Another thing, how can you check that the first letter in the name is a character (that way you could avoid the long do while(...); and the current code will accept a name if it's 5 or more spaces)
If you really want to avoid the jump, you could always just paste the code:

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

int main()
{
    std::string name, rightname;

    std::cout << "Brave adventurer, what is your name?" << "\n";
    do getline (std::cin, name);
    while (name == "" or name == " " or name == "  " or name == "   " or name == "    ");

    std::cout << "\n" << "Your name is " << name << " right?" << "\n";
    getline (std::cin, rightname);

    if (rightname != "Y" and rightname != "y" and rightname != "yes" and rightname != "Yes")
    {
        std::cout << "\n" << "Oh, I'm sorry, I'm a little deaf, must be my old age. So what is your name?" <<  "\n";
        do getline (std::cin, name);
        while (name == "" or name == " " or name == "  " or name == "   " or name == "    ");

        std::cout << "\n" << "Your name is " << name << " right?" << "\n";
        getline (std::cin, rightname);
    }

    return 0;
}



Something even better:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <string>

int main()
{
    std::string name, rightname;

    std::cout << "Brave adventurer, what is your name?" << "\n";
    do getline (std::cin, name);
    while (name == "" or name == " " or name == "  " or name == "   " or name == "    ");
    
    do
    {
       std::cout << "\n" << "Your name is " << name << " right?" << "\n";
    getline (std::cin, rightname);

        std::cout << "\n" << "Oh, I'm sorry, I'm a little deaf, must be my old age. So what is your name?" <<  "\n";
        do getline (std::cin, name);
        while (name == "" or name == " " or name == "  " or name == "   " or name == "    ");

    } while (rightname != "Y" and rightname != "y" and rightname != "yes" and rightname != "Yes");

    return 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <string>

int main()
{
    std::string name, rightname;

    std::cout << "Brave adventurer, what is your name?" << "\n";

    while(getline (std::cin, name))
    {
        if (isalpha(name[0])) //Check if first character is space.
        {
            std::cout << "\n" << "Your name is " << name << " right?" << "\n";
            getline (std::cin, rightname);

            while (rightname != "Y" and rightname != "y" and rightname != "yes" and rightname != "Yes")
            {
                std::cout << "\n" << "Oh, I'm sorry, I'm a little deaf, must be my old age. So what is your name?" <<  "\n";
                getline (std::cin, name);

                    if (!isalpha(name[0]))
                    {
                        std::cerr << "Whoops there was an error. Do not start your name with a space" << "\n";
                        break;
                    }

                std::cout << "\n" << "Your name is " << name << " right?" << "\n";
                getline (std::cin, rightname);

            }
        }
        else
        {
            std::cerr << "Whoops there was an error. Do not start your name with a space" << "\n";
        }

        if (rightname == "Y" || rightname == "y" || rightname == "yes" || rightname == "Yes")
            break;

    }
    return 0;
}


Not sure if it's the best solution, but here's what I came up with.
Last edited on
@Bourgond Aries
Your post wasn't helpful, you didn't read my whole question.

@Olysold
Your solution was alright, but it doesn't work fully.
When you get
Whoops there was an error ...
whatever you write afterwards doesn't do what it's supposed to...
Yeah I should have actually read your entire code. I edited it above and I think it's working as you may want it. It could use some tidying up though imo.
Make a toupper helper function so instead of

rightname != "Y" and rightname != "y" and rightname != "yes" and rightname != "Yes"

you can do

ToUpper(rightname) != "Y" and ToUpper(rightname) != "YES"


Also, what happens if their name has 10 spaces?
Why don't you just check that somewhere in the string there is a valid char?
Last edited on
hmm, is there a way to make the input only accept names that have no digits or spaces or special characters in them (only letters basically)
It should be something like:
1
2
for (unsigned short i = 0; i < str.length(name); i++)
isalpha name[i];

Right?
I'm not sure, but here's how I would check.

1
2
3
4
5
6
7
8
9
10
11
12
13
string name;

    getline(cin, name);

    for (auto check : name)
    {
        if (!isalpha(check)) // Check if the string has any non-characters.
        {
            cout << "Error" << endl;
        }
        else
            cout << "No errors" << endl;
    }
Last edited on
To check isalpha() on each letter is actually bad: Some people will use their nickname and it can have digits.

In addition in some cultures names can have an apostrophe or space in them. Some characters from foreign languages can be not allowed by isalpha() too.

Names and time is the worst nightmare for programmer actually.
Then how can I allow only letters, digits and apostrophes (but not asterisks, spaces, slashes, etc.) ?
Actually only allowing letters is alright, you just have to tell the user to not use digits if they try to.
Last edited on
Once you decide on the requirements, you could define your own function, similar to isalpha() but meeting your specifications.

The string functions find_first_of or find_first_not_of could be useful, but of course the details are up to you.
http://www.cplusplus.com/reference/string/string/find_first_of/
http://www.cplusplus.com/reference/string/string/find_first_not_of/
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
39
40
41
42
43
#include <iostream>
#include <algorithm>
#include <cctype>

int main()
{
	bool answered = true;

	do
	{
		const char *prompt[] =
		{
			"Brave adventurer, what is your name? ",
			"So what is your name? "
		};

		std::string name;

		std::cout << prompt[!answered];
		do
		{
			std::getline( std::cin, name );
		} while ( ( name.empty() || 
		        !std::all_of( name.begin(), name.end(), []( char c ) { return ( isalpha( c ) ); } ) )
			&& std::cout << "? " );

		std::cout << "\nYour name is " << name << " right? ";

		std::string yes_no;
		std::getline( std::cin, yes_no );

		std::transform( yes_no.begin(), yes_no.end(), yes_no.begin(), 
		                []( char c ) { return ( toupper( c ) ); } );

		answered = yes_no == "Y" || yes_no == "YES"; 

		if ( !answered )
		{
			std::cout << "\nOh, I'm sorry, I'm a little deaf, must be my old age" 
			          << std::endl;
		}
	} while ( !answered );
}


EDIT: I made some updates.
Last edited on
@Vlad
Your code won't compile without -std=c++11
And it accepts a name if you just press enter. It doesn't accept names with spaces though.
Actually everything in your code can be bypassed by pressing enter, I think it would be better to wait until you get a valid answer.

And how can you get rid of having two variables: answered and yes_no, and replace them by one? I think this is slightly over-complicated.
Last edited on
I updated the code.
As for -std=c++11 then you should always use it.
And I do not think that introducing a variable of type bool that is set to the value of expression yes_no == "Y" || yes_no == "YES"; makes the code sligltly complicated. It makes the code more clear!
Last edited on
Also I decided to introduce a lambda-expreesion to make the code more readable

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
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <algorithm>
#include <cctype>

int main()
{
	bool answered = true;

	do
	{
		const char *prompt[] =
		{
			"Brave adventurer, what is your name? ",
			"So what is your name? "
		};

		auto InvalidName = []( const std::string &s )
		{
			return( ( s.empty() || 
			          !std::all_of( s.begin(), s.end(), 
				                []( char c ) { return ( isalpha( c ) ); } ) )
				&& std::cout << "? " );
		};

		std::string name;

		std::cout << prompt[!answered];
		do
		{
			std::getline( std::cin, name );
		} while ( InvalidName( name ) );

		std::cout << "\nYour name is " << name << " right? ";

		std::string yes_no;
		std::getline( std::cin, yes_no );

		std::transform( yes_no.begin(), yes_no.end(), yes_no.begin(), 
		                []( char c ) { return ( toupper( c ) ); } );

		answered = yes_no == "Y" || yes_no == "YES"; 

		if ( !answered )
		{
			std::cout << "\nOh, I'm sorry, I'm a little deaf, must be my old age" 
			          << std::endl;
		}
	} while ( !answered );
}


Hope it will be compiled.:)
Last edited on
To filter only letter you could do something like this...

 
if (toupper(letter) >= 'A' && toupper(letter) <= 'Z') 
Is letter like a function or is it a char array and you have to assign every element in the string to the array and then run this condition for the whole string length?

I'm really confused right now and I can't see how your code can work, unless the compiler knows the alphabet.
Yeah, I'm tired... sorry if this is silly me...
Last edited on
@Vidminas



You can try my program at www.ideone.com selecting C++11. At least it is compiled with MS VC++ 2010.
Pages: 12