Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check user input for errors elegantly

My program waits for user input, and when appropriate, will process it. I need to check the user input to make sure it fulfils certain criteria, and if it doesn't fulfil all of those criteria it will be rejected.

Pseudo-code is something like:

if (fulfills_condition_1)
{
    if (fulfills_condition_2)
    {
        if (fulfills_condition_3)
        {
            /*process message*/
        }
        else
            cout << error_message_3; //where error_message_1 is a string detailing error
    }
    else
        cout << error_message_2; //where error_message_2 is a string detailing error
}
else
    cout << error_message_1; //where error_message_3 is a string detailing error

There is the possibility that the number of these conditions could increase, and I was wondering if there was a neater way to represent this using a switch or something like that instead of lots of cascading if statements.

I know there is the possibility of using

if (fulfills_condition_1 && fulfills_condition_2 && fulfills_condition_3)
    /*process message*/
else
    error_message; //"this message is not formatted properly"

but this is less useful than the first, and does not say where the issue is.

The conditions can roughly be arranged in increasing importance i.e. checking for condition_1 is more important than checking for condition_3, so the if statements do work - but is there a better way in general for doing this?

like image 420
sccs Avatar asked Mar 14 '13 08:03

sccs


4 Answers

How about

if (!fulfills_condition_1) throw BadInput(error_message_1);
if (!fulfills_condition_2) throw BadInput(error_message_2);
if (!fulfills_condition_3) throw BadInput(error_message_3);

/* process message */

Then your exception handler can report the error message, and retry or abort as appropriate.

like image 169
Mike Seymour Avatar answered Nov 02 '22 05:11

Mike Seymour


If what bothers you are the cascading ifs, you could go for one of the following:

Using a boolean:

bool is_valid = true;
string error = "";
if (!condition_one) {
  error = "my error";
  is_valid = false;
}

if (is_valid && !condition_two) {
  ...
}

...

if (!is_valid) {
  cout << error;
} else {
  // Do something with valid input
}

Using exceptions:

try {
  if (!condition_one) {
    throw runtime_error("my error");
  }

  if (!condition_two) {
    ...
  }

  ...

} catch (...) {
  // Handle your exception here
}
like image 26
alestanis Avatar answered Nov 02 '22 03:11

alestanis


I suggest you can use "early return" technique:

  if (!fulfills_condition_1)
     // error msg here.
     return;

  // fulfills_condition1 holds here.

  if (!fulfills_condition_2)
     // error msg here.
     return;

  // Both conditon1 and condition2 hold here.

  if (!fulfills_condition_3)
     // error msg here.
     return.
like image 29
Zhi Wang Avatar answered Nov 02 '22 04:11

Zhi Wang


If this was going to be reused in a few places, I would make a DSL:

Validator inputType1Validator =
    Validator.should(fulfill_condition_1, error_message_1)
             .and(fulfill_condition_2, error_message_2)
             .and(fulfill_condition_3, error_message_3)

inputType1Validator.check(input);
like image 24
Peter Wood Avatar answered Nov 02 '22 04:11

Peter Wood