Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I not pass an interface as const?

I recently came across (again) the Delphi compiler code-gen bug when passing an interface as const leaks a reference.

This happens if your method is declared to pass an interface variable as const, e.g.:

procedure Frob(const Grob: IGrobber);

and the fix is to simply remove the const:

procedure Frob(Grob: IGrobber);

I understand that const (and var, and out) allow you to pass items by reference. In the case of a structure this saves an argument copy; letting you instead simply pass the pointer to the item.

In the case of an Object/Pointer/Interface there is no need to pass by reference, since it is a reference; it already will fit in a register.

In order to never have this problem again, i went on a crusade. I searched all my source trees for:

const [A-Za-z]+\: I[A-Z]

And i removed about 150 instances where i pass an interface as const.

But there are ones that i cannot change. The TWebBrowser callback events are declared as:

OnDocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
                                    \___/
                                      |
                                      ?

Have i gone too far? Have i done a bad thing?

Edit: Or, to phrase it in a less "based on opinion" style question: are there any serious down-sides to not passing an interface as const?

Bonus: When Delphi does not (always) increment a interface reference count they are violating The Rules of COM:

Reference-Counting Rules

Rule 1: AddRef must be called for every new copy of an interface pointer, and Release called for every destruction of an interface pointer, except where subsequent rules explicitly permit otherwise.

Rule 2: Special knowledge on the part of a piece of code of the relationships of the beginnings and the endings of the lifetimes of two or more copies of an interface pointer can allow AddRef/Release pairs to be omitted.

So while it may be an optimization that the compiler can take advantage of, it has to do it correctly so as to not violate the rules.

like image 807
Ian Boyd Avatar asked Jun 24 '15 13:06

Ian Boyd


People also ask

Can interface be used for constants?

A Java interface can contain constants. In some cases it can make sense to define constants in an interface. Especially if those constants are to be used by the classes implementing the interface, e.g. in calculations, or as parameters to some of the methods in the interface.

Can an interface be passed as a parameter?

Yes, you can pass Interface as a parameter in the function.

CAN interface have constants PHP?

It's possible for interfaces to have constants. Interface constants work exactly like class constants. Prior to PHP 8.1. 0, they cannot be overridden by a class/interface that inherits them.

CAN interface have constants C#?

Apparently C# cannot define a constant associated with an interface.


1 Answers

This happens if your method is declared to pass an interface variable as const, e.g.:

procedure Frob(const Grob: IGrobber);

That's not quite right. In order for there to be a leak, you need for there to be nothing in the code that ever takes a reference to the newly created object. So if you write:

Frob(grob);

There there's no problem because the interface grob already has at least one reference.

The problem arises when you write:

Frob(TGrobberImplementer.Create);

In that scenario nothing takes a reference to the interface and so it is leaked. Well, it will be leaked so long as nothing in the implementation of Frob takes a reference to it.

Have I done a bad thing?

Well, that depends. I don't think anything particularly bad will come of what you have done. There is a downside in terms of performance because all of your functions that accept interface parameters will now have to add and release references, using implicit try/finally blocks. Only you can judge whether that matters.

The more significant issue relates to code that is outside of your control. You give

procedure OnDocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);

as an example. There's no issue there because you never call that method. It's an event handler that you implement. The framework calls it, and it passes an interface that is already referenced.

The real problem comes from methods declared in the RTL or any other third party code that you call. If you are calling the methods, and if they use const interface arguments then you can fall into the trap.

It's easy enough to work around, albeit tiresome.

grob := TGrobberImplementer.Create;
Frob(grob);

My reasoning to deal with the issue goes like this:

  1. There is a performance cost in passing interface parameters by value.
  2. I cannot ensure that every method that I call will accept interface parameters by value.
  3. Therefore, I embrace the fact that I need to deal with calling const interface parameters at least some of the time.
  4. Since I have to deal with it some of the time, and since I hate inconsistency, I choose to embrace dealing with it all of the time.
  5. Therefore, I choose to make all interface parameters in methods that I write be const.
  6. And consequently I make sure that I never pass an interface as an argument unless it is already referenced by a variable.
like image 107
David Heffernan Avatar answered Oct 02 '22 16:10

David Heffernan