Peter said:
Out of curiosiy, could you give some few examples where Ada catches
faults not found by a C++ compiler. I assume - of course - code
written in modern C++: no casts, functions instead of macroes, a
limited use of pointers and so on.
Generally speaking, the very fact that you feel an urge to distinguish
between "C++" and "modern C++" is an indication that C++ is a poor
language containing many unsafe features, some of which you obligingly
enumerated above. By contrast, there is no distinction between "Ada"
and "modern Ada". Ada is safe by design, from the ground up.
Now for one specific example, I wrote a buffer overflow in a C++
library a few years ago, and it took me and two other people 3 days to
find it. The fix was, of course, trivial once the bug was found. As
it turned out, this particular bug would have been impossible to write
in Ada. I can't post the code, as it is proprietary and I don't have
it at hand anyway, but the gist of it is that, in Ada, loop variables
(a) are constants and (b) do not exist outside of the loop:
procedure Proc (A : in String) is
begin
for J in A'Range loop
J := J + 4; -- illegal, J is constant inside the loop
end loop;
Do_Womething_With (J); -- illegal, J no longer exists
end Proc;
Also notice that, in Ada, the "for" statement declares the loop
variable automatically.
The bug in the C++ library was that I was mistakenly reusing the loop
variable after the loop, instead of the intended variable. Of course,
the loop variable was an index pointing after the end of the buffer.
Some other features that make Ada inherently safer than C++ are:
* assignment is not an operator; it is an operation which does not
return a value. Thus, bugs like "if (something = 0)" cannot exist.
* case statements (Ada's equivalent of a switch in C++) are required
to handle all possible cases. Thus it is impossible to forget one.
And, of course, there is no "break;" crap in Ada.
* conditions cannot mix "and" and "or" without parentheses. Thus
there is no possibility that the programmer make wrong assumptions
about precedence of operators or order of evaluation.
* the type system, when used appropriately, makes it possible for the
compiler to find semantic errors in addition to just syntax errors.
For example, you can declare that Numers_Of_Apples and
Numers_Of_Oranges cannot be mixed. This is not possible with C++'s
typedef.
* conversions from floating point to integer types involve rounding.
The rounding is precisely and deterministically defined by the ISO
standard for the Ada language. Similarly, floating-point and
fixed-point types can be declared with known, deterministic,
guaranteed precision.
* pointer types cannot be converted to one another. You cannot
convert a pointer-to-String to a pointer-to-random-object.
* accessibility rules are rather complex, but they are designed to
minimise the chance of mistakes. Basically, the scope of a pointer
type must be included in the scope of the pointed-to type. This
makes many mistakes impossible, such as returning a pointer to an
object which no longer exists.
* when the compiler cannot check some code statically, it inserts
run-time checks which are guaranteed to catch all errors by raising
exceptions. In C++ you must code these checks by hand, and of
course at some point you'll forget one crucial check which will cost
you days in debugging.