Search code examples
delphiinheritancedelphi-xecompiler-warnings

Why does the compiler warn when overloading an abstract method introduced in the base class?


I have code like this:

TBaseClass = class(TObject)
protected
  procedure aMethod(const s:string);virtual;abstract;
end;

TDerivedClass = class(TBaseClass)
protected
   procedure aMethod(const s:string);overload;override;
   procedure aMethod(const s:string;const x:integer);overload;
end;

Compiler generates a warning:

[DCC Warning].... W1010 Method 'aMethod' hides virtual method of base type 'TBaseClass'

Clicking on the warning sends me to 'aMethod(const s:string;const x:integer);' since it is not marked with the override directive. However, that method CANNOT be marked override: no method with that signature exists in the base class, and adding the override directive to that method causes a compiler error:

[DCC Error].... E2037 Declaration of 'aMethod' differs from previous declaration.

This is obvious, since no method with that signature exists in TBaseClass.

Only 'aMethod(const s:string)' exists in the base class, and that method is marked 'override' - so nothing in the base class is being hidden at all.

Why is this not an erroneous warning? (not the first one I've come across, either...)

The reference to the other question is incorrect, IMO. I have a solution - I simply used refactor, and renamed the problematic method. But I'm not looking for a solution, which is trivial. I'm looking for an explanation of this warning. Is there something wrong with this design? (Perhaps using overload and override together is not good design - I can agree with that, but that's not what the compiler warning is really about.)


Solution

  • I recently ran into this same problem in Indy. Its TIdStack base class has abstract GetSocketOption() and SetSocketOption() methods that TIdStackBDSBase would override and overload with its own abstract methods for its descendants (TIdStackWindows, etc) to override. I was getting these exactly same kinds of compiler errors.

    For example:

    type
      TIdStack = class(TObject)
        ...
        procedure GetSocketOption(ASocket: TIdStackSocketHandle;
          ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption;
          out AOptVal: Integer); virtual; abstract;
        ...
      end;
    

    .

    type
      TIdStackBSDBase = class(TIdStack)
        ...
        procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
          AOptName: TIdSocketOption; out AOptVal: Integer); overload; override;
        procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
          AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); overload; virtual; abstract;
        ...
      end;
    
    procedure TIdStackBSDBase.GetSocketOption(ASocket: TIdStackSocketHandle;
      ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer);
    var
      LBuf, LLen: Integer;
    begin
      LLen := SizeOf(LBuf);
      GetSocketOption(ASocket, ALevel, AOptName, LBuf, LLen);
      AOptVal := LBuf;
    end;
    

    .

    type
      TIdStackWindows = class(TIdStackBSDBase)
        ...
        procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
          AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
        ...
      end;
    
    procedure TIdStackWindows.GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer);
    begin
      ...
    end;
    

    Regardless of whether TIdStack.GetSocketOption() is declared as overload or not, XE2 reports this error:

    [DCC Error] IdStackWindows.pas(296): E2137 Method 'GetSocketOption' not found in base class
    

    It turns out that in some situations (like Indy's), the compiler requires the base class method to be declared as overload (even if there is no corresponding overloaded method in the base class itself) in order for a derived class to override + overload it.

    However, when I did that, it did not work in XE2 and earlier, causing the "hides virtual method" warnings and other errors. That appears to have been fixed in XE3. So what I ended up having to do in Indy was:

    1. declare the base TIdStack methods as overload; virtual; abstract;.

    2. in TIdStackBDSBase, declare the overriden methods as overload; override;, then:

      a. in XE2 and earlier, declare the overloaded methods as reintroduce; overload;, and declare separate non-overloaded virtual; abstract; methods for descendants to override.

      b. in XE3 and later, declare the overloaded methods as overload; virtual; abstract;, and let descendants override them normally.

    In other words, the following code works in XE3 but not in XE2:

    type
      TIdStack = class(TObject)
        ...
        procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer); overload; virtual; abstract;
        ...
      end;
    

    .

    type
      TIdStackBSDBase = class(TIdStack)
        ...
        procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer); overload; override;
        procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); overload; virtual; abstract;
        ...
      end;
    
      procedure TIdStackBSDBase.GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer);
      var
        LBuf, LLen: Integer;
      begin
        LLen := SizeOf(LBuf);
        GetSocketOption(ASocket, ALevel, AOptName, LBuf, LLen);
        AOptVal := LBuf;
      end;
    

    .

    type
      TIdStackWindows = class(TIdStackBSDBase)
        ...
        procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
        ...
      end;
    
      procedure TIdStackWindows.GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer);
      begin
        ...
      end;
    

    The following code works in XE2, though:

    type
      TIdStack = class(TObject)
        ...
        procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer); overload; virtual; abstract;
        ...
      end;
    

    .

    type
      TIdStackBSDBase = class(TIdStack)
        ...
        procedure WSGetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); virtual; abstract;
        ...
        procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer); overload; override;
        procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); reintroduce; overload;
        ...
      end;
    
    procedure TIdStackBSDBase.GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out AOptVal: Integer);
    var
      LBuf, LLen: Integer;
    begin
      LLen := SizeOf(LBuf);
      WSGetSocketOption(ASocket, ALevel, AOptName, LBuf, LLen);
      AOptVal := LBuf;
    end;
    
    procedure TIdStackBSDBase.GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer);
    begin
      WSGetSocketOption(ASocket, ALevel, AOptName, AOptVal, AOptLen);
    end;
    

    .

    type
      TIdStackWindows = class(TIdStackBSDBase)
        ...
        procedure WSGetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
        ...
      end;
    
      procedure TIdStackWindows.WSGetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer);
      begin
        ...
      end;