| 1 | // A small context defining the notion of an ordered otype.  (The standard | 
|---|
| 2 | // library should probably contain a context for this purpose.) | 
|---|
| 3 | context ordered(otype T) { | 
|---|
| 4 | int ?<?(T, T), ?<=?(T, T); | 
|---|
| 5 | }; | 
|---|
| 6 |  | 
|---|
| 7 | // A subrange otype resembling an Ada subotype with a base otype and a range | 
|---|
| 8 | // constraint. | 
|---|
| 9 | otype subrange(otype base_t | ordered(base_t), base_t low = 0, base_t high = 8) = base_t; | 
|---|
| 10 |  | 
|---|
| 11 | // Note that subrange() can be applied to floating-point and pointer otypes, not | 
|---|
| 12 | // just integral otypes. | 
|---|
| 13 | //   This requires a "otype generator" extension to Cforall.  Type generators | 
|---|
| 14 | // must accept otype and non-otype parameters, which is beyond what we discussed | 
|---|
| 15 | // previously.  Type parameters must be usable in the declaration of | 
|---|
| 16 | // subsequent parameters: parameter T is used to declare parameters "low" | 
|---|
| 17 | // and "high". | 
|---|
| 18 |  | 
|---|
| 19 | // Example usage: | 
|---|
| 20 | subrange(unsigned, 1, 31) day_of_month; | 
|---|
| 21 | subrange(char, 'a', 'z')  lcase; | 
|---|
| 22 | subrange(int, 0, (rand() & 0xF) ) foo; | 
|---|
| 23 |  | 
|---|
| 24 | // What sorts of expressions can be used as arguments of otype generators?  Is | 
|---|
| 25 | // "subrange(int, 0, rand() & 0xF)" legal?  Probably.  The nearest C equivalent | 
|---|
| 26 | // to the "low" and "high" arguments is the array size in a variable-length | 
|---|
| 27 | // array declaration, and C allows assignment expressions there. | 
|---|
| 28 |  | 
|---|
| 29 | // Convenient access to subrange bounds, for instance for iteration: | 
|---|
| 30 | forall (otype T, T low, T high) | 
|---|
| 31 | T lbound( subrange(T, low, high) v) { | 
|---|
| 32 | return low; | 
|---|
| 33 | } | 
|---|
| 34 |  | 
|---|
| 35 | forall (otype T, T low, T high) | 
|---|
| 36 | T hbound( subrange(T, low, high) v) { | 
|---|
| 37 | return high; | 
|---|
| 38 | } | 
|---|
| 39 |  | 
|---|
| 40 | // Example usage: | 
|---|
| 41 | unsigned lday = lbound(day_of_month); | 
|---|
| 42 |  | 
|---|
| 43 | // Assignment from the base otype, with bounds checking.  I'll ignore the issue | 
|---|
| 44 | // of exception handling here.  Inlining allows the compiler to eliminate | 
|---|
| 45 | // bounds checks. | 
|---|
| 46 | forall (otype T | ordered(T), T low, T high) | 
|---|
| 47 | inline subrange(T, low, high) ?=?(subrange(T, low, high)* target, T source) { | 
|---|
| 48 | if (low <= source && source <= high) *((T*)target) = source; | 
|---|
| 49 | else abort(); | 
|---|
| 50 | return target; | 
|---|
| 51 | } | 
|---|
| 52 |  | 
|---|
| 53 | // Assignment between subranges with a common base otype.  The bounds check | 
|---|
| 54 | // compares range bounds so that the compiler can optimize checks away when the | 
|---|
| 55 | // ranges are known to overlap. | 
|---|
| 56 | forall (otype T | ordered(T), T t_low, T t_high, T s_low, T s_high) | 
|---|
| 57 | inline subrange(T, t_low, t_high) ?=?(subrange(T, t_low, t_high)* target, | 
|---|
| 58 | subrange(T, s_low, s_high) source) { | 
|---|
| 59 | if ( (t_low <= s_low || t_low <= source) | 
|---|
| 60 | && (s_high <= t_high || source <= t_high) ) *((T*)target) = source; | 
|---|
| 61 | else abort(); | 
|---|
| 62 | return target; | 
|---|
| 63 | } | 
|---|