If you want to convert a string to a number and verify that the entire string was numeric, you can use strtol
instead of atoi
. As an additional bonus, strtol
correctly checks for overflow and gives you the option of specifying whether or not you want hexadecimal/octal conversions.
Here's a simple implementation, with all the errors noted (printing error messages from a function like this is not a good idea; I just did it for compactness). A better option might be to return an error enum instead of the bool, but this function returns a std::pair<bool, int>
: either (false, <undefined>)
or (true, value)
:
std::pair<bool, int> safe_get_int(const char* s) {
char* endptr;
bool ok = false;
errno = 0; /* So we can check ERANGE later */
long val = strtol(s, &endptr, 10); /* Don't allow hex or octal. */
if (endptr == s) /* Includes the case where s is just whitespace */
std::cout << "You must specify some value." << '\n';
if (*endptr != '\0')
std::cout << "Argument must be an integer: " << s << '\n';
else if (val < 0)
std::cout << "Argument must not be negative: " << s << '\n';
else if (errno == ERANGE || val > std::numeric_limits<int>:max())
std::cout << "Argument is too large: " << s << '\n';
else
ok = true;
return std::make_pair(ok, ok ? int(val) : 0);
}
In general, philosophical terms, when you have an API like strtol
(or, for that matter, fopen
) which will check for errors and deny the request if an error occurs, it is better programming style to "try and then check the error return", than "attempt to predict an error and only try if it looks ok". The second strategy, "check before use", is plagued with bugs, including security vulnerabilities (not in this case, of course, but see TOCTOU for a discussion). It also doesn't really help you, because you will have to check for error returns anyway, in case your predictor was insufficiently precise.
Of course, you need to be confident that the API in question does not have undefined behaviour on bad input, so read the official documentation. In this case, atoi
does have UB on bad input, but strtol
does not. (atoi
: "If the value cannot be represented, the behavior is undefined."; contrast with strtol
)