Hello!

Hello!

[UPDATE]

I think I know now what was going on :)

There are several parts to this story:

1) Generic interfaces with GUID
2) A specialized interface WITH a different type as parameter*
3) Other specialized interfaces

TD;LR; It's a compiler bug.

The key to understanding the issue is this:

IEntityList = interface( IBaseEntity )
['{28832807-5D4E-430B-8545-AF6244E923A0}']
procedure Load( List: TRoArray;EstateID: String );
end;

IWebEstateUserList = interface( IEntityList )
procedure Load( var List: TWebEstateUserList;EstateID: String );
end;

The second interface has a var parameter that is declared like so:

Type

TWebEstateUserList = class( TROArray )
end;

The interesting bit is that even if I remove the GUID, the problem remains.
If an implementation class is NOT provided, then the unit compiles just fine.
Another issue is the fact that one Load has a var parameter and the other does not, essentially making them overloaded (even though that was not intended).
This, by the way, is quite likely why it was complaining for a missing implementation :)

Now, just why it would be looking at the earlier class and then also processing it while clearly inside comments that is not something I can explain. But at least we now know what was going on and why it behaved like that :)

[OLD POST]
Not sure what is going on here.
Here are my interfaces:

IBaseEntity = interface
['{95AE6EE1-CDBF-4B4D-BB34-61E5FF5A10CF}']

end;

IDBEntity = interface( IBaseEntity )
['{484D50B8-CFAD-4048-8479-0D424A62E5A3}']
procedure Save( Entity: T );
procedure Load( Entity: T );
end;

IPersistenceRegistry = interface
['{E5DE87BD-6994-4D9C-ACCB-7698601C2D6B}']
procedure Register( EntityPersistence: TInterfacedClass;Entity: TClass );
function FindPersistence( Entity: TClass ) : IBaseEntity;
end;

IWebEstate = interface( IDBEntity )
['{A4A5B3D7-53D8-4923-A4E0-EEC37E3E2C15}']
procedure Save( Entity: TWebEstate );
procedure Load( Entity: TWebEstate );
end;

IWebEstateUser = interface( IDBEntity )
['{96706068-184A-4F89-8464-5BF4771C1848}']
end;

IEntityList = interface( IBaseEntity )
['{28832807-5D4E-430B-8545-AF6244E923A0}']
procedure Load( List: TRoArray;EstateID: String );
end;

IWebEstateUserList = interface( IEntityList )
procedure Load( var List: TWebEstateUserList;EstateID: String );
end;

Then I also have my class declarations (in other units):

Base:
TEntityPersistence = class(TInterfacedObject,IBaseEntity, IDBEntity)
strict protected
procedure Insert( Entity: T );virtual;abstract;
procedure Update( Entity: T );virtual;abstract;
function IdIsEmpty( Entity: T) : Boolean;virtual;abstract;
procedure Save(Entity: T); virtual; abstract;
procedure Load(Entity: T); virtual; abstract;
end;

TEntityList = class( TInterfacedObject, IBaseEntity, IEntityList )
strict protected
procedure Load( List: TRoArray;EstateID : String );virtual;abstract;
end;

Nexus Datasets:
TnxEntityPersistence = class( TEntityPersistence )
strict protected
procedure PrepareSaveParams( Entity: T;AQuery : TnxQuery );virtual;abstract;
public
procedure LoadFromDataset( Entity: T;ADataset: TnxDataset );virtual;abstract;
end;

Concrete classes:

TWebEstatePersistence = class( TnxEntityPersistence,IWebEstate )
strict protected
procedure Insert( Entity: TWebEstate );override;
procedure Update( Entity: TWebEstate );override;
function IdIsEmpty( Entity: TWebEstate) : Boolean;override;
procedure Save(Entity: TWebEstate);override;
procedure Load(Entity: TWebEstate);override;
public
procedure LoadFromDataset( Entity: TWebEstate;ADataset: TnxDataset );override;
end;

TWebEstateUserPersistence = class( TnxEntityPersistence, IWebEstateUser )
strict protected
procedure PrepareSaveParams( Entity: TWebEstateUser;AQuery: TnxQuery );override;
procedure Insert( Entity: TWebEstateUser );override;
procedure Update( Entity: TWebEstateUser );override;
function IdIsEmpty( Entity: TWebEstateUser ) : Boolean;override;
procedure Save(Entity: TWebEstateUser );override;
procedure Load(Entity: TWebEstateUser );override;

public
procedure LoadFromDataset( Entity: TWebEstateUser;ADataset: TnxDataset );override;
end;

TWebEstateUserListPersistence = class( TEntityList, IWebEstateUserList )
strict protected
procedure Load( List : TROArray;EstateID: String );overload;override;
procedure Load( var List: TWebEstateUserList );reintroduce;overload;
end;

For some reason, the compiler complains that TWebEstateUserPersistence does not implement IWebEstateUserList.Load. Now, why does he
think it should? I see no reason for that, but this
doesn't mean there isn't one :)

My Berlin isn't updated (yet).

A


Comments

  1. Nicholas Ring, the compiler highlights TWebEstateUser and not TWebEstateUserList.

    ReplyDelete
  2. Your IWebEstateUserList has the following method:

    procedure Load( var List: TWebEstateUserList;EstateID: String );

    While your implementation class doesn't not have a method with this signature:

    procedure Load( List : TROArray;EstateID: String );
    procedure Load( var List: TWebEstateUserList );

    The first method, it doesn't have the correct first parameter type, and the second method, while the first parameter is correct, it doesn't have the second parameter.

    Possible solution: change the second method, by adding a second parameter, to be:

    procedure Load( var List: TWebEstateUserList; EstateID: String );

    ReplyDelete
  3. Andrea Raimondi Sorry - I was totally wrong with my first comment and I deleted it...

    ReplyDelete
  4. Andrea Raimondi You won't believe it but I am dealing with a very similar situation here :-) It's like I am looking at my code ....haha. In my code, the "strict" parameter confused the compiler. Can you try to remove it or move the Load method to public?

    ReplyDelete
  5. John Kouraklis moving both to public doesn't help. WTF?

    ReplyDelete
  6. Andrea Raimondi Looking at the code more thoroughly and if I don't miss anything, the TWebEstateUserPersistence brings the IDBEntity interface twice in the class and hence the confusion. I think that both Tnx.... and IwebEstateUser descent and carry IDBEntity interface

    ReplyDelete
  7. John Kouraklis, this is interesting: if I DELETE the IWebEstateUser interface ENTIRELY (AND remove it from the class declaration) then the message does not appear. IF I reinstate the interface BUT NOT reinstate the declaration, the message appears again. This must be a compiler problem.

    ReplyDelete
  8. Andrea Raimondi I think this is the problem. You have two different classes/interfaces descending from the same interface and because there are different GUIDs the compiler is expecting to meet the requirements for both classes/interfaces

    ReplyDelete
  9. Generic support is just broken with interfaces if you want to resolve a particular type via its guid. You need to explicitly create one interface type and guid per each specialized methods set.

    ReplyDelete
  10. So, I decided to entirely remove the TWebEstateUserPersistence declaration AND implementation.
    Rebuilt the project and the compiler is STILL stopping on its declaration (yes, IT IS COMMENTED).
    So, the compiler is trying to compile commented code. WHAT THE BLOODY HELL!!?!?!!!

    ReplyDelete
  11. A. Bouchez "Generic support is just broken with interfaces"...
    I totally disagree! The issue with the code above is generic interfaces should NOT have a GUID because one is generated automatically by the compiler when a specific interface type is created from the generic interface (even implicitly, if I remember correctly).

    So there is indeed a compiler bug, which is not raising a warning (or error) if you place a GUID after a generic interface, as that is a mistake.

    But overall it looks to me the compiler is handling this better than some developers thing -- which is a documentation issue, not a compiler one.

    ReplyDelete
  12. Marco Cantù Do you mean that the IBaseEntity in the code above should not have a GUID?

    ReplyDelete
  13. Andrea Raimondi I get this kind of behavior sometimes (especially with breakpoints). Save the project, clean it and then build it and...hopefully...it will work. :-)

    ReplyDelete
  14. John Kouraklis IBaseEntity is a regular interface, needs a GUID. IDBEntity and IEntityList are generic interfaces, they should not have a GUID, although I'm not 100% sure happens if they do.

    ReplyDelete
  15. Marco Cantù I see. You mean an interface with generics. Sorry I misunderstood. So, you are saying that this may confuse compiler and the correct course of action is not to use GUIDs in such cases

    ReplyDelete
  16. Marco Cantù Even you are not 100% sure what happens if they do have a GUID? What is sure is that you can't resolve the GUID at runtime, as I wrote. Sounds like a bit broken, to me, at least. ;)

    ReplyDelete
  17. Ok, he may not remember it off the top of his head now.

    ReplyDelete
  18. Andrea Raimondi So, we are certain now that interfaces with generics SHOULD NOT have GUIDs? I need to update my classes in that case

    ReplyDelete
  19. John Kouraklis I would say that given the current information that is very likely so if you ALSO have other weird/odd things in your code. I can't prove that GUIDs alone created that mess :) But given that Marco Cantù and Malcolm Groves both agree that it's a bad idea, I would update the classes if I were you :)

    ReplyDelete
  20. I can't comment on whether the guids caused any or all of Andrea's problems. Seems unlikely somehow.

    However, if you accept that a guid on an interface is meant to uniquely identify that interface, and if you go one step further and accept that IFoo and IFoo are different interfaces, then they shouldn't have the same guid.

    Should Delphi figure it out for me under the covers so I don't have to worry? That'd be great, but personally there are things I'd have higher on my generics wishlist than that :-)

    ReplyDelete
  21. Andrea Raimondi I will do this change I think although I haven't noticed any problems because of this. I, also, agree with Malcolm Groves that in your code, the GUIDs do not seem very likely to have caused the behaviour you observed

    ReplyDelete
  22. Malcolm Groves That's the question Malcolm: does the compile understand IFoo and IFoo as two different interfaces? Is this documented anywhere even the guidance not to provide a GUID with generic types?

    ReplyDelete
  23. Malcolm Groves I am openly saying that they are likely a contributor but you ALSO need other things "wrong". In other words, it may have worked with the GUIDs if there weren't other things as well. I also openly state that I can't prove this was a marking of GUIDs alone. However, I agree under your reasoning (which makes a lot of sense) that generic interfaces should not have a GUID. I'd love if Delphi gave a compilation error if your generic interfaces have GUIDs.

    ReplyDelete

Post a Comment