1 | // A small context defining the notion of an ordered otype. (The standard
|
---|
2 | // library should probably contain a context for this purpose.)
|
---|
3 | trait 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 | }
|
---|