1

I've a program that accepts a user's input, checks if the input is empty, and also if it matches up to a vector of strings (as it's a username-style program).

However, how would I go about cancelling the checking of the string to see if it matches one in the vector if the command was say, /help, or /commands? Naturally, if someone enters one of those commands, I'd like it not to check if it's a username, but display the help topics etc.

I was thinking about using a break, but I'd like some help as I am quite inexperienced with C++. Here's the relevant part of my code so far:

vector <string>  unamevec(1,"Administrator"); //Declare vector.

bool isValidUserName(const string& input) { //Check username is in vector
  for(int i = 0; i < 1; ++i) {
      if(input == unamevec[i]) {
          return true;
      }
  }
  return false;
}

int main() {

string userinput;
string accesslevel;

while (userinput.empty()){ //Check for empty input

    cout << "Please enter your identity.\n"; //Identify user
    getline(cin, userinput);

    if (!isValidUserName(userinput)) {

        do //Check to see if user is the same as the vector "unamevec"
        {
            cout << "This user is either non existent or has access privileges revoked.\n"; //Wrong username!
            cout << "Please re-enter the username.\n";
            getline(cin, userinput);
        }
    while (!isValidUserName(userinput));
    }
}
Talisman
  • 388
  • 4
  • 19
  • 1
    I would start by organizing your code so you are only getting input once (e.g. just a single call to `getline` at the top). Then everything else may become clearer and fall into place. Input => Process (its a command, its a user, or its invalid with an error displayed) => Repeat. – Jason C Jul 19 '14 at 11:37
  • Is `/help` a possible user name? (This is important, because if it is, you need to compare input to usernames first.) – Jongware Jul 19 '14 at 11:44
  • @Jongware Another option is to prohibit usernames that start with `/` so that a username cannot be created that blocks access to a command, or check commands first (and prefer to block out the user on conflict). – Jason C Jul 19 '14 at 11:50
  • Ideally /help wouldn't be a possible username. Jason, I'm not entirely sure how I could do that as getline is used here twice to make the username check valid. See http://stackoverflow.com/questions/24838365/checking-a-string-matches-any-string-in-a-string-array – Talisman Jul 19 '14 at 11:50
  • @HarryLudlow [This](http://pastebin.com/RzptJvBJ) or some variation thereof (e.g. you could flatten out those nested if's if you prefer, I just wanted to clarify that command handling was a distinct separate branch than user handling), although I was hoping you'd work it out, as the challenge is good learning! – Jason C Jul 19 '14 at 11:57
  • By the way you probably want `i < unamevec.size()` rather than `i < 1` in your `isValidUserName`; or just use [`std::find`](http://stackoverflow.com/questions/571394/how-to-find-an-item-in-a-stdvector). – Jason C Jul 19 '14 at 12:00
  • Thanks for the tips. What is the quit variable actually for, though? :s – Talisman Jul 19 '14 at 12:09
  • Use an existing command line options parsing library, like Boost up – Manu343726 Jul 19 '14 at 13:17
  • @HarryLudlow For you to set to true when you want to quit. Maybe you have a quit command or something. Whatever you want. – Jason C Jul 19 '14 at 20:21

1 Answers1

2

There's nothing wrong with using break to solve a problem. But in terms of "top level structure", I'll start on a point...that you are not checking the input stream status for end-of-file or error conditions.

Consider what might happen if your standard input is coming piped from a file instead of a terminal, and you hit the end of that file. No more input is coming...including no /quit command (or whatever). How would your loop terminate? Similarly for the file-as-input scenario, what if it was on a networked filesystem that suddenly disconnected?

(Note: I'd suggest giving getline and error handling a read-through, as an accessible article about the details. One often-convenient detail is that getline returns the stream object you passed in...and streams can behave like a boolean false under an if when there's an error or end-of-file. If you don't need to prompt-before-getting each time through a loop, that makes for usefully controlling the loop with the result of getline itself.)

Anyway, to get the processing you're looking for...you might try something like:

while (true) {
    cout << "Please enter the username.\n";

    if (not getline(cin, userinput)) {
        // eofbit, failbit, or badbit
        break;
    }

    if (isValidUserName(userinput)) {
        // do whatever you do with valid user name input
    }
    else if (isValidCommand(userinput)) {
        // do whatever, and break if you have reason to leave the loop
        // (such as a /quit command)
    }
    else {
        cout << "Not a valid user or command.\n";
    }
}

// Only in case of set badbit we are sure that errno has been set in
// the current context. Use perror() to print error details.
if (cin.bad())
    perror("error while reading from standard input");

// decide if cin.eof() represents an error or not, and handle it if so

(Note: You don't have to use not instead of !, I just find it more readable if one is learning C++ from the get-go on code that will never be C compatible. I also think or is more readable than || and and is more readable than &&. They're keywords and you can't use them as variable names so...might as well use them for something. :P My opinion.)

As has been pointed out, if you're going to use an input strategy like this then user names can't start with '/'. Design-wise, there's lots to think about...but as a learning exercise I'll say that's all right.

Also pointed out: you probably want to use std::find. Many collections allow for visiting of all the elements without needing to be able to be indexed by integer. And some can be indexed by integer, but can do lookup faster if you don't make them.

  • Thanks a lot; that was what I was trying to get at. As for my design strategy, I do not need their usernames to begin with '/' as I will be assigning usernames of fixed syntax (think: office-network-style-names). I am intrigued though as to your suggestion of using std::find? Would that replace my if functions, in essence? – Talisman Jul 19 '14 at 12:55
  • @HarryLudlow To understand what "Modern C++" can do, you need to be careful what you read...lots of outdated sites and tutorials. The upshot is that you not only have reusable containers for data, but you have [reusable *algorithms*](http://en.cppreference.com/w/cpp/algorithm) which have been written for you. And yes; something like `std::find` removes the need for you to manually loop and compare; it knows if looping is right or if there is a better lookup for the collection (e.g. it may be sorted and binary searchable). Try some `#include ` samples out and see how they work! – HostileFork says dont trust SE Jul 19 '14 at 13:22