Code:
template <typename T, typename Char, typename Traits, typename Validator>
bool read(std::basic_istream<Char, Traits>& rstream, T& t, Validator validator)
{
try {
std::basic_string<Char, Traits> buf;
std::getline(rstream, buf);
if(!rstream) return false;
t = boost::lexical_cast<T>(buf);
return validator(t);
} catch(boost::bad_lexical_cast&) {
return false;
}
}
template <typename T, typename Char, typename Traits, typename Alloc, typename Validator>
T ask(std::basic_ostream<Char, Traits>& wstream, std::basic_istream<Char, Traits>& rstream,
const std::basic_string<Char, Traits, Alloc>& question,
const std::basic_string<Char, Traits, Alloc>& failure, Validator validator)
{
T t;
while(wstream << question, !read(istream, t, validator)) {
wstream << failure;
}
return t;
}
template <typename T, typename Validator>
inline T ask(const std::string& question, const std::string& failure, Validator validator)
{
return ask(std::cout, std::cin, question, failure, validator);
}
template <typename T, typename Validator>
T ask(const std::wstring& question, const std::wstring& failure, Validator validator)
{
return ask(std::wcout, std::wcin, question, failure, validator);
}
// Example usage:
std::unordered_set<Beer> goodBeers{ottakringer, stiegl, hadmar};
Beer beer = ask<Beer>("Gimme a beer!\n", "That's'a not a good beer!\n",
[&](const Beer& beer) -> bool { return goodBeers.find(beer) != goodBeers.end(); });
It doesn't get any simpler, and I don't see a use for more flexibility or extensibility. If you really want, you can replace the getline-lexical_cast sequence with another functor, but I just don't see the point.