Using TPW's Dynamic Method Tables directly.

OWL, the object-oriented Windows library that comes with Turbo Pascal for Windows, uses TPW's new dynamic methods in an interesting way: You can install a Windows message handler simply by defining a dynamic method. To install a wm_Paint message handler, for example, you just give your application object a virtual wm_Paint method.

When the window function receives a message, it can tell if you have defined a method to handle it. If you do have a handler for that specific message, the windows function will call it. If you don't, it will pass the message to the object's generic message handler.

Not only does being able to use one window function for every window save memory and programming time while reducing the chances of error, doing a REPNE CMPSW on a table of method indices is more efficient than stepping through a case statement, too.

But if you'd rather write your own Windows interface than use OWL, and you would like to use dynamic methods the same way OWL does, you face a bit of a problem: TPW doesn't provide any way to call a method by its dynamic index. Rather, you must call the method by name, just like a normal static or virtual method. While this produces code that does do a call-by-index via a run-time library routine, there doesn't seem to be any defined access to that routine.

This is a shame, because this sort of call-by-index opens up all sorts of interesting possibilities, like modifying the DMT at runtime and the ability to `pass messages' to objects rather than just calling a static name. When you give an object commands by passing numeric (or enumerated) messages to it, you gain the ability to treat the message as a variable, not a constant, and the same code can thus tell an object to do different things at different times. For example, where a traditionally object-oriented program can tell an object to Display itself without regard to how it will do that, its more interpretive, `doubly polymorphic' cousin can tell an object to `do something', without knowing or caring how the object will carry out the command or whether the command is Display, Print, SaveToDisk, or CopyToClipboard.

Somewhat similarly, if we can compare the address of an object's dynamic method to the address of its ancestral type's method, we can do something that we can't do in TP5.5 or 6: find out if a particular object has overridden an ancestral method. While most of the time we don't want to know this, and just want to be able to call the method and trust that the object will respond appropriately, there are times when we do care. For example, just as one can do `background processing' under DOS by using KeyPressed and ReadKey instead of Read, so does one do background processing under Windows by using PeekMessage instead of GetMessage. But it doesn't make a lot of sense to use PeekMessage if you don't need to do background processing, as this slows the whole system down by forcing Windows to give your task a share of the CPU on every pass through the task list. If the ancestral application object could tell before it entered its message loop whether or not the specific descendant type has a `real' Background method or just inherits the ancestor's `stub' method, it could automatically use the appropriate message loop.

Fortunately, while we can't call the RTL's DMT lookup routine directly, the DMT and VMT formats are straightforward and thoroughly documented, and the TypeOf() operator will return a pointer to the VMT. The Dynamic function in Listing 1 takes two arguments: a pointer to a VMT and a dynamic method index. It returns either a pointer to the method, or Nil if there is no method with that index.

Its operation is quite simple. It loads its arguments into registers, then reads the DMT offset from the VMT. If there is no DMT, it returns Nil. If there is a DMT, Dynamic reads the length word, and calculates the offset of the last index in the table. It then scans backwards until it finds the desired index entry or until it reaches the start of the table. (The backwards scan means that a successful search leaves the match position in CX.) If the search was successful, it reads and returns the appropriate method pointer; otherwise it loads a pointer to the object's parent's DMT and jumps back to the DMT existence check at the top of the function.

Using Dynamic is just as simple. While the manuals explicitly state that Turbo Pascal provides no way to use a pointer to code and that all you can do with such a pointer is to pass it to an assembly language routine, this is just not true. Despite their strange syntax, procedural types are essentially just another sort of typed pointer, so to use a pointer to a piece of code all we have to do is to cast it to a procedural type.

Casting pointers to `normal' procedures and functions is a bit easier than casting pointers to methods: We just use a procedural type with the same syntax as the code we're calling. Thus, if we have

  type     FnType = function (N: integer): integer;
  function Foo(N: integer): integer; begin {...} end;
  const    Fn: FnType = Foo;     FnPtr: pointer = @ Foo; 
the three statements "Foo(N)", "Fn(N)", and "FnType(FnPtr)(N)" all have the same effect: They push a copy of N then call Foo. What's more, the latter two statements will generate the exact same call-through-a-pointer code (which is a bit smaller and a bit slower than the direct call-32-bit-address code).

Casting pointers to method calls is complicated by the `invisible' Self pointer. It's only invisible to the called code: The calling code has to be sure to supply it! For example, "type NiladicMethod = procedure (var Self)" is a prototype for a method with no normal arguments and no result, while "type MsgHandler = procedure (var Msg: MsgType; var Self)" is a prototype for a method with one var parameter.

[See my article "Three Myths About Turbo Pascal" for more examples of using pointers to methods.]
Thus, assuming your export function can associate a window handle with an object pointer and can thereby call a method, an object oriented window function for Windows 3.0 might look like Listing 2 while the application's main message loop might look something like Listing 3.

In conclusion, dynamic methods are an exciting new addition to the Turbo Pascal language, and I hope that they (and the PChar extensions) are part of the next version of Turbo Pascal for DOS. The DMT lookup function I've presented in this article is simple and reasonably efficient, and its use in a few key places allows applications to be simultaneously more flexible and easier to code.


This article originally appeared in PC Techniques.

Copyright © 1991, Jon Shemitz, jon@midnightbeach.com. HTML markup, 16 October 1994.

KBD icon Return to Publications list