/* * Safe input example: * If your compiler is not up to date for C++ 11, some of this stuff might not work for you. * If that's the case, try just the topmost example - this should work. */ #include template void SafeInput(StreamIn & Object, std:: istream & InputStream = std:: cin) { for ( ;; ) { if (InputStream >> Object) return; else { InputStream.clear(); InputStream.ignore(std:: numeric_limits:: max(), '\n'); std:: cerr << "Invalid input: try again.\n"; } } } /* * As complicated as it looks, it isn't that bad to use. * You simply call the function as you would any other, but you specify the type of the first * parameter in angle brackets - look, here's an example: */ int main(int, char **) { int A = 0; std:: cout << "The current value of 'A' is: " << A; std:: cout << "\nInput the new value of 'A': "; SafeInput (A); std:: cout << "The new value of 'A' is " << A; return 0L; } /* * Likewise, if 'A' was a 'char' instead, you would call 'SafeInput(A);', * and the same for any type that you can get input for with std:: cin. */ /* * That's not too bad- copy all the above and compile to try it out yourself. * You should not be able to crash your program with input, and it should rather reset and prompt * the user again. * * What happens is this: * 1: Prompt the user for input. * 2: If the fail bit of std:: cin was not set, (called InputStream in this function), * break and return. * 3: Else, reset the stream of all errors, and ignore. * 4: Print an error message, and repeat. * * But we can make it better. * This works great for some basic stuff, but what if you have some sort of restriction * on your input, as is commonly the case? * We can use a neat function of the new C++11 standard, called lambdas to supply a predicate * to test whether the input is okay, inline. * See: http://en.cppreference.com/w/cpp/language/lambda * * Note: Syntax might get ugly here, don't worry. Even when we're done, it's still shorter * than the alternative, which is typing out the entire function SafeInput inline with the * predicate enclosed. */ /* * We need to add another include this time - this will offer us a bit more flexibility in how we * use this feature. */ #include #include template void SafeInput(StreamIn & Object, std:: function Predicate = nullptr, std:: istream & InputStream = std:: cin) { for ( ;; ) { if (InputStream >> Object) { if (Predicate == nullptr) { return; } else if(Predicate(Object) ) { return; } else { InputStream.clear(); InputStream.ignore(std:: numeric_limits:: max(), '\n'); std:: cerr << "Invalid input: try again.\n"; } } } int main(int, char **) { int A = 0; std:: cout << "The current value of 'A' is: " << A; std:: cout << "\nInput the new value of 'A': "; SafeInput (A, [](const int A){ return (A <= 100 && A >= 0)? true: false; } ); std:: cout << "The new value of 'A' is " << A; return 0L; } /* * In this case, we demonstrate the power of such a construct by letting us constrain input to the * values in the interval [0, 100]. * You can alter the predicate function to evaluate anything, as long as the value returned * is boolean. If you need to read external state, you can do that. mention the name in the lambda * and capture by value: * int y = 25; * SafeInput (a, [=](const int a) { * return A <= y? true: false; * } ); */ /* * Don't be intimidated by the syntax, especially if you've never seen these features before. * The function accomplishes something rather trivial: get input and make sure that it meets some * restriction. * It works like this: * * 1: Get the type of the data we want to input: * SafeInput * 2: Get the datum that we actually want to change * SafeInput(Value, * 3: Get a pointer to a function, functor or a "lambda function", since std:: function lets us * use any, that returns a boolean and takes the type of the data we want to change as an argument. * SafeInput(Value, [](const int SomeName){ return true } ); * 4: And close the function. The predicate (in this case always true) will be evaluated on each * input, if it's provided. Since this one is constantly true, we could have just left it out. * SafeInput(Value); * 5: Test if input is okay - the stream is okay. Assuming it is, if and only if the predicate * was supplied, evaluate that predicate and filer input based on that. If the predicate returns * false, than prompt the user again. * 6: If the stream isn't okay, prompt the user again. */ /* * You should be able to copy- paste this entire file into your compiler in two parts. * As ugly as this is, it's frankly more concise, readable, and safer than trying to validate * input inline. If you can't compile the second one, than trust me, it's your compiler! * Tested on GNU GCC 4.8.1, and the test version of the "Microsoft Visual C++ Compiler Nov 2012 CTP" * Feel free to use this subject to the terms of the Do What The Fuck You Want to Public License. http://www.wtfpl.net/ * Cheers. */