Tuesday, April 04, 2006

Dynamic methods and inherited

In an earlier blog post we covered how virtual methods and inherited calls work. In Delphi there is another kind of polymorphic method, the dynamic method. Note that this polymorphism series targets only the native Win32 platform, but suffice to mention that in Delphi for .NET, dynamic methods are actually identical to virtual methods. In Win32, message methods use the same underlying compiler structures and dispatch mechanism as dynamic methods, while in .NET an attribute- and reflection- based solution is used. We’ll cover message methods in a later article.

While message methods can be very useful, dynamic methods were originally created to work around data segment size issues in 16-bit Windows and DOS. They first appeared in Turbo Pascal for Windows, later in Borland Pascal 7.0 (that targeted real-mode DOS, protected mode DOS and 16-bit Windows). The problem it solved was the total size of all classes’ VMTs. In those days the VMT structures were crammed together in the 64 kB sized global data segment. If you have a base class with a large number of virtual methods, and a large number of descendant classes that only override a few of these methods, there will be some “wasted” space in the VMTs. This is because the non-overridden methods will still have a slot in each descendant’s VMT table – and all those slots will point to the base class methods. Now, with dynamic methods, the compiler instead builds a kind of sparse array for each class – the dynamic method table (DMT) – that is referenced from the VMT. Only newly introduced or overridden dynamic methods take up space in each class’ DMT. In a large class hierarchy like Turbo Vision, OWL or VCL with many descendants (think TComponent and TControl), using dynamic methods can save some space. In the days of the 64 kB data segment limit, this was crucial.

Nowadays, their usefulness is much more limited. In Win32 Delphi, the VMT and DMT structures are stored in the code segment, not in the data segment and there are no size limits (well, 2 GB). Calling a dynamic method can be significantly slower than calling a virtual method and the space savings is minuscule compared with the typical size of a VCL application. In fact, if you don’t have a large class hierarchy or if most methods are overridden, dynamic methods will create larger and slower code, not smaller. So the general advice is to avoid declaring dynamic methods in your classes – use virtual methods instead.

Ok, with that little history lesson under our belts, we’re ready to dive into the dynamic method semantics. Well, it’s not much of a dive, actually. The semantics is identical to how virtual methods work – both with regards to declaring and overriding methods and to use the implicit “inherited;” syntax vs. the explicit “inherited MethodName;” syntax.

But for completeness – here is the short story:

  TShape = class
procedure Clicked; dynamic;
This declares a new dynamic method. As indicated above, you should think twice before doing this. At least make sure that the method you make dynamic is not used in a performance sensitive routine. A routine called as part of UI handling is probably ok – in this case handling a mouse-click on the shape object.
  TRectangle = class(TShape)
procedure Clicked; override;

procedure TRectangle.Clicked;
inherited Clicked;

This will unconditionally call the inherited Clicked method in the base class. If the base class method is abstract, this will fail at run-time with an EAbstractError exception.

The alternative syntax is to call just "inherited;" – like this:

  procedure TRectangle.Clicked;

When the parent method is non-abstract this will work identically as above, passing any parameters that the current routine was passed. If the base class method is abstract, the “inherited” call becomes a no-op. The compiler generates no code for it (and thus you cannot set a breakpoint on it).

In an upcoming article we’ll dig deeper into the inner workings of dynamic methods and the DMT, including a tip on how you can speed up code that needs to call a dynamic method in a performance sensitive loop.

[Delphi syntax highlighting provided by DelphiDabbler PasH]


Unknown said...

Great Hallvard, thanks, I've just finished a series of classes that use polymorphism extensively and was wondering what the difference was between:

inherited Create;


Guru said...

None. But the second one is preferred (more explicit).

Copyright © 2004-2007 by Hallvard Vassbotn