Hi Robert, Robert Ramey wrote:
creating types which carry their ranges around with them through expressions.
I'm curious to know how well this works in practice, i.e. how much
of the time do you need run-time checking in real programs. Here
are a few examples that have occurred to me:
1. Arguments to functions
void f(safe<int> i)
{
int8_t j = i; // Needs a run-time check unless we know i<128
}
f(3); // Run-time check happens, even though value is known.
Even though the body of f is visible at the point of the call,
I don't think an approach based on types can avoid the run-time
check unless the function args are templates:
template <typename RANGE_TYPE>
void f(RANGE_TYPE i)
{
int8_t j = i;
}
f(3);
Here you can presumably arrange things so that the type of i inside
f includes the range information and the run-time check can be
avoided. But this does rely on the body of f being visible at the
point of the call. The verbosity of the function declaration should
improve a bit with concepts, but it's not really a drop-in replacement.
Related to this is function return types:
safe<int> incr(safe<int> i)
{
return i+1;
}
int8_t j = incr(42); // Run-time check or not?
Maybe auto return types help a bit but what about
return (i%2==0) ? i/2 : 3*i+1;
2. Merging constraints
Example:
void g(int j);
void f(safe<int> i)
{
for (int a = 0; a < 1000000; ++a) {
g(i+a);
}
}
Does that generate a million run-time checks? If my expectation
is that i will be "small", most likely I would be happy with just
one check at the start of f:
assert(i < maxint - 1000000);
3. Implied constraints:
vector<S> v;
f(v); // fills v
safe