Pantalaimon
Pantalaimon

Reputation: 596

Array typed parameter not accepted when overloading function

In Delphi, I can do this:

Type
  TFourCC = Array[0..3] of AnsiChar;

Function Func(Param : TFourCC) : Boolean;
begin
  { ... }
end;

Func('ABCD'); // I can pass this as literal text without problems

Now, I want to make this parameter optional.

Function Func(Param : TFourCC = 'ABCD') : Boolean;
begin
  { ... }
end;

Now, the compiler throws me an error: E2268 Parameters of this type cannot have default values

Ok, so I was thinking overloading the function should do the trick then...

Function Func : Boolean; overload;
begin
  { ... }
end;

Function Func(Param : TFourCC) : Boolean; overload;
begin
  { ... }
end;

Func('ABCD'); // This line that worked in first example now gives an error

Unfortunately, Delphi doesn't like this either. Where it first accepted the parameter as a TFourCC typed variable, it now gives me E2250 There is no overloaded version of 'Func' that can be called with these arguments.

I beg to disagree with what this error tells me, the same thing worked when it wasn't overloaded.

Can someone explain me the logic behind this, and possibly a solution? I'd like to keep the TFourCC as it is (not a string type), it keeps the handling of reading and writing much easier. I rather avoid assigning it to a variable first before passing it, because the function will be used alot..

Upvotes: 6

Views: 166

Answers (1)

Andreas Rejbrand
Andreas Rejbrand

Reputation: 109003

Unfortunately, this is how the type system currently works.

But the good news is that you can do magic with records and operator overloading. For instance, with

type
  TFourCC = record
  strict private
    function GetChar(Index: Integer): AnsiChar;
    procedure SetChar(Index: Integer; const Value: AnsiChar);
  public
    class operator Implicit(AValue: AnsiString): TFourCC;
    class operator Implicit(AValue: TFourCC): AnsiString;
    class operator Equal(a, b: TFourCC): Boolean;
    class operator NotEqual(a, b: TFourCC): Boolean;
    property Chars[Index: Integer]: AnsiChar read GetChar write SetChar; default;
  case Boolean of
    False: (AnsiChars: array[0..3] of AnsiChar);
    True: (Data: Cardinal)
  end;

implementation

{ TFourCC }

class operator TFourCC.Implicit(AValue: AnsiString): TFourCC;
begin
  if Length(AValue) <> 4 then
    raise Exception.Create('Not a valid TFourCC string.');
  Result.Data := PCardinal(@AValue[1])^;
end;

class operator TFourCC.Implicit(AValue: TFourCC): AnsiString;
begin
  SetLength(Result, 4);
  PCardinal(@Result[1])^ := AValue.Data;
end;

class operator TFourCC.Equal(a, b: TFourCC): Boolean;
begin
  Result := a.Data = b.Data;
end;

class operator TFourCC.NotEqual(a, b: TFourCC): Boolean;
begin
  Result := a.Data <> b.Data;
end;

function TFourCC.GetChar(Index: Integer): AnsiChar;
begin
  Result := AnsiChars[Index];
end;

procedure TFourCC.SetChar(Index: Integer; const Value: AnsiChar);
begin
  AnsiChars[Index] := Value;
end;

you get all these benefits:

procedure TForm1.FormCreate(Sender: TObject);
var
  x: TFourCC;
begin
  x := 'FINE';                     // implicit conversion from string
  ShowMessage(x);                  // implicit conversion to string
  x[0] := 'D';                     // can access parts for writing (without explicit member)
  ShowMessage(x);
  ShowMessage(x[0]);               // can access parts for reading (without explicit member)
  ShowMessage(x.Data.ToString);    // can access underlying storage as a 32-bit integer
end;

And, you can now do

procedure f(A: TFourCC); overload;
begin
  ShowMessage(A);
end;

procedure f; overload;
begin
  ShowMessage('ABCD');
end;

Unfortunately, I am very much in a hurry right now, so I cannot double-check the correctness or comment further right now!

Upvotes: 6

Related Questions