Primitive types have default conversion rules, a lot of programming languages work this way by default. This is so that you don’t have to typecast everything and it’s really a convenience. For example, you’ll be able to assign an Integer to an Extended without trouble, both by passing a literal 42 as well as assigning a variable of type Integer.
procedure Hello;
var
SomeNumber: Extended;
AnotherNumber: Integer;
begin
AnotherNumber := 6;
SomeNumber := 2; // works
SomeNumber := AnotherNumber; // works
end;
Even if do type aliasing, the types are still the types they were and have automatic conversion rules:
type
ANewType = Extended;
AnotherNewType = Integer;
implementation
procedure Hello;
var
SomeNumber: ANewType;
AnotherNumber: AnotherNewType;
begin
AnotherNumber := 6;
SomeNumber := 2; // works
SomeNumber := AnotherNumber; // works
end;
If you really want strict conversion rules, because for example you want to avoid meters being assigned to seconds or vice versa, you will need to create new types. And new types are created by using either a record or a class.
To allow certain conversions, you can add those rules to the types using operator overloading. (see Embarcadero Wiki).
In FreePascal the syntax is different, but you can do the same (see FreePascal Reference).
You can then even make conversion rules for meters * seconds creating a meters per second type.
Here’s an example of how it works in Delphi:
type
TMetersPerSecond = record
private
m: Extended;
s: Extended;
value: extended;
mset: Boolean;
sset: Boolean;
function Units: string;
public
function ToString: string;
end;
TMeters = record
private
value: Extended;
function Units: string;
public
class operator Implicit(Value: Extended): TMeters ;
class operator Explicit(Value: TMeters ): TMetersPerSecond ;
class operator Multiply(ms: TMetersPerSecond; m: TMeters ): TMetersPerSecond;
function ToString: string;
end;
TSeconds = record
private
value: Extended;
function Units: string;
public
class operator Implicit(Value: Extended): TSeconds;
class operator Explicit(Value: TSeconds): TMetersPerSecond;
class operator Multiply(ms: TMetersPerSecond; s: TSeconds): TMetersPerSecond;
function ToString: string;
end;
implementation
uses
SysUtils;
class operator TMeters.Implicit(Value: Extended): TMeters;
begin
Result.value := Value;
end;
class operator TSeconds.Implicit(Value: Extended): TSeconds;
begin
Result.value := Value;
end;
class operator TMeters.Explicit(Value: TMeters): TMetersPerSecond;
begin
Result.m := Value.value;
Result.mset := True;
if Result.sset then
Result.value := Result.s * Result.m;
end;
class operator TSeconds.Explicit(Value: TSeconds): TMetersPerSecond;
begin
Result.s := Value.value;
Result.sset := True;
if Result.mset then
Result.value := Result.s * Result.m;
end;
class operator TSeconds.Multiply(ms: TMetersPerSecond; s: TSeconds): TMetersPerSecond;
begin
Result := ms;
Result.s := s.value;
Result.sset := True;
if Result.mset then
Result.value := Result.s * Result.m;
end;
class operator TMeters.Multiply(ms: TMetersPerSecond; m: meters): TMetersPerSecond;
begin
Result := ms;
Result.m := m.value;
Result.mset := True;
if Result.sset then
Result.value := Result.s * Result.m;
end;
function TMetersPerSecond.Units: string;
begin
Result := 'm/s';
end;
function TMetersPerSecond.ToString: string;
begin
Assert(mset and sset);
Result := Format('%f%s', [value, Units])
end;
function TMeters.Units: string;
begin
Result := 'm';
end;
function TMeters.ToString: string;
begin
Result := Format('%f%s', [value, Units]);
end;
function TSeconds.Units: string;
begin
Result := 's';
end;
function TSeconds.ToString: string;
begin
Result := Format('%f%s', [value, Units]);
end;
var
time: TSeconds;
distance: TMeters;
speed: TMetersPerSecond;
different_speed: TMetersPerSecond;
initialization
time := 10.0;
distance := 3;
// distance := time; // does not work, because seconds are not meters!
speed := TMetersPerSecond(time) * distance;
// or the other way around
speed := TMetersPerSecond(distance) * time;
WriteLn('speed -> ' + time.ToString + ' * ' + distance.ToString + ' = ' + Speed.ToString);
// different_speed := distance; // this doesn't work without explicit conversion
WriteLn('different_speed -> ' + different_speed.ToString);
end.
Contact
GDK Software NL
(+31) 78 750 1661GDK Software UK
(+44) 20 3355 4470