Avoiding goto

Pages: 12
Sorry for the late response...
Vlad, I wasn't referring to your code there, I don't understand how could IceThatJaw's example work...

I did a little bit of changes in the code, posted by vlad from moscow:
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
#include <iostream>
#include <algorithm>
#include <string>

int main()
{
	bool answered = true;

	do
	{
		const std::string prompt[] =
		{
			"Brave adventurer, what is your name? \n-> ",
			"So what is your name? \n-> "
		};

		auto InvalidName = []( const std::string &s )
		{
			return( ( s.empty() || !std::all_of( s.begin(), s.end(),
                      []( char c ) { return ( isalpha( c ) ); } ) )
				&& std::cout << "\n" << "Your name cannot contain digits, symbols, or spaces!" << "\n-> " );
		};

		std::string name;

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

		std::cout << "\n" << "Your 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 << "\n" << "Oh, I'm sorry, I'm a little deaf, must be my old age..." << "\n";
		}
	} while ( !answered );
}


I think this code is really confusing! Isn't there a way to simplify it?
Also, this little program accepts your name if you just press enter, how can i tell the program to just ignore any enter key presses as long as the name doesn't contain a single letter? (or a digit as well, this would make the warning make more sense)
Last edited on
Okay, I'm sorry, I know Vlad doesn't find that code complicated at all, but I'm just a newbie...
I tried to write another program with similar functionality and I ended up having to copy-paste almost the whole code twice!
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include <algorithm>
#include <string>

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

    std::cout << "Brave adventurer, what is your name? \n->";
    getline (std::cin, name);

    while (true)
    {
        found = name.find_first_not_of("ABCDEFGHIJKLMNOPRSTUVWXYZabcdefghijklmonprstuvwxyz0123456789");

        if (found != std::string::npos)
        {
            std::cout << "The first invalid character is " << name[found];
            std::cout << " at position " << found << "\n\n";
        }

        else break;

        std::cout << '\n' << "Your name contained invalid characters, please choose another name: \n->";
        getline (std::cin, name);
    }

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

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

    while (rightname != "Y" 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);

        while (true)
        {
            found = name.find_first_not_of("ABCDEFGHIJKLMNOPRSTUVWXYZabcdefghijklmonprstuvwxyz0123456789");

            if (found != std::string::npos)
            {
                std::cout << "The first invalid character is " << name[found];
                std::cout << " at position " << found << '\n';
            }

            else break;

            std::cout << '\n' << "Your name contained invalid characters, please choose another name: \n->";
            getline (std::cin, name);
        }

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

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

    return 0;
}


I think this isn't too bad, but a bit extensive... Also this program doesn't allow any spaces, because I couldn't think of a way to make sure that the first letter is not a space and then if it isn't check for invalid characters.
You could include here the space character

found = name.find_first_not_of(" ABCDEFGHIJKLMNOPRSTUVWXYZabcdefghijklmonprstuvwxyz0123456789");

if you want that it must be allowed.

@Vlad, that's what I did originally, but then I get the same problem that someone can just press "Enter" or input some spaces and the name will accept it. How do I check that the first char is not a space and only then do the found = name.find_first_not_of(" ABCDEFGHIJKLMNOPRSTUVWXYZabcdefghijklmonprstuvwxyz0123456789");?
You can remove leading spaces from the string and when check whether a string is empty.
Thank you, Vlad (why didn't I think of this before?)
I think I've just been leveled-up as I was able to make quite many changes :P
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
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <algorithm>
#include <string>

void GetInput(std::string question, std::string &name);
void CheckName(std::string &name, std::string &rightname);

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

    GetInput("Brave adventurer, what is your name? \n-> ", name);
    CheckName(name, rightname);

    while (rightname != "Y" and rightname != "YES")
    {
        GetInput("\nOh, I'm sorry, I'm a little deaf, must be my old age. So, what is your name? \n-> ", name);
        CheckName(name, rightname);
    }

    return 0;
}

void GetInput(std::string question, std::string &name)
{
    do
    {
        std::cout << question;
        getline (std::cin, name);

        while (name[0] == ' ')
        name.erase ( name.begin() );

    } while ( name.empty() );
}

void CheckName(std::string &name, std::string &rightname)
{
    std::string question = "\nYour name is ";
    question.append(name);
    question.append(", right?\n");
    unsigned found;

    while (true)
    {
        found = name.find_first_not_of(" ABCDEFGHIJKLMNOPRSTUVWXYZabcdefghijklmonprstuvwxyz0123456789");

        if (found != std::string::npos)
        {
            std::cout << "The first invalid character is " << name[found];
            std::cout << " at position " << found << "\n\n";
        }

        else break;
        GetInput("\nYour name contained invalid characters, please choose another name: \n-> ", name);
    }

    GetInput(question, rightname);

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


The problem here is that when CheckName calls GetInput the name never actually changes, resulting in the program leaking a name with invalid characters.
Like this:

Brave adventurer, what is your name?
-> asd%$^
The first invalid character is % at position 3


Your name contained invalid characters, please enter another name:
-> asdf

Your name is asd%$^, right?

You could write function getInput simpler

1
2
3
4
5
6
7
8
9
10
void GetInput(std::string question, std::string &name)
{
    do
    {
        std::cout << question;
        getline (std::cin, name);

        name.erase( 0, name.find_first_not_of( " \t", 0 ) );
    } while ( name.empty() );
}


As for function CheckName then apart from that it has a very bad design (it shall do only a check of entered data) you should place the code snip

1
2
3
    std::string question = "\nYour name is ";
    question.append(name);
    question.append(", right?\n");


after the loop while ( true ) and before statement

GetInput(question, rightname);
Last edited on
That's great, it fixed everything.
I'm not sure what you mean
it shall do only a check of entered data
but I know that if you have to move the code to a different place for it to work, that's most likely bad design.
What is the real problem behind this and what should I do?
The bad design means that the function does other things apart from checking entered data.
True, but in the code I have to check the input data and prompt the user to re-enter it if the data contained invalid characters more than once. This is why a function is more efficient. Is it alright to just re-name it, or should I make another function for the input of rightname?
The function shall contain only checking the entered data. Other logic should be moved outside it and either placed in main or some other function.
Last edited on
Is this better? (edited)
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <iostream>
#include <algorithm>
#include <string>

void GetInput(std::string &name);
void CheckName(std::string &name);
void VerifyName(std::string &name, std::string &rightname);

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

    std::cout << "Brave adventurer, what is your name?";
    GetInput(name);
    CheckName(name);
    VerifyName(name, rightname);

    while (rightname != "Y" && rightname != "YES" && rightname != "OK" && rightname != "OKAY")
    {
        std::cout << "\nOh, I'm sorry, I'm a little deaf, must be my old age. So, what is your name?";
        GetInput(name);
        CheckName(name);
        VerifyName(name, rightname);
    }

    return 0;
}

void GetInput(std::string &input)
{
    do
    {
        std::cout << "\n-> ";
        getline (std::cin, input);
        input.erase( 0, input.find_first_not_of(" \t\f\v\n\r", 0 ) ); //Erase spaces from beginning
        input.erase( input.find_last_not_of(" \t\f\v\n\r") + 1 ); //Erase spaces from end

    } while ( input.empty() );
}

void CheckName(std::string &name)
{
    unsigned found;

    while (true)
    {
        found = name.find_first_not_of(" ABCDEFGHIJKLMNOPRSTUVWXYZabcdefghijklmonprstuvwxyz0123456789");

        if (found != std::string::npos)
        {
            std::cout << "The first invalid character is " << name[found];
            std::cout << " at position " << found << "\n\n";
        }

        else break;
        std::cout << "\nYour name contained invalid characters, please choose another name:";
        GetInput(name);
    }
}

void VerifyName(std::string &name, std::string &rightname)
{
    std::cout << "\n" << "Your name is " << name << ", right?";
    GetInput(rightname);

    std::transform( rightname.begin(), rightname.end(), rightname.begin(), ::toupper);
}
Last edited on
I updated the code above with the best I could come up with. I couldn't find a better way to make all letters uppercase in rightname, but I only need #include <algorithm> for the std::transform function, so I thought, maybe there's an alternative?

How can I change the name verification so it accepts spelling mistakes like "yep" instead of "yes" and so on, basically - if one letter is different, consider it as the same. This rule shouldn't apply to short words like "ok" and the letter "y", it should only be applied to longer ones (in this case "yes" and "okay")
Last edited on
Topic archived. No new replies allowed.
Pages: 12