Jump to content

santiago

Members
  • Content Count

    79
  • Joined

  • Last visited

  • Days Won

    2

Posts posted by santiago


  1. Wow Remy!
    Thank you for such a detailed answer. 🙂

    It is now crystal clear to me.

     

    I never use those types of casts with interfaced objects, but since I had seen them since day 1 when I got started with Delphi I assumed it was legit. And oddly enough they have been working fine for many years!!

    That is why I got so confused when I removed a redundant interface declaration and suddenly it stopped working.

     

    In the meantime I have already replaced several of the unsafe casts with safe casts.
     


  2. 13 minutes ago, Uwe Raabe said:

    It is not! A cast tells the compiler to treat the given memory address as if it were of the casted type. To get a supported interface out of a class instance one has to call QueryInterface, which in the end is what the AS operator does.

    Great! Thank you! Understood now. 🙂

    This means that we need to change all the locations in our legacy code that perform such casts. I am just surprised as to why we have not had any problems before that. They have been in there for ages....
    And some are called rather frequently.


  3. 2 hours ago, David Heffernan said:

    This question is basically like asking why TButton(ListBox) fails when ListBox is a TListBox. That's directly analagous to your cast.

    Am sorry David, but I don't quite follow.
    Variable Obj is an instance of TMyClass and is declared as IInterface.

    TMyClass does implement IMyInterface. So it should be a valid cast.

    However when we call a method the access violation occurs.

     

    We have some legacy source code that does such things:

    IWhatever(SomeVariable).AMethod;

    It has been running fine for years. The other day it broke because I removed an Interface declaration from a derived class, since the interface was already implemented in a base class.


    So basically we had:

     

    TDerivedClass = class(TBaseClass, ISomeInterface1, ISomeInterface2, ISomeInterface3)

     

    and I changed it to:

     

    TDerivedClass = class(TBaseClass, ISomeInterface2)


    because TBaseClass already implements ISomeInterface1 and ISomeInterface3.

    And suddenly

     

    ISomeInterface1(AVariable).SomeMethod;

     

    resulted in an access violation.

     

    Thank you!


  4. 8 hours ago, Kas Ob. said:

    Casting is not a language feature per se but more like a tool allowing the developer to twist the compiler arm into doing something it will not do by default, think of it like overriding, i am not saying it is totally bad or must not be used, on contrary it is useful and can short the code, and i used casting in many places, but there is many cases where it is become dangerous, like never between two managed types !, between simple types it is OK as long you know what you are doing, between simple type and managed one, here comes the danger because the compiler will not question you code and will generate code as to what it is built to do, specially for managed types, there is many magic (intrinsic) code involved and this might lead to all sort of problem, the worst of them all if it did work on you device and failed on other devices because may be the content of the stack is different, simply it will become unpredicted behaviour.

     

    Anyway, i suggest to look at the assembly to get the picture of the compiler magic involved with interfaces, and when you casted it manually instead of the right way (using "as" or by extracting using "Supports") the compiler didn't have a chance to correct your code, and the code was faulty there, in other word the compiler didn't see the need to put the needed code ( in this case didn't perform QueryInterface ) because you told to handle it as IMyInterface, while in fact obj is IInterface.

    Think of Interfaces like an attachments, these attachments are not referenced in the TObject VMT directly but in a table attached to the TObject/Class VMT, accessing them need some extra work and memory walk to see if they do exist and referenced in the object or not, also each interface have a reference to its object, meaning when you case using "as" on interface the compiler adds code to invoke search in the object attached to this interface and return the result, same as Supports or QueryInterface, something like reverse walk then search.

     

    No.

    Thank you Kas.
    What do you mean exactly by "managed types"?

    If I understand correctly it is not safe to use such casts with TInterfacedObjects that implement many interfaces.

    What puzzles me though is that in our source code we have several locations where we do precisely that:
     

    IWhatever(SomeVariable).AMethod;

    This has been used in our software for many years, way before I got started with Delphi.
    The other day I removed a redundant interface declaration from a derived class. The interface was already listed and implemented in a base class. And all of the sudden some tests started failing. The reason was that such unsafe casts which had been working properly so far, no longer did.

     

    Am I correct in saying that we must always use Supports or as when dealing with TInterfacedObjects and unsafe casts should be avoided unless you really know what you are doing?

     

     



     


  5. Hi there,

     

    I have the following interface and two simple classes. A base class and a derived class (super class).

      IMyInterface = interface
      ['{34408757-240F-4B63-A1CA-4B1FC3BF5072}']
        procedure DoesNothing;
      end;
    
      TMyBaseClass = class(TInterfacedObject, IMyInterface)
        procedure DoesNothing;
      end;
    
      TMyClass = class(TMyBaseClass)
      end;

     

    and wrote the following test method.
    I would like to understand why we get the EAccessViolation with the hard (unsafe) cast.

     

    procedure TMyDebugUnitTestCase.Test_MyTest1;
    var
      MyObj1: IMyInterface;
      MyObj2: IMyInterface;
      MyObj3: IMyInterface;
      Obj: IInterface;
    begin
      Obj := TMyClass.Create;
      CheckTrue(Supports(Obj, IMyInterface, MyObj1));
      MyObj1.DoesNothing;
    
      MyObj2 := Obj as IMyInterface;
      CheckNotNull(MyObj2);
      MyObj2.DoesNothing;
    
      MyObj3 := IMyInterface(Obj);
      CheckNotNull(MyObj3);
      MyObj3.DoesNothing;  // EAccessViolation -> Access violation at address 00000001 in module 'CoreTests.exe'. Read of address 00000001
    end;

    The problem also happens if I declare the derived class as follows:

     

      TMyClass = class(TMyBaseClass, IMyInterface)
      end;

     

    Is it necessary to explicitly list interfaces already declared in base classes in the super class?
    To me this just seems redundant. Am I correct here?
    I have heard from other developers saying that this is necessary, but being unsure as to why,

     

    Thank you!

     

     


  6. On 10/8/2020 at 4:54 AM, Marco Cantu said:

    We also have another not in the fix pack that we are rolling out early in a patch, affecting unit cache for large projects and causing compiler time to grow (dramatically!) over successive builds

     

    Marco,
    do you have an estimate when this patch will become available?
    I would like to give 10.4.1 another try. Currently we are still on 10.3.3.


  7. I use 10.3.3 with IDE Fix Pack and have not had that issue to such an extent.

    It has happened though that Code Insight has stopped working.

    If I remember correctly it works after a while or just by restarting the IDE. These incidents have been very few and far between, so for me it has not been much of an issue.

     

    I will pay more attention next time it happens.

     

    But I do use 10.3.3 + IDE Fix Pack on a daily basis on a very big project.

     


  8. I cleaned up one project (40K lines of code, ca. 80 units, depends on 21 projects).

    I cleared the 'Unit Scope Names' from the Project Options to be empty.

    I had to fix many compile errors (in 47 units) by using the full scoped name (e.g: System.SysUtils, instead of just SysUtils).

     

    Delphi Rio 10.3.3 (WITH IDEFixPack)

    Before the changes this project compiled in ca. 8 seconds

    After the changes it improved slightly to: ca. 7.7 seconds

     

    Delphi 10.4.1

    Before the changes: ca. 11.5 seconds.

    After the changes: 8.7 seconds.

     

    Delphi Rio 10.3.3 (WITHOUT IDEFixPack)

    Before the changes: 18.1 seconds

    After the changes: 14.5 seconds

     

    Ideally you should always use the fully scoped names.

    I would have never had thought that this would have such an impact on compile time...

    • Like 2

  9. On 10/8/2020 at 4:44 PM, Uwe Raabe said:

    The idea was to write all units in all uses clauses with their full names instead of relying on the value in Unit Scope Names.

    Now I follow you 🙂

    This is very actually very interesting. This is how the 'Unit Scope Names' settings look for our projects.

    If I understand correctly, the fewer unit scopes that are defined here, the faster the project will compile. Correct?

     

    I am certain we don't need all those entries. I guess no one has ever looked into it until now.

    Anyhow, I will try to eliminate the need for using Unit Scope Names.

     

    UnitScopes.jpg


  10. On 10/9/2020 at 1:35 AM, dummzeuch said:

    Is it possible to have UnitAlias empty? When I try that, it automatically gets reset with the ancient aliases for WinTypes etc. But I haven't tried that with recent IDEs.

    This is how it looks like for me (Delphi Rio 10.3.3).

    Unit Aliases is something I have never needed to use so far.

    Aliases.jpg

    • Like 1
    • Thanks 1

  11. I did some quick tests with our project.

     

    2.1 mio lines of code. (1 group project, consisting of 24 projects).

     

    10.3.3

    =1.08 min

     

    Disabling the UnitFindByAlias patch, makes it 20 seconds slower.

    This is similar to the build time using 10.4.1

     

    In the Delphi Compiler Options, UnitAlias is empty.

     


  12. 22 hours ago, Tom F said:

    Does your Ctrl-Tab do the same thing as Ctrl-b <Enter>?

    I was not even aware of  the 'Buffer List'.
    But yes indeed, the 'Buffer List' provides the same functionality. But is not as convenient to use.

     

    However, if you are used to the way the Ctrl+Tab shortcut works and behaves in different development IDE's you will not be very happy with the 'Buffer List'.

    I would have still developed the Ctrl+Tab plugin if I had been aware of the 'Buffer List' before.

     

    • Like 1

  13. On 7/10/2020 at 1:56 AM, David Heffernan said:

    I have noticed that the list of units that the plugin offers includes those that are loaded but not visible in the IDE. In my case this means units loaded because of visual form inheritance.

     

    So with a base form and a derived form, suppose that you load just the derived form. Well, both of the forms are offered by the plugin even though the base form isn't visible in the IDE. Is that intentional?

    Issue has been solved. 🙂

    • Like 1
    • Thanks 1

  14. 6 hours ago, David Heffernan said:

    I have noticed that the list of units that the plugin offers includes those that are loaded but not visible in the IDE. In my case this means units loaded because of visual form inheritance.

     

    So with a base form and a derived form, suppose that you load just the derived form. Well, both of the forms are offered by the plugin even though the base form isn't visible in the IDE. Is that intentional?

    Definitely not intentional. Will have a look. Thx for reporting. 🙂

    • Like 1

  15. On 7/7/2020 at 5:08 AM, David Heffernan said:

    I have one very minor suggestion. I wonder if it would be prudent to add a namespace to all your unit names to avoid potential clashes. For instance, one of your units is named Main and I bet there are other packages around that use that name. With a namespace prefix you sidestep any such pitfalls.

    Done 🙂
    Thx!

×