Querying for the implementing object from an interface (1)

Interfaces are a bit of a funny feature in native code Delphi — a fairly basic construct, and indeed, one around since Delphi 3, yet implemented in a manner that makes their use needlessly fiddly outside of their original purpose, which was to be a support feature for COM development.* Ever cursed how interface types appear to support inheritance, but don’t really (e.g., a class needs to explicitly implement every interface down the inheritance tree rather than just the bottom one)? Or how you have to be careful about not leaving danging interface references about, since a virtual method call (namely, to _Release) *will* be made behind your back (and what do you mean, ‘but _Release isn’t a virtual method on the implementing object…’)? Or how you have to concentrate when using runtime-only TXMLDocument instances in ways irrelevant for runtime-only components generally? Blame the Delphi 3 engineers baking COM conventions into the language!

With that in mind, I thought it very much a Good Thing when Malcolm Groves reported how yet another COM-related quirk — namely, the inability to cast from an interface to an object reference — will be removed in Delphi 2010, a thought strengthened when I read Allen Bauer’s subsequent post on the not particularly gory details. The one quibble I have, though, is how the new feature is being presented, viz., as the ability to cast from an interface to the implementing object. For, if we leave aside COM-specific issues and think of what an interface is in principle, surely the answer is, nothing more and nothing less than a collection of methods. What, though, is an object? In short, a collection of methods + other stuff. At heart, an object — any object! — is thus a strict superset of an interface. In querying for a particular class from an interface reference, then, what you’re doing is querying for just another ‘interface’  type. Think of it like that, and the fact that an ‘EInvalidCast’ exception will be raised upon trying to cast from an external interface not implemented in Delphi, far from being a limitation or quirk of the new feature, makes sense — for, any Delphi class will be an ‘interface’ that ‘extends’ the TObject ‘interface’, and by definition, no externally-authored object will do that.

Anyhow, on reading Allen Bauer’s post, I wondered how far (and how easily) the technique D2010 uses can be employed in earlier versions. Ten minutes playing around gave an answer. More on that next time though…

* Knowing nothing about the internal discussions at the time, I should probably hedge and say motives may have been mixed — at least, Java emerged in the same timeframe with interface types as a first class language feature not designed to support for any particular external API. The fact that the base Delphi interface type was called IUnknown rather than IInterface up until Delphi 6 emphasises the Delphi engineers’ COM-centric mindset though.

Advertisements

4 thoughts on “Querying for the implementing object from an interface (1)

  1. It’s not really that surprising that interfaces were COM centric given that Delphi was at that time (and still essentially is) a Windows development language.

    Had they not incorporated COM semantics into the compiler, we the developers would have been left with that thankless task when implementing COM systems, which – at the time – would have been the vast majority of the usage of interfaces.

    It might be interesting to speculate how these COM semantics might now be removed and made invokable/suppressible on a case by case basis, but to have expected that to have been a consideration at that time is I think to rely too heavily on rose-tinted 20:20 hindsight (Java was by it’s nature very different, being specifically NOT a Windows specific implementation).

    Also of course part of the COM semantic is expressed within the VCL classes, rather than the compiler/RTL per se. i.e. the ref count based lifetime management of TInterfacedObject etc.

    I get around this part in my code at least by having my own TInterfacedObject which supports interfaces but doesn’t use ref counted lifetime management. The RTL defined TInterfacedObject I have aliased as TCOMInterfacedObject.

    (I could have made my own class TNonCOMInterfacedObject, but … well… yuck). 🙂

    Problems can potentially arise if non-NIL references are left “dangling” to free’d objects of course, but I no more allow that to happen than I would allow non-NIL object references to “dangle” so in practice it’s rarely – read: never more than potentially – a problem for me.

    • Jolyon — while ‘but Delphi is a Windows development language’ is a reason I’m generally confortable with, I think the issues are more complicated in this case. For a start, I still stand by the relevance of the Java precedent — ideally, IUnknown would have been a descendent of the native interface type, not the native interface type itself. In fact, look at the COM support in what was ‘the’ Windows development environment of the time, namely Visual C++. To support COM, MS didn’t create extentions to the language that baked in COM semantics, but instead created special libraries for the task. You might reply — Ah, but what of Delphi’s main competitor, namely VB? MS retooled the whole product to be based on COM! Indeed they did, and created high level COM development environment far slicker than Delphi’s IMO. In VB5/6, you didn’t have ‘native’ controls and COM controls – you had native controls (and yes, you could write custom controls in VB5/6, and very easily too, without any of the VCL’s distinctions between TFrame, TActiveForm and TCustomControl). Likewise, you didn’t have a COM string type (WideString) and a native string type (AnsiString) — you had a native string type. Moreover, you didn’t have native classes and COM interfaces — you had native classes.

      Also of course part of the COM semantic is expressed within the VCL classes, rather than the compiler/RTL per se. i.e. the ref count based lifetime management of TInterfacedObject etc.’

      The fact that IUnknown is half implemented in the compiler, half implemented in code, is a good example of how Delphi’s COM support generally has a complexity about it missing in both general VCL programming and COM programming in VB. For sure, when creating a plain jane desktop app, you can drill down to HWNDs and DCs etc., but you don’t have to have techinical knowledge about what the VCL wraps to be a competent VCL programmer. Not so with Delphi’s COM support, where everything is all ‘see through’ even when you’re not actively looking.

      I get around this part in my code at least by having my own TInterfacedObject which supports interfaces but doesn’t use ref counted lifetime management … Problems can potentially arise if non-NIL references are left “dangling” to free’d objects of course, but I no more allow that to happen than I would allow non-NIL object references to “dangle”’

      I still think a person shouldn’t have to care about this. In the case of the dangling object that leads to an AV, the error is in you *explicitly* calling a method after the object has been destroyed; in the interface case, it is in simply leaving the reference dangling, since the call that causes the AV (namely, to _Release) is put in behind your back.

  2. interfaces are introduced in 1999 with Delphi 5, along with major IDE OpenTools API re-mastering.
    Just for a record 🙂

    • 1. This is an old post.
      2. Interfaces were introduced in 1997 with Delphi 3, as part of proper COM support. The OpenTools API had nothing to do with it, notwithstanding how the API was reworked later on to use interfaces.

      Just for the record…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s