From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,25b9eb5c3a89bced X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news2.google.com!news3.google.com!border1.nntp.dca.giganews.com!border2.nntp.dca.giganews.com!nntp.giganews.com!news.maxwell.syr.edu!news-rtr.nyroc.rr.com!news-out.nyroc.rr.com!twister.nyroc.rr.com.POSTED!53ab2750!not-for-mail From: "REH" Newsgroups: comp.lang.ada References: <1145852356.559455.222600@i39g2000cwa.googlegroups.com> <1145855124.720029.35280@t31g2000cwb.googlegroups.com> <1235818.stAph9vF03@linux1.krischik.com> <1146207623.635562.289570@i39g2000cwa.googlegroups.com> <1201812.r16X2gYOJE@linux1.krischik.com> Subject: Re: where exactly c++,c fail and Ada gets thru' X-Priority: 3 X-MSMail-Priority: Normal X-Newsreader: Microsoft Outlook Express 6.00.2900.2670 X-RFC2646: Format=Flowed; Original X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2670 Message-ID: Date: Sat, 29 Apr 2006 14:08:18 GMT NNTP-Posting-Host: 69.205.134.80 X-Complaints-To: abuse@rr.com X-Trace: twister.nyroc.rr.com 1146319698 69.205.134.80 (Sat, 29 Apr 2006 10:08:18 EDT) NNTP-Posting-Date: Sat, 29 Apr 2006 10:08:18 EDT Organization: Road Runner Xref: g2news2.google.com comp.lang.ada:3998 Date: 2006-04-29T14:08:18+00:00 List-Id: "Martin Krischik" wrote in message news:1201812.r16X2gYOJE@linux1.krischik.com... > Now I do wonder if REH managed some template/boolean magic to get the same > result on C++. > The simplistic answer is for each ranged type R, there is an intermediate type I. I has the same properties as R, except it is allowed to "grow." That is, as the results of partial expressions become large, I chooses an integer type with a larger range (sadly, my technique will not work with floating point types). If I is not able to find a type with a larger enough range, it is flagged has having the possibility of overflowing. The math is basically done by partial specializing on whether overflow is possible. The tricky part is determining whether an overflow can occur. For this I took a page from my COBOL days. If you performed an operation on two values and tried to shove it into a variable without enough digits, COBOL would warning you. For example, multiplying two two-digit values and trying to assign it to anything less than a 4-digit variable can overflow. Of course, it is trivial to determine this for every algebraic operator. So, for each operation, we calculate the maximum number of digits needed for the result and try to use a type with that ranged. The maximum digits is kept in I. This allows intermediate values to exceed the bounds of the ranged type (like doing the math using R'Base). When a type I is assigned to type R, it is ranged check if necessary. If the lower or upper bounds of I is within the bounds of R, that particular check is also removed. This is all done at compile time. As an example, assume R is an unsigned type with a ranged of [0, 100]. The number of binary digits necessary to hold R is 7. So, if x and y are variables of type R, then x * y needs at most 14 digits. On any system, unsigned int can hold a 14-digit number. No overflow can happen, so no check is done. The result is of type I. I is a class temple whose template parameters hold information needed at compile-time. I "contains" type unsigned int, and 14 for the digits. I also "knows" it's base type (R), so is able to keep types unique. R can be instantiated with or without explicit construction (defines whether or not conversion to R requires an explicit cast), and with or without conversion operators (defines whether R can be implicitly converted to other types). R can bound the largest type allowed in its calculations. For example, assume we are on a system where int is 16 bits and long is 32 bits. If we take the resulting value of type I above and do I * I, this needs a 28-digit type. Normally, the resulting I type would use unsigned long. R can be bounded to stop at 16-bits if, for some reason you do not want to use type long. Thus, instead of growing, the new I would be flagged and the operation would be checked for overflow. Now, you may ask why I didn't just define I with the resulting range of [0, 1000]. The answer is that it would be a lot nastier because then I would also have to do overflow checking on the ranges at compile-time. It would be really nasty for signed types because you cannot just do the operation and check for overflow. Allowing a signed operation to overflow is undefined behavior. The above technique works for both signed and unsigned types, and all three integer representations allowed by the C++ standard. I'd like to enhance it to handle floating points, but I currently have no clue how that could be done. I'll admit the templates are very, very complex, but for the most part the client software does not have to worry about it. You can just use you would any other numerical type. I don't recall having any issue with "error messages" while writing it. Modern C++ compiler have gotten very good at giving more sane messages. They have also gotten better at compile times. My ranged type is not perfect, and there is no way to perfectly replicate what Ada can do, but as I said I cannot always use Ada. I also realize that the class will never be able to remove as many checks as an Ada compiler, but it is much better than nothing. The checks are very expensive since they are done in standard C++ (though one could sacrifice portability for speed if their compiler supported inline assembly). On benchmarks I've run, the class can be very close to raw integers in terms of speed, where not eliminating any checks can cost 10x or more. Of course this all depends on your system, compiler, etc., and the particular expressions (what the class can catch). In particular, very complex expressions will saturate the range of the types available quickly and inhibit anymore optimizations. Breaking the expression up can help to alleviate this, but each time an I is assigned to an R, a range check may have to be done (those range checking is much, much more efficient than overflow checking). REH