Peter Gerwinski wrote:
here are three new sections for the "programmer's guide to GPC" (see `info -f gpc -n programming'). Comments/improvements/etc. welcome.
OK, here they are. :-)
@node Schemata [...] Schemata are types that depend on a variable, called ``discriminant''.
<nitpick> There can be more than one discriminant. </nitpick>
@node Pointer arithmetics [...] @smallexample Var A: array [ 1..7 ] of Char; p, q: ^Char; i: Integer;
[...]
for p:= @@A [ 1 ] to @@A [ 7 ] do p^:= 'x';
q:= @@A [ 3 ]; while p > q do
This is a bad example, IMHO, since p is defined as being undefined after a for loop, AFAIK -- even if it might actually have the correct value here.
begin p^:= 'y'; dec ( p ); end (* while *);
i:= q - p;
This would, after the above while loop, always be 0, and is therefore not very instructive, I think.
@node Type casts
This section does not clearly draw the distinction between value type casting and variable type casting. So I'll try to write an improvement of this section (it got a little bit longer ;-):
@c ============================================================================
@node Type casts @section Type Casts @cindex type casts
In some cases, especially when interfacing with other languages, Pascal's strong typing can be an obstacle. To temporarily circumvent this, GPC (and other Pascal compilers) defines explicit ``type casts''.
There are two kinds of type casts, value type casts and variable type casts.
Value type casts
To convert a value of one data type into another type, you can use the target type like the name of a function that is called. The value to be converted can be a variable or an expression.
An example:
@smallexample Var Ch: Char; i: Integer;
[...]
i := Integer (Ch); @end smallexample
Another, more complicated, example:
@smallexample Type CharPtr = ^Char; CharArray = array [ 0..99 ] of Char; CharArrayPtr = ^CharArray;
Var Foo1, Foo2: CharPtr; Bar: CharArrayPtr;
[...]
@{$X+@} @{ We need extended syntax in order to use ``Succ'' on a pointer @}
Foo1 := CharPtr ( Bar ); Foo2 := CharPtr ( Succ ( Bar ) ); @end smallexample
However, because of risks involved with type casts, explained below, you should try to avoid type casts whenever possible -- and it should be possible in most cases. For instance, the first example above could use the built-in function ``Ord'' instead of the type cast:
@smallexample i := Ord (Ch); @end smallexample
The assignments in the second example could be written in the following way without any type casts:
@smallexample Foo1 := @@Bar^ [ 0 ]; Foo2 := @@Bar^ [ 1 ]; @end smallexample
Value type casting only works between certain types: either between different ordinal types (including integer types), or between different real types, or between different pointer types. In each case, the actual value, i.e. the ordinal or numeric value or the address pointed to, respectively, is preserved in the cast.
Note: It is also possible to cast from an integer into a real type. This is a consequence of the fact that integer values are generally automatically converted to real values when needed.
Note: In the case of pointers, a warning is issued if the dereferenced target type requires a bigger alignment than the dereferenced source type (see @ref{alignment}).
Variable type casts
It is also possible to temporarily change the type of a variable, without converting its contents in any way. This is called variable type casting.
The syntax is the same as for value type casting. This can be confusing, as the example below shows.
The type-casted variable is still the same variable (memory location) as the original one, just with a different type. Outside of the type cast, the variable keeps its original type.
There are some important differences between value and variable type casting:
- Variable type casting only works on variables, not on expressions.
- The result of a variable type casting is still a variable. Especially, it can be used on the left side of an assignment (as a so-called ``lvalue''), or passed by reference to a procedure.
- No values are converted. The contents of the variable, seen as a raw bit pattern, are just interpreted according to the new type. In many cases, this does not make a big difference, e.g. the same ordinal values of different ordinal types have the same bit pattern (provided, the types have the same size), and similarly for different pointer types. However, there are cases where it does make a difference, most notably the completely different internal representation of integer and real types. This is demonstrated in the following example, admittedly a very constructed example, by using an integer and a real type of the same size and trying to cast between them.
- Because bit patterns are just interpreted differently, the source and target type must have the same size. If this is not the case, GPC will give a warning. (@@error? see below)
- Beware: Variable type casts might have unexpected effects on different platforms since you cannot rely on a specific way the data is stored (e.g. see @ref{endianness}).
@smallexample program TypeCastingTraps;
{ Declare a real type and an integer type of the same size, and some variables of these types we will need. }
Type RealType = ShortReal; IntegerType = Integer ( BitSizeOf ( RealType ) );
Var i, i1, i2, i3, i4, i5: IntegerType; r, r1, r2, r3, r4: RealType;
begin
@{ First part: Casting integer into real types. @}
@{ Start with some integer value @} i := 42;
@{ First attempt to cast. Here, an lvalue is casted, so this must be a variable type cast. Therefore, the bit pattern of the value of i is transferred unchanged into r1 which results in a silly value of r1. @} IntegerType (r1) := i;
@{ Second try. Here we cast an expression -- though a trivial one --, rather than a variable. So this can only be a value type cast. Therefore, the numeric value is preserved, i.e. r2 = 42.0 . @} r2 := RealType (i+0);
@{ Third way. In this last example, a variable is casted, and the result is used as an expression, not as an lvalue. So this could be either a value or variable type cast. However, there is a rule that value type casting is preferred if possible. So r3 will contain the correct numeric value, too. @} r3 := RealType (i);
@{ Of course, you do not need any casts at all here. A simple assignment will work because of the automatic conversion from integer to real types. So r4 will also get the correct result. @} r4 := i;
@{ Now the more difficult part: Casting real into integer types. @}
@{ Start with some real value. @} r := 41.9;
@{ Like the first attempt above, this one does a variable type cast, preserving bit patterns, and leaving a silly value in i1. @} RealType (i1) := r;
@{ The second try from above does not work, because an expression of type real is to be casted into an integer which is not allowed. @} @{ i2 := IntegerType (r+0); @}
@{ Third way. This looks just like the third way in the first part which was a value type cast. But -- surprise! Since value type casting is not possible from real into integer, this really does a variable type casting, and the value of i3 is silly again! This difference in behaviour shows some of the hidden traps in type casting. @} i3 := IntegerType (r);
@{ As often, it is possible to avoid type casts altogether and convert real types into integers easily by other means, i.e. by using the built-in functions ``Round'' or ``Trunc'', depending on the mode of rounding one wants. @} i4 := Round (r); @{ 42 @} i5 := Trunc (r); @{ 41 @}
end. @end smallexample
When dealing with objects (see @ref{OOP}), it is often necessary---and safe---to cast a pointer to an object into a pointer to a more specialized (derived) object. In future releases, GPC will provide an operator @samp{as} for a safer approach to this problem.
See also: @ref{absolute}, @ref{alignment}, @ref{endianness}, @ref{OOP}, @ref{Ord}, @ref{Chr}, @ref{Round}, @ref{Trunc}.
@c ============================================================================
Some notes:
- My texi skills are quite limited. In particular I think I should - highlight the headings "value"- and "variable type casting" - mark the "Note:"'s specially and - format the enumeration better than with "-"'s, but I wanted to get this out tonight, so I didn't look up how to do it.
- When writing this, I strongly thought there should be a way to mark certain sections as "advanced" and "very advanced"... :-)
- I'm not completely sure about the kind of types between which value type casting works, with "works" in the sense of (either or all): supported by BP, supported by GPC currently, should be supported by GPC. This is left to be checked and corrected if necessary.
- Unfortunately, GPC does not behave completely like I described it :-(, but I think wrongly so. In particular, for the line "RealType (i1) := r;", it does a value type cast which is IMHO wrong since it is an lvalue. So I consider it a bug in GPC, tested by the following program, very similar to the one above, without comments:
program frank170; Type RealType = ShortReal; IntegerType = Integer ( BitSizeOf ( RealType ) );
Var r, r1, r2, r3, r4: RealType; i, i1, i2, i3, i4, i5: IntegerType;
begin i := 42; IntegerType (r1) := i; r2 := RealType (i+0); r3 := RealType (i); r4 := i; r := 41.9; RealType (i1) := r; i3 := IntegerType (r); i4 := Round (r); { 42 } i5 := Trunc (r); { 41 } if (r1<>42) and (r2=42) and (r3=42) and (r4=42) and (i1<>41) and (i1<>42) and (i3<>41) and (i3<>42) and (i4=42) and (i5=41) then writeln('OK') else writeln('Failed') end.
- I think, GPC should give an error, not just a warning, when casting between types of different size -- at least in {$x-} mode. With extended syntax active (or some other switch), a warning still seems appropriate.
Of course: Comments/improvements/etc. welcome!
Frank