Parametrized types such as C++ templates are a nice thing, but most of the time they can only be parametrized by other types.
However there is a special case in C++ where it is possible to parametrize a template by an integer. For example, fixed-length arrays are a typical use case:
template<typename T, int SIZE> class FixedArray
{
T m_values[SIZE];
public:
int getElementCount() const { return SIZE; }
T operator[] (int i) const {
if (i<0 || i>=SIZE)
throw;
else
return m_values[i];
}
};
void f()
{
FixedArray<float, 5> float_array; // Declares a fixed array of 5 floats.
//The size is known at compile time, and values are allocated on the stack.
}
Only constant integers and pointers are allowed in C++, but I think it could be interesting to use any value for parametrization (floats, class instances, etc.). This could allow expressing preconditions at compile-time (usually informally specified in the documentation), and check them automatically at runtime. For example, here is an "Interval" template in an hypothetical C++ dialect :
// Interval of type T between TMin and TMax.
template<typename T, T TMin, T TMax> class Interval
{
T m_value;
public:
Interval(int val) { *this = val; }
Interval& operator = (T val) {
//check that 'val is in [TMin, TMax] and set the value if so, throw error if not
if (val < TMin || val > TMax)
throw;
else
m_value = val;
return *this;
};
operator T() const { return m_value; }
}
// Now define a f function which is only allowing a float between O and 1
// Usually, that kind of function is taking a float parameter with the doc saying "the float is in 0-1 range". But with our Interval template we have
// that constraint expressed in the type directly.
float f(Interval<float, 0, 1> bounded_value)
{
// No need to check for boundaries here, since the template asserts at compile-time that the interval is valid
return ...;
}
// Example usage
void main();
{
float val = 0.5;
Interval<float, 0, 1> sample_interval = val; // OK. The boundary check is done once at runtime.
f(sample_interval); // OK. No check, because it is asserted by the type constraint at compile-time.
// This can prevent endless precondition testing within an algorithm always using the same interval
sample_interval = 3*val; // Exception at runtime because the constraint is no longer satisfied
f(sample_interval); // If I am here, I am sure that the interval is valid. But it is not the case in that example.
}
What could be interesting then would be to express relationships between these types. For example, expressing the rule to assign an Interval A to another Interval B with other boundaries, or simply a rule to assign a value to an Interval, with everything checked at compile-time.
Is there any language with this kind of parametrization (or a similar approach), or one must still be invented? Any useful research papers?