April 17
Automatic checking numeric range
I was recently involved in a discussion about the lack of “ranged ints” in the C# language. A ranged int is an integer type that would only accept a smaller subrange of all possible integers, say just 1 to 100. This is a discussion dear to me be because I sort of designed “ranged ints” for C++.
It all started because I used to develop with Turbo Pascal which *had* “ranged” integer types. You could turn on a compiler option that would automatically validate the range at runtime. I used it as a debugging aid, like an “automatic assert” every time the variable was assigned to. For instance, I would have a “row” variable that must hold values between 1 and 24, so I would declare it to be of a “row type”, a ranged integer of 1 to 24. During development and testing I would get an error if it got assigned something outside of that range. The final “non-checked” version would have this option turned off and have no performance penalties.
Then I had to switch to C++ and I missed my “ranged ints”. Since C++ templates allowed not only types but constants as arguments as well, I developed my own “ranged int” classes based on templates. At debug time they would check the range. I then flipped a compiler “define” and they would behave pretty much like a normal int, semantically and performance wise. This was not a popular C++ technique, but I used it left and right, despite that it made for slower compilations and at one point the Borland C++ compiler would choke with so much template usage.
I dusted off my C++ skills and wrote such an example as a console Win32 app below – this one is pretty simple (no copy constructor, no necessary operator overloading) and it will actually coerce the value to be within the range instead of issuing a warning, but you can get the idea. Of course, you could do the same with other numeric types such as float.
I couldn’t replicate the technique with C#. Maybe if it would accept constants as generics arguments, I could have my “ranged ints” back…
Here is the sample:
#include "stdafx.h"
#define DEBUG
template <int V1, int V2> class RangeInt {
private:
int Value;
#ifdef DEBUG
// Checks range and force into the range if outside
// An alternative would be to throw an exception or flag the error somehow
void CheckRange() {
if (Value < V1) {
Value = V1;
}
if (Value > V2) {
Value = V2;
}
}
#endif
public:
RangeInt() {
Value = V1;
}
RangeInt(int N) {
Value = N;
#ifdef DEBUG
CheckRange();
#endif
}
operator int() {
return Value;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
RangeInt<1, 100> MyVal;
MyVal = 0;
printf("%d\n", MyVal);
MyVal = 1000;
printf("%d\n", MyVal);
return 0;
}