This example of template foo has a value parameter that is specialized for 10:
template foo(U : int, int T : 10)
{
U x = T;
}
void main()
{
assert(instance foo(int, 10).x == 10);
}
Specialization
Templates may be specialized for particular types of arguments by following the template parameter identifier with a : and the specialized type. For example:
template TFoo(T) { ... } // #1
template TFoo(T : T[]) { ... } // #2
template TFoo(T : char) { ... } // #3
template TFoo(T,U,V) { ... } // #4
instance TFoo(int) foo1; // instantiates #1
instance TFoo(double[]) foo2; // instantiates #2 with T being double
instance TFoo(char) foo3; // instantiates #3
instance TFoo(char, int) fooe; // error, number of arguments mismatch
instance TFoo(char, int, int) foo4; // instantiates #4
The template picked to instantiate is the one that is most specialized that fits the types of the TemplateArgumentList. Determine which is more specialized is done the same way as the C++ partial ordering rules. If the result is ambiguous, it is an error.
Limitations
Templates cannot be used to add non-static members or functions to classes. For example:
class Foo
{
template TBar(T)
{
T xx; // Error
int func(T) { ... } // Error
static T yy; // Ok
static int func(T t, int y) { ... } // Ok
}
}
Templates cannot be declared inside functions.
Contracts
Contracts are a breakthrough technique to reduce the programming effort for large projects. Contracts are the concept of preconditions, postconditions, errors, and invariants. Contracts can be done in C++ without modification to the language, but the result is clumsy and inconsistent.
Building contract support into the language makes for:
-
a consistent look and feel for the contracts
-
tool support
-
it's possible the compiler can generate better code using information gathered from the contracts
-
easier management and enforcement of contracts
-
handling of contract inheritance
The idea of a contract is simple - it's just an expression that must evaluate to true. If it does not, the contract is broken, and by definition, the program has a bug in it. Contracts form part of the specification for a program, moving it from the documentation to the code itself. And as every programmer knows, documentation tends to be incomplete, out of date, wrong, or non-existent. Moving the contracts into the code makes them verifiable against the program.
Assert Contract
The most basic contract is the assert. An assert inserts a checkable expression into the code, and that expression must evaluate to true:
assert(expression);
C programmers will find it familiar. Unlike C, however, an assert in function bodies works by throwing an AssertException, which can be caught and handled. Catching the contract violation is useful when the code must deal with errant uses by other code, when it must be failure proof, and as a useful tool for debugging.
The pre contracts specify the preconditions before a statement is executed. The most typical use of this would be in validating the parameters to a function. The post contracts validate the result of the statement. The most typical use of this would be in validating the return value of a function and of any side effects it has. The syntax is:
in
{
...contract preconditions...
}
out (result)
{
...contract postconditions...
}
body
{
...code...
}
By definition, if a pre contract fails, then the body received bad parameters. An InException is thrown. If a post contract fails, then there is a bug in the body. An OutException is thrown.
Either the in or the out clause can be omitted. If the out clause is for a function body, the variable result is declared and assigned the return value of the function. For example, let's implement a square root function:
long square_root(long x)
in
{
assert(x >= 0);
}
out (result)
{
assert((result * result) == x);
}
body
{
return math.sqrt(x);
}
The assert's in the in and out bodies are called contracts. Any other D statement or expression is allowed in the bodies, but it is important to ensure that the code has no side effects, and that the release version of the code will not depend on any effects of the code. For a release build of the code, the in and out code is not inserted.
If the function returns a void, there is no result, and so there can be no result declaration in the out clause. In that case, use:
void func()
out
{
...contracts...
}
body
{
...
}
In an out statement, result is initialized and set to the return value of the function.
The compiler can be adjusted to verify that every in and inout parameter is referenced in the in { }, and every out and inout parameter is referenced in the out { }.
The in-out statement can also be used inside a function, for example, it can be used to check the results of a loop:
in
{
assert(j == 0);
}
out
{
assert(j == 10);
}
body
{
for (i = 0; i < 10; i++)
j++;
}
This is not implemented at this time.
Share with your friends: |