3

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vector
  • 10,879
  • 12
  • 61
  • 101
  • possible duplicate of [Delphi: Method 'Create' hides virtual method of base - but it's right there](http://stackoverflow.com/questions/9103263/delphi-method-create-hides-virtual-method-of-base-but-its-right-there) – J... May 02 '13 at 17:00
  • @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 May 02 '13 at 17:19
  • 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) http://stackoverflow.com/a/58167/327083 – J... May 02 '13 at 17:26
  • 1
    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... May 02 '13 at 17:34
  • @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 May 02 '13 at 17:48
  • Remy's already posted essentially the same answer, I hate to double-up ;) – J... May 02 '13 at 17:52
  • @J...: yes it does work, but I'm not going to use that approach. Rather obfuscated IMO. I'm just going to drop the overload and change the method name. – Vector May 02 '13 at 17:53
  • @J... : I see Remy's answer - but he seems to have run into something else - says it only works in XE3 for him. Besides, he's got plenty of points. :-) – Vector May 02 '13 at 17:57

1 Answers1

3

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;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • With base class `overload; virtual; abstract;` and derived class `overload; override` (for the declared base method) and `overload;` (for the remaining methods) I do not get the 'hides virtual` compiler warning on XE2 or D2010... – J... May 02 '13 at 17:48
  • Same here - it added overload to the base declaration and all was well in XE-1. – Vector May 02 '13 at 17:55
  • I have updated my answer with the exact code that causes XE2 to error but works in XE3, and what code I had to use in XE2 to make it work. – Remy Lebeau May 02 '13 at 18:17
  • @RemyLebeau I still have no problem with the pattern you posted : Base method overload/virtual/abstract, derived class overload/override (of base method) - overload/virtual/abstract (extended args), second derived class override of virtual extended args method ...no warning in D2010 or XE2 – J... May 02 '13 at 18:37
  • I am using XE2 Update 4, what are you using? And I have had Indy users complain about the same problems, so it is not just me. That is why I had to re-write Indy's code the way I did, which works. – Remy Lebeau May 02 '13 at 20:26
  • @RemyLebeau I'm using XE2 update 4, D2010 update 4&5. Both compile the above pattern without warning. – J... May 03 '13 at 12:28
  • @J...: I don't know what else to tell you. The code I showed is a direct copy-paste of Indy's actual code. I assure you, it does NOT compile in XE2, but does in XE3 and XE4. Feel free to download Indy and try compiling it for yourself (disable the IFDEFs around the offending code in IdStackBSDBase.pas and IdStackWindows.pas). – Remy Lebeau May 03 '13 at 16:46
  • @RemyLebeau I believe you, but at the same time whatever the problem is it appears to not be caused by this pattern. There must be something else going on with Indy. I'll have a look at some point, perhaps. – J... May 03 '13 at 19:29