Jump to content

EugeneK

Members
  • Content Count

    66
  • Joined

  • Last visited

Posts posted by EugeneK


  1. 31 minutes ago, PeterBelow said:

    But that's the way it currently works! Of course you can design your own interface + implementor that acts exactly like anonymous methods act now, but that would still not be the same implementation the compiler creates behind the curtain now.

    I'm not sure you understand my point. What I'm saying is that Embarcadero can theoretically hide ability to implement this interface manually and all existing code that uses anonymous methods in documented way will still work. Or they can change interface, say, rename Invoke to InvokeEx for some reason.

    So manually implementing this interface is a hack that can randomly stop working. Although I agree that probability is low that they will change it.


  2. On 3/15/2024 at 2:11 PM, Remy Lebeau said:

    How would you propose doing it without using an interface?  The only way that makes sense to me is a managed record with a manual reference count (but managed records didn't exist yet when anonymous procedures were introduced).

    I'm not saying doing it without interface, I mean hiding this implementation detail from the users. I.e. not allowing to manually implement this interface in code. This won't break any documented uses of anonymous methods.


  3. 6 minutes ago, Dalija Prasnikar said:

    You don't understand. They are implemented as interface based class. Without that implementation they wouldn't work at all. It is just that compiler hides that fact from us and automatically creates everything needed.

    But this implementation does not have to be accessible as interface to end user for it to work.


  4. In Dalija Prasnikar's smart pointer unit here https://github.com/dalijap/code-delphi-mm/blob/master/Part5/SmartPointer/uSmartPtr.pas#L8  following code is used

     

      ISmartPointer<T> = reference to function: T;
    
      TSmartPointer<T: class, constructor> = class(TInterfacedObject, ISmartPointer<T>)

    basically anonymous method declaration is equivalent to interface.

    Is it documented somewhere? I tried looking at Interfaces and Anonymous method sections in documentation but can't find anything.

    • Like 1

  5. 1 hour ago, Dalija Prasnikar said:

    For providing different implementation for classes by using interfaces, it is not enough to add interface to the class, you need to change all signatures from class to interface. This is not backward compatible.

    I meant in my own code, for example in unit tests sometimes it is easier to provide mock of some class rather than constructing it.

     

    Quote

     

    TXmlDocument is an outlier.

     

    It is an outlier but it exists, which means it is possible, and I dread thinking what it would have been using it if it would not be interface based.

     

    Quote

     

    I beg to differ. Original code had a memory leak because of wrong assignment, but such leak is detectable and if you follow the leak you can find issue in the code. If you had IStrings, you would not have a leak, but you would still have a bug in your code because there is still a wrong assignment somewhere. Anything can be source of bugs, just the bugs and issues you have will be different.

    Well in my case it would have been an innocent bug because it was assigned just after creating, so no information was lost, but application would have been working instead of shutting down every few days.
     

    Quote

     

    I think the above code is simple enough. The name SmartPointer is rather long and it can be easily shortened.

     

    That's really nice, I wonder why they won't add something like this to RTL. But you agree that it is less readable than using interfaces.
     

    Quote

     

    Which also shows why you cannot insert different implementation as you need to typecast back to TStrings if you want to preserve backward compatibility.

     

    But why would you want to typecast back to TStrings? By backwards compatible I meant that existing code won't break and if you need to use TStrings you just use continue to use TStrings as before, but in new code if you declare it as IStrings you can pass it to procedures that accept IStrings.

     

    Quote

     

    I assume this is the ticket https://quality.embarcadero.com/browse/RSP-36609 I fully agree with the closure. Adding interfaces to TStrings for the sole purpose of having automatic memory management would only add burden to that class hierarchy while achieving nothing that cannot be done by smart pointers. What Bruneau Babet suggests is the right course of action, adding memory management from the within opens possibility for wrong usage and accidental errors. Using smart pointers (the examples from C++ and Rust are in line with above smart pointer example) is the way to go.

     

    Main purpose is separation of interface from implementation so you can use TMySuferfastStrings or TUnitTestThisClassStrings and your code won't need any changes, I guess I prefer way of C# where all collections are interfaced, rather than C++

     


  6. 5 hours ago, Dalija Prasnikar said:

    So if you want to add interfaces to some RTL class and change its memory management behavior, you cannot do that just by adding interface to its declaration and required reference counting methods and let the reference type decide the memory management. I mean in simple scenario you could, but such code would be very fragile. Following code works correctly when it comes to memory management, but if the reference counting is in any way triggered by second example (Test2) it would auto destroy the object instance.

     

    Of course you don't just add interface to declaration, some work is needed but all your description does not say that it can't be implemented in backwards compatible way. And doing it class by class is much easier than adding ARC to whole compiler.

     

    Quote

     

    So, if you want to have such class that can have its memory managed in dual way, you need to handle that through some additional parameter during instance construction (or through some special value of existing parameter, like it was done with TXmlDocument) that would prevent accidental reference counting from destroying the object prematurely. And then you would also have to pay attention on how you store such instance and that reference type matches passed parameter and associated memory management.

     

    All classes that take TComponent owner already need to be manually freed when passed nil as a parameter. There could be bugs because of double free/memory leak because of this too.

     

    Quote

     

    On top of that, you would need to declare interface for the instance public API because otherwise you would not be able to use any of the object's intended functionality. With TXmlDocument the whole functionality is already interface based, so exposing that through outer wrapper is trivial.

     

    Exactly! That's the whole point, it also has other advantages, like ability to provide different implementation for classes.

    And TComponent seems to already implement interface reference counting, so maybe all that is needed to add interface implementing public methods.

    Quote

     

    In real life scenarios, if you need to automatically manage some object instance, there are better ways to do that (through some kind of smart pointer implementations or similar) than by forcing interfaces on those base classes.

     

    You never really need automatically manage memory, you can always free it manually it just a big source of bugs. I think goal of compiler developers should be to give people options to avoid them and write simpler code. Just now I found 10 year old bug where TStrings object was assigned to already created object causing memory leak, this kind of bug is almost impossible to find and it would not be a problem if it was IStrings instead.

    For smart pointers I don't really see a way to use them as elegantly as interfaces, at least with current Delphi language.

     

    Quote

     

    I am not saying that sometimes such dual memory management is not useful, but that such instances are rare and adding such thing on majority of RTL classes would be waste of time and effort.

    I don't agree with this, but Embarcadero devs seem to agree, since they closed my ticket about it.

     


  7. 16 minutes ago, Dalija Prasnikar said:

    How would that be backward compatible? Classes that use reference counting have completely different memory management and require slightly different handling than ones that are not reference counted. Every line of code that would use such changed classes would have to be inspected and changed.

     

    This would be like having ARC compiler all over again, only without half of required changes being automatically implemented in the compiler. You cannot force ARC on existing code that is not written with ARC in mind. That does not work. It may work in some simple scenarios, but then when you get to slightly more complex code is all falls apart.

    I was thinking something like TXmlDocument/IXmlDocument, if you use TXmlDocument you continue using regular Create/Free or component ownership and if you use IXmlDocument it is reference counted.


  8. You can take a different approach, these fields are reflecting some query, since you declare all the fields I assume query is static. This means that you can generate Delphi code based on this query, that will declare and assign all the variables, so you won't have to write it yourself and in addition this will make it easy to add new queries, and if you change query compiler will tell you about errors in dependent code.


  9. Hi

     

    There is following code in the end of TCustomSslWSocket.TriggerSSLHandshakeDone

     

        Disconnect := FALSE;
        if Assigned(FOnSslHandshakeDone) then
            FOnSslHandshakeDone(Self, ErrCode, FSslPeerCert, Disconnect);
        if Disconnect and (ErrCode = 0) then
            Close;

     

    I wonder why last two lines not just

        if Disconnect then
            Close;

    User can't force disconnect on failed handshake?

×