I'm writing this app that's based in the terminal, and of course there's a lot of input involved, especially numerical input. I've decided to use the method of reading an entire line of input and converting into a number and displaying an error message if the input is out of bounds or just straight-up invalid. This happens inside and infinite loop so that the user can keep trying until they get it right.
Having to do this so many times in my code has resulted in me having to move it to function in a namespace like so:
io_helpers.h
#ifndef IO_HELPERS_H
#define IO_HELPERS_H
#include <string>
namespace easyio
{
bool fileExists(const std::string& filename);
int readInt(const std::string& prompt, int min, int max, const std::string& errorMsg);
}
#endif
io_helpers.cpp
#include "io_helpers.h"
#include <fstream>
#include <iostream>
using namespace std;
bool easyio::fileExists(const string& filename)
{
ifstream fin(filename);
if (fin.is_open())
{
return true;
}
else
{
return false;
}
}
int easyio::readInt(const string& prompt, int min, int max, const string& errorMsg = "invalid input!")
{
while (true)
{
try
{
string input;
cout << prompt << ": ";
getline(cin >> ws, input);
int number = stoi(input);
if (number < min || number > max)
throw exception();
return number;
}
catch (exception& e)
{
cout << errorMsg << endl;
}
}
}
Is this method of input the best method though? Is there anything I can do to improve the user experience or the performance? And on a side-note, should this be a static method in a class or a function in a namespace, or does it not matter?
fileExists
outside the namespace? In addition, you might as well passconst std::string&
(like you do in the other function). Third, you're not returning anything in thecatch
clause. Perhaps you want to throw the exception to the caller function? To me it looks like there's no need to "get smart around" with those exceptions there. Simply passint errorVal
to the function, and return it in case you scan an input value out of range. \$\endgroup\$fileExists
in the namespace, as would passing aconst std::string&
... chalk that up to absent-mindedness. As for returning from thecatch
clause, I don't need to, because it will just loop back into thetry
clause if an exception is thrown (unless I'm wrong in how exceptions work, which I could be; I don't use them often). \$\endgroup\$while (true)
. \$\endgroup\$