3
votes

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

1
@J... : it's not a dupe. He accepted a SOLUTION. I have a solution - that's not what I'm looking for. I'm looking for an explanation.Vector
Follow through in the comments - I linked to the best solution, probably the best answer to the question 'why?' is here, however (also linked in the marked dupe) stackoverflow.com/a/58167/327083J...
For the short answer, the base class method signature does not include overload so any derived classes which overload it are introducing a change to the method signature and are 'hiding' the ancestor's definition. Mark aMethod as overload;virtual;abstract; in TBaseClass and the compiler warning goes away (even if TBaseClass does not define any overloaded variants).J...
@J... : Huh - you're right. I just tried it - didn't think it would compile because there is no overloaded method in the base class. But it did - in fact 'overload' always seems to compile, no matter where you use it. (I VERY RARELY use it - don't like it - rather re-name the method) I still think the warning is incorrect because since it's overloaded, nothing is being hidden - the original call is still callable from the derived class-but apparently reuse of the name is enough to generate the warning-which is the real answer to the question. Post your comment as answer and get the points.Vector

1 Answers

3
votes

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;