Friday, June 25, 2010

Life before Interfaces

Way way back this is how IUnknown was defined before Delphi added the interface language feature:

IUnknown = class
{ IUnknown }
  function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall; abstract;
  function _AddRef: Integer; virtual; stdcall; abstract;
  function _Release: Integer; virtual; stdcall; abstract;

Here's IUnknown now:
IUnknown = interface
  function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  function _AddRef: Integer; stdcall;
  function _Release: Integer; stdcall;

There are so many advantages it's hard to list them all and now we just take them for granted, but here are a few advantages:

  • implement multiple interfaces on the same object
  • compiler support for lifetime management
  • assign an interface to a GUID


Bruner said...

They look almost the same to me. I looked at interfaces before and I just don't see the advantages.

You have to add the the weird syntax for the GUID, (that I've never understood it's purpose or usefulness). And you are calling functions, which are equivalent.

To me it just looks like a make-work project. You need to explain why this is better. Your reasons don't make sense.

1. implement multiple interfaces on the same object, is like saying "use interfaces so that you can use interfaces!" Why would I want to do it? Why couldn't I do the same with function calls?

2. Compiler suppport for lifetime management. You mean the compiler won't support stdcalls in the future? Is this just a reason you are giving as to why it's safe to use interfaces? If so, still don't see the advantage.

3. assign an interface to guid. Why would I want to do that? I've never done it in the past?

What is the point of posting on your blog if your not explaining what you mean. Anyone can post words. I expect better out of you since your are obviously knowledgeable.

CR said...

@Brunner - I'm not the blogger himself, but I can't for the life of me see the aim of this post as being to advocate the use of interfaces - rather, it's presupposing their use, presumably in a COM or COM-style context (hence the talk of IUnknown). If you think having COM-style interfaces baked into language is *not* a boon to COM programming, then with respect, you have no idea what you're talking about.

CR said...

Sorry, I meant Bruner. We all could read a bit more slowly I think...

FrancoisG said...

@Bruner. This post presupposes you know what Interfaces are and where they come from. It's not an introduction to Interfaces.

You can read about them on Delphi basics and then this post will make more sense:

Chris Bensen said...

@Bruner, my post was directed at COM, but I guess was vague enough I apologize for the confusion. I was looking at some stuff the other day and reminiscing how much tougher it used to be.

But you bring up some good general interface questions that people have written entire books about so I'm bound to miss a few things, so here are some answers off the top of my head.

Remember that interfaces are just another tool at your disposal when developing software. Programmers said the same thing about classes when functions was all they knew. Interfaces are about encapsulating elements of your program into well defined components for reuse. As with anything there is a time and a place to use these tools and at times using an interface can be overkill. It's all about known when to use the right tool. For example, why would you choose an enum over an integer with constant values? I prefer enums most of the time over an integer because it is self documenting and the compiler imposes the boundaries.

1. Interfaces allow for a common "interface" between two objects. It can be really handy if you are building a pluggable modular system, especially one that can grow! It is also handy if you have teams working on modules, now they can have well defined interface. You can do the same with a class but you only get one.

2. I'm not sure where you got from "compiler support for lifetime management" to "You mean the compiler won't support stdcalls in the future". When using interfaces the Delphi compiler is using a form of garbage collection so you don't always need to worry about freeing objects. There are cases, some I've written about before, where tricky things can happen so it isn't fool proof but it works out pretty good most of the time.

3. You'd have to use interfaces to appreciate the compiler magic behind assigning an interface to a GUID. Take a look at the Supports function. It gets you from one interface to another. If that interface is not supported then it returns false. Here is the prototype:

function Supports(const Instance: IInterface; const IID: TGUID; out Intf): Boolean;

Before the compiler support you called it like this:

if Supports(Foo, Goo_GUID, Goo) then …

You started with a Foo and ended up with a Goo. With the compiler support to assign an interface to a GUID you can call Supports like this:

if Supports(Foo, IGoo, Goo) then …

Very handy.

If you don't use interfaces it might be worth at least learning about them!

Mark said...

Bruner, I think the problem is a mismatch in expectations.

I suspect Chris's expectation was that most people would realise that this post was NOT an introduction to interfaces, but rather a reflection on the bad old days GIVEN our current experience with interfaces and their yummy goodness.

Your expectation seems to have been that this post would enlighten someone who knows jack about interfaces and their heritage.

Do you see the difference?

format71 said...

I have never been in COM-land, but I see interface as a key feature for breaking dependencies.

I hate the way they are in Delphi, though. The life-time management features does nothing but confuse and make things harder to use. I guess it is mandatory for COM-use, but I wished they had made it possible to choose. One base interface for com-use and one for regular use.

(And yes, I know it is possible to 'break' the feature by overriding addref and release...)

Mark said...

format71, I half agree with you.

I would love there to be a non-refcounted version of interfaces as that would make it much easier to architect our products. We tend to design a lot of pure virtual classes "at the bottom" and pass those around as parameters. I know you can stub-out the refcounting in _AddRef and _Release but it's not seamless thanks to the compiler trying to be helpful and calling the damn things anyway.

If we had non-refcounted interfaces an entire product could be specified via interfaces instead of pure virtual classes and my life would be bliss.

Note to all refcounting experts: I am willing to concede that having spent my entire programming career managing object lifetimes explicitly, I may just be too long in the tooth. But I do seem to have more problems with refcounted interfaces than plain vanilla classes.

Bruner said...

Thanks, Chris Benson for replying and enlightening me. I program for a wide variety of platforms, so I tend to use the least common denominator. To me com refers to an old style executable that loaded directly into memory. :)
I don't know if I'll bother with them since most of my programing is in C++ these days, (platform independence and all that). It looks like it's a very specialized tool that might be useful under windows, but I've got enough to keep in my head as it is. :)

(If I'm wrong and it's useful for multiplatform somehow, please let me know.).

Chris Bensen said...

@format71, @Mark, there are many problems with non-refcounted interfaces. First would be confusing having two different interface types. When looking at a piece of code which one do you have? I assume you wouldn't inherit from IInterface/IUnknown so in addition to not having AddRef and Release you wouldn't have QueryInterface or a GUID to make QueryInterface work, so you'd be stuck with casting and less type safety. So while it seems like a good idea in theory, in practice it isn't. There is so little overhead in the calls to AddRef and Release it shouldn't be a problem stubbing them out, and if you pass the interface as a const to methods then the compiler never increments the reference count.

Chris Bensen said...

@Bruner, interfaces can be very useful. As I said before their use just depends on what you are building and how scalable and modular you want to build it. At any rate it's worth knowing about them. Good luck!

Mark said...

Chris, I take your point about possible confusion over which flavour of interface one is dealing with. However, in my non-refcounted fantasy world I still inherit from IInterface and get to use "as" and QueryInterface for interrogation and type safety. The only thing done differently is lifetime management.

Where interfaces really come into their own (for us, at least) is providing support for third-party plug-ins to control our app. Basically all we do is pass out an application interface and the plug-in hooks into our notifications and whatnot. No special API design is required because plug-ins use the same framework as the rest of the app. This feature alone makes interfaces indispensable.

Post a Comment