[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 8.7 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:

    program TypeCastDemo;

    var
      Ch: Char;
      i: Integer;

    begin
      i := Integer (Ch)
    end.

Another, more complicated, example:

    program TypeCst2Demo;

    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 }

    begin
      Foo1 := CharPtr (Bar);
      Foo2 := CharPtr (Succ (Bar))
    end.

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:

    i := Ord (Ch);

The assignments in the second example could be written in the following way without any type casts:

    Foo1 := @Bar^[0];
    Foo2 := @Bar^[1];

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 current 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 section 8.2.11.2 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 section 8.2.11.1 Endianness).

    program TrapsOfTypeCastsDemo;

    { 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.

When dealing with objects (see section 8.8 Object-Orientated Programming), 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 ``as'` for a safer approach to this problem. See also: section 9.2 absolute, section 8.2.11.2 Alignment, section 8.2.11.1 Endianness, section 8.8 Object-Orientated Programming, section 9.181 Ord, section 9.41 Chr, section 9.236 Round, section 9.282 Trunc.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

This document was generated by Frank Heckenbach on May, 10 2002 using texi2html