R
REH
I have updated my C++ library. There is one class template in particular I
think you may find interesting. I had written an article on it that was in
review for (hopefully) publication with CUJ before their sudden demise. A
big thank you to Victor Bazarov for peer reviewing it for me. I am very
sorry to have wasted your time.
The class is "ranged_type" and is used to define range and overflow checked
integer types. If an attempt is made to construct or assign an out-of-range
value to a ranged_type, or if an arithmetic operation overflows, a member
function in it's traits type is called. The default behavior is to throw an
exception. Although the object itself is constrained to a given range,
intermediate operations are allowed to utilize the full scale of the integer
types available (except that signedness of the types must always be the
same).
The interesting part of the class is that it does static analysis using
templates to remove checks at compile-time that it determines are
unnecessary. This reduces the overhead occurred in doing the range and
overflow checks. This is good because all the checks are done with standard
C++ and can be very expensive.
For example:
typedef ranged_type<unsigned, 0, 100> R;
R x, y, z; // default initialized to the minimum of the range (in this case
0).
const R a = 5, b = 7, c = 1;
// sometime later;
R r = ((x + a) * (y + b)) / (z - c);
For this assignment all the checks are removed, except for the subtraction
(result may be negative), the division (must check for divide-by-zero) and
the final assignment to r. For the assignment, only the upper end of the
range is checked (remember that intermediate values may be full-scale). The
lower end is not checked because the minimum possible value (0) is within
the range [0,100].
In order to determine what kind of saving could be achieved, I defined a
test using the expression:
ax^2 + by^2 + cz^2
------------------
dw^2
The values of the constants a, b, c, d and the variables w, x, y, z were all
chosen so that the expression would never overflow. Three cases were done.
The first used unsigned int. The second used a range type defined similar
to the one above. The third was like the second, except the code was
compiled so that the checks were always done regardless of the possibility
of overflow. For each case, the expression was put an external function
which was executed several million times, and an average elapsed time for
one execution calculated. For this particular test, using my machine, and
my compiler (Visual C++ Toolkit 2003 (13.10.3077), optimization level O2)
the results were:
1st case: 0.0246 seconds
2nd case: 0.0297 seconds
3rd case: 0.1119 seconds
The results using GCC (MinGW 3.4.2, optimization level O3) were similar.
This was an admittedly contrived test, but I was interested in seeing what
the best case savings would be. Your mileage may vary. As can be seen,
when the class removes unnecessary checks, the elapsed time is must closer
to using actual primitive types, where as when the check are always done,
the elapsed time is about a magnitude larger.
I do not know how useful this class will be in real-world code, but I'm
interested in any comments you may have.
The latest code can always be found here:
http://www.richherrick.com/software/herrick_library.html
Regards,
REH
think you may find interesting. I had written an article on it that was in
review for (hopefully) publication with CUJ before their sudden demise. A
big thank you to Victor Bazarov for peer reviewing it for me. I am very
sorry to have wasted your time.
The class is "ranged_type" and is used to define range and overflow checked
integer types. If an attempt is made to construct or assign an out-of-range
value to a ranged_type, or if an arithmetic operation overflows, a member
function in it's traits type is called. The default behavior is to throw an
exception. Although the object itself is constrained to a given range,
intermediate operations are allowed to utilize the full scale of the integer
types available (except that signedness of the types must always be the
same).
The interesting part of the class is that it does static analysis using
templates to remove checks at compile-time that it determines are
unnecessary. This reduces the overhead occurred in doing the range and
overflow checks. This is good because all the checks are done with standard
C++ and can be very expensive.
For example:
typedef ranged_type<unsigned, 0, 100> R;
R x, y, z; // default initialized to the minimum of the range (in this case
0).
const R a = 5, b = 7, c = 1;
// sometime later;
R r = ((x + a) * (y + b)) / (z - c);
For this assignment all the checks are removed, except for the subtraction
(result may be negative), the division (must check for divide-by-zero) and
the final assignment to r. For the assignment, only the upper end of the
range is checked (remember that intermediate values may be full-scale). The
lower end is not checked because the minimum possible value (0) is within
the range [0,100].
In order to determine what kind of saving could be achieved, I defined a
test using the expression:
ax^2 + by^2 + cz^2
------------------
dw^2
The values of the constants a, b, c, d and the variables w, x, y, z were all
chosen so that the expression would never overflow. Three cases were done.
The first used unsigned int. The second used a range type defined similar
to the one above. The third was like the second, except the code was
compiled so that the checks were always done regardless of the possibility
of overflow. For each case, the expression was put an external function
which was executed several million times, and an average elapsed time for
one execution calculated. For this particular test, using my machine, and
my compiler (Visual C++ Toolkit 2003 (13.10.3077), optimization level O2)
the results were:
1st case: 0.0246 seconds
2nd case: 0.0297 seconds
3rd case: 0.1119 seconds
The results using GCC (MinGW 3.4.2, optimization level O3) were similar.
This was an admittedly contrived test, but I was interested in seeing what
the best case savings would be. Your mileage may vary. As can be seen,
when the class removes unnecessary checks, the elapsed time is must closer
to using actual primitive types, where as when the check are always done,
the elapsed time is about a magnitude larger.
I do not know how useful this class will be in real-world code, but I'm
interested in any comments you may have.
The latest code can always be found here:
http://www.richherrick.com/software/herrick_library.html
Regards,
REH