Just for perfectionism and for sport, I'm looking for a way to correctly read (numerically) into ANY integer variable, under ANY circumstances and on ANY platform that supports C++, as per the C++ Standard, and not as per some de-facto standards.
Of course, problems start as soon as this variable starts to be of a char type. If so, cin will write into the variable the ASCII code of the first character from standard input, rather than the numbers themselves. We might run into these problems for example when we start using typedefs from <cstdint>. I found out that int_least8_t, for example, is very likely to bind to char (if not even guaranteed). Also, AFAIK there is at least a theoretical chance that, for example, uint_fast32_t binds to char32_t. I'd like my program to handle such cases properly.
Therefore I have to typecast my integer variable to a type that will be guaranteed not to have such problems. The most general solution seems to be to use the unary + operator; AFAIK it is guaranteed to typecast a char variable into an integer variable of at least as much size, and will do nothing with true integer variables. So, this is better than an explicit typecast, which would require some more work. Unfortunately, since I'm not allowed to read into a temporal, I'll have to define a helper variable:
1 2 3
|
auto hlp = +var;
cin >> hlp;
var = hlp;
|
This is ugly and adds the overhead of useless copying. Is there any way make this better, without the overhead? I'd like to know it.
But that's not the end of problems. Not near. The unary + actually might convert the variable to a broader type. For example, applying it to a usual char is likely (or guaranteed) to produce an int, which is very likely (although not guaranteed) to be broader than a char. Normally cin does range checking, and if input exceeds the variable's range, it sets up ios::failbit. Not here, however. cin will write to hlp, which is an int and has limits of an int. So an integer overflow might happen when var is being assigned the value of hlp.
To prevent this we have to do range checking by hand. This produces a horrible code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
auto hlp = +var;
cin >> hlp;
auto maxval = numeric_limits<decltype(var)>::max();
auto minval = numeric_limits<decltype(var)>::lowest();
if(!cin.fail() && (hlp > maxval || hlp < minval))
{
cin.setstate(ios::failbit);
if(hlp > maxval)
var = maxval;
else
var = minval;
}
else
var = hlp;
|
And this is not only ugly, but also broken (from my perfectionistic point of view). The problem is, I wanted to make sure the program behaves well for all cstdint typedefs. But here we have the use of numeric_limits, which may only be specified for fundamental types; and cstdint typedefs are not guaranteed to bind to fundamental types (AFAIK).
Is there any better way to do this? My only other idea is to check whether var is of any of these types: char, unsigned char, wchar_t, char16_t or char32_t, and if so, perform the promotion with the unary + and do range checking with numeric_limits which is guaranteed to work properly since numeric_limits is specified for these types. But then again, are we guaranteed not to have any other implementation defined char types? If we are not, (which I'm afraid is the case), then even this idea breaks.
And what about the overhead? Is it going to be horrible? How to make it more efficient?
Still looking for a neat solution ;P