E2251 Ambiguous overloaded call to '%s' Delphi error

While porting our legacy project from old Delphi version to new Delphi version like from Delphi 7 to Delphi XE5, we faced above error frequently as we have used StrPas / StrPos / StrLen / TextToFloat function calls. And we found several cases and several reasons for cause of this error. Here I will explain why we get this error and what is the solution of this error.

Some of following error we faced.

E2251 Ambiguous overloaded call to 'Pos'
E2251 Ambiguous overloaded call to 'StrPas'
E2251 Ambiguous overloaded call to 'StrLen'
E2251 Ambiguous overloaded call to 'TextToFloat'

Why we get this error and what is the solution?

Following are possible reasons of occurring this error in latest Delphi versions code.

Case 1
If more than one overloaded version of a function exists in a unit and we are passing wrong arguments to the function call then this error will show.

For example.

We have different versions of StrPas function in SysUtils.pas

function StrPas(const Str: PAnsiChar): AnsiString;

function StrPas(const Str: PWideChar): UnicodeString;

On above function versions, one version is having PAnsiChar as parameter and the other one is having PWideChar as parameter. 

And I have written a function in Delphi 7 which works fine.

function GetActualString(S: string): string;
begin
  Result := StrPas(@S);
end;

But when I compile the same function in Delphi XE5 version then I get “E2251 Ambiguous overloaded call to StrPas” error.

Here the error is because @S has type of Pointer and the compiler does not know which overload function to be called as per the parameter type  (PAnsiChar or PWideChar).

Solution 

So here the solution is to change the code @S as PChar(S). In Delphi XE5 change the function as follow.

function GetActualString(S: string): string;
begin
  Result := StrPas(PChar(S));
end;

We can use PWideChar also now PChar is PWideShar. And for AnsiString we can use PAnsiChar.

Another Example of Pointer type

Here is another version of the same function where I am passing the parameter as Pointer type. It will also result the same error and also have same solution.

function GetActualString(S: Pointer): string;
begin
  Result := StrPas(S);
end;

So here we can see, I have declared S parameter of type Pointer so on function call statement compiler cannot get which version to be called. So to fix this issue we can apply same solution.

Solution 

function GetActualString(S: Pointer): string;
begin
  Result := StrPas(PChar(S));
end;

Case 2
When we have the same function in two different units and we have added both unit to a unit’s uses part. 

For Example

Sometime during upgrade a project to Delphi XE5 from Delphi 7, we get "Ambiguous overloaded call to 'StrLen'" error for function  StrLen because in new Delphi versions StrLen is moved to Ansistrings.pas unit and for backup support it also exists in SysUtils.pas unit.

  System.SysUtils.pas 
       function StrLen(const PAnsiChar): Cardinal;

  System.AnsiStrings.pas       
       function StrLen(const PAnsiChar): Cardinal;

Here I have used StrLen function on button click.

uses
    SysUtils, Ansistrings;
……
procedure TForm1.Button1Click(Sender: TObject);
var
    S : AnsiString;
    L : Integer;
begin
    S := 'Hello world!';
    L := StrLen(PAnsiChar(S));
end;

Here you can see we have used both Sysutils and Ansistrings units. On button click we are calling StrLen function and you will get the error saying “E2251 Ambiguous overloaded call error to 'StrLen'”. And as function StrLen exists in  both SystUtils and Ansistrings so compiler cannot get which one to be called. 

Solution 

So here the solution is add the unit name with the function call statement like follow.

procedure TForm1.Button1Click(Sender: TObject);
var
    S : AnsiString;
    L : Integer;
begin
    S := 'Hello world!';
    L := Ansistrings.StrLen(PAnsiChar(S));
end;

Note that here I used Ansistrings instead of Sysutils because the SysUtils one is deprecated and produces a compiler warning: "Moved to the AnsiStrings unit".

Case 3
If we have used Array as parameter to a function and we are passing dynamic array as value then it that case compiler cannot find what type of value we are passing and it will result to the error.

For Example

TForm1 = class(TForm)
   procedure FormCreate(Sender: TObject);
   private
      procedure ArrayTest(iArray : array of integer); overload;
      procedure ArrayTest (oArray : array of TObject); overload;
end;
.......
procedure TForm1.FormCreate(Sender: TObject);
begin
   ArrayTest ([10, 15]);
end;

So where in above example we can ArrayTest is an overloaded function. One version of function having integer array as parameter and other one is having Tobject array as parameter. So when we call the function with passing dynamic array like above function call then it will result error "E2251 Ambiguous overloaded call to ArrayTest”. Because here, compiler cannot get what type of data we are passing and which overloaded version function to be called.

Solution

So here the solution is, we have to declare a local variable either of integer type or Tobject type and then need to pass to the function call like follow.

procedure TForm1.FormCreate(Sender: TObject);
var
  Tmp : array of integer;
begin
  SetLength(Tmp, 2);
  Tmp[0] := 10;
  Tmp[1] := 15;
  ArrayTest (Tmp);
end;

Comments

  1. This: `function GetActualString(S: string): string;` doesn't make any sense. Just use `UniqueString()`. Or do `Result := PChar(s);`. `StrPas()` does nothing!

    ReplyDelete
  2. GetActualString function is just an example. If we have sued StrPas, StrPos, StrLen functions and written code like StrPas(@S) then we can replace with the solution. I agree that we don't use StrPas very frequently but we do use StrPos, StrLen. So that same solution can be applied for them too.

    ReplyDelete

Post a Comment

Popular posts from this blog

ShellExecute in Delphi

How to send Email in Delphi?

Variants in Delphi. Use of Variant Array. How to check a Variant is unassigned or empty or clear ?