-
Content Count
1129 -
Joined
-
Last visited
-
Days Won
102
Everything posted by Dalija Prasnikar
-
The Case of Delphi Const String Parameters
Dalija Prasnikar replied to Mike Torrettinni's topic in RTL and Delphi Object Pascal
In theory concepts are one thing, compiler specifications and implementation details another. Unfortunately Delphi does not have formal compiler specification, and compiles implementation is often the only specification. Of course, there are always corner cases where it is hard to say whether some behavior is intended or it is a bug, but in this case - const parameters behaviors are pretty well defined. I don't think you fully grasp meaning of the above sentence. Yes, const means that you are not allowed to change parameter inside function and it does not necessarily imply passing by reference. But, you are forgetting that reference types consist of two parts - reference (that is the only part passed as parameter and cannot be changed) and associated content (data) that is allocated on a heap. Besides reference types, Delphi also has value types. And the optimization part of passing by reference is primarily for passing value types larger than register size. Since references (pointers) are not larger than register size, they will not be passed by reference, but by value. Code is not correct and it will fail, not because Value is somehow changed - it is not and it is fully compliant with definition of const parameters. Problem arises because being passed as const - where reference cannot be changed triggers reference counting optimization for reference counted types. Since parameter will not be changed, there is not need to take care of reference counting. Because reference counting is not triggered, original string (same would apply for any other reference counted type) where Value reference points to, will be destroyed when s1 is assigned new value - in that moment Value reference that was not changed will point to now invalid memory location. It is also important to note that actual reference count is maintained as part of heap allocated data - not inside reference variable. -
The Case of Delphi Const String Parameters
Dalija Prasnikar replied to Mike Torrettinni's topic in RTL and Delphi Object Pascal
I probably wasn't specific enough when I said there are no flaws in compiler. I meant only related to what @Kas Ob. was saying and his example does not have const parameters. Also, only object instances suffer from this particular bug. -
The Case of Delphi Const String Parameters
Dalija Prasnikar replied to Mike Torrettinni's topic in RTL and Delphi Object Pascal
You are mixing a lot of things here. First, Java has garbage collection, so you absolutely cannot compare it to what Delphi does. WriteItDown procedure is compiled independently of the call site - it has to cover all possible variants. So while your example only passes string literal that has special handling, WriteItDown still has to take care of situation where dynamically allocated string variable will be passed to it. Hence try...finally block. As for the rest is concerned I am not sure I am following what you want to say... what const are you talking about? WriteItDown does not have const parameter, so it increases and decreases reference count as expected. Again there is no flaw in compiler, is works exactly as intended. If you pass reference counted parameter as value it will trigger reference counting mechanism, if you pass it as const it will not. You can choose to make parameter const or not depending on what method does with it. And again special handling for string literals is required because compiler does not know what kind of string you passed in when you called the method. -
Is it really that bad to Use boolean parameters to make your functions do two things?
Dalija Prasnikar replied to Mike Torrettinni's topic in General Help
There are two problems with using Boolean parameters. First, is when like in Nick's example you are literally calling two separate functions to do something based on boolean parameter. If there is common logic shared inside the method, where making two distinct methods would violate DRY principle, then it is fine to have parameter that will make your method do two things. Another problem using Boolean parameters, when you have valid reason to make method do two things is that boolean on its own is very rarely descriptive. EnableDisableControls(True); Does the above call enable or disable controls? If you need to make method do two things, it is better to use enum with two values that will give you more descriptive code on the call site. In your example if the method would be called EnableControls, then using Boolean parameter would be self descriptive. It is pretty obvious what following line does: EnableControls(True); On the other hand boolean in following method makes code unreadable ListFiles(const APath: string; IncludeSubFolders: Boolean) Can you tell what following code does, unless you know ListFiles declaration by heart? ListFiles(Path, True); In such case enum, is much better option even though it would have only two states. ListFiles(Path, optIncludeSubFolders); -
The Case of Delphi Const String Parameters
Dalija Prasnikar replied to Mike Torrettinni's topic in RTL and Delphi Object Pascal
This is nature of reference types. It is possible to modify their content. Only reference is passed as constant, not the actual content. Being confused about reference types and how they are passed to the procedures is not Delphi specific thing. For instance, Java passes all parameters as values, but in case of reference types (objects) that does not mean content of the object cannot be changed, unless it is made immutable through some other means (for instance, not having setter or similar methods that would mutate content). That causes two misconceptions, first. that objects references in Java are passed by reference, not by value and second that otherwise mutable objects cannot be changed inside method. Of course, comparing to Java, Delphi has many more parameter modifiers and that certainly complicates situation and possible variations. -
The Case of Delphi Const String Parameters
Dalija Prasnikar replied to Mike Torrettinni's topic in RTL and Delphi Object Pascal
I wrote more elaborate post on const parameters https://dalijap.blogspot.com/2021/01/are-const-parameters-dangerous.html -
The Case of Delphi Const String Parameters
Dalija Prasnikar replied to Mike Torrettinni's topic in RTL and Delphi Object Pascal
This problem is pure ARC problem. One instance of something is destroyed when other one is created and assigned because const parameter did not trigger reference counting mechanism - this is why const is used as speed optimization in the first place. Same thing that happens with strings happens with reference counted objects, but it is easier to see what happens with objects. You need form with memo and button to test following code. IFoo = interface function GetCount: Integer; function GetNumber: Integer; end; TFoo = class(TInterfacedObject, IFoo) private FNumber: Integer; public constructor Create(ANumber: Integer); destructor Destroy; override; function GetCount: Integer; function GetNumber: Integer; end; constructor TFoo.Create(ANumber: Integer); begin inherited Create; FNumber := ANumber; Form1.Memo1.Lines.Add('Created ' + FNumber.ToString); end; destructor TFoo.Destroy; begin Form1.Memo1.Lines.Add('Destroyed ' + FNumber.ToString); inherited; end; function TFoo.GetCount: Integer; begin Result := FRefCount; end; function TFoo.GetNumber: Integer; begin Result := FNumber; end; procedure TForm1.Button1Click(Sender: TObject); var s1: IFoo; procedure Test(const Value: IFoo); begin s1 := TFoo.Create(456); Memo1.Lines.Add('Test Value ' + Value.GetNumber.ToString); end; begin s1 := TFoo.Create(123); Test(s1); end; Output is: Created 123 Created 456 Destroyed 123 Test Value 123 Destroyed 456 It is pretty obvious that object instance holding 123 is destroyed before its value is printed. Value.GetNumber.ToString is accessing dangling pointer - if the memory where object or string previously occupied is not reused, you may get expected value even though that instance is dead, but if memory is reused in the meantime you will read content of some other data. Also the whole thing might crash, or it might not. Basically you get undefined behavior. -
Delphi Event-based and Asynchronous Programming eBook complete version released
Dalija Prasnikar replied to Dalija Prasnikar's topic in Tips / Blogs / Tutorials / Videos
Thanks! About the delay... the final release was originally planned for December 11, but I had a minor accident on November 30, and ended up in ER, where I also tested positive for COVID! In less than a week whole family got infected. We all had mild symptoms and were left at home with no treatment., but living was pretty hectic for most of the following two weeks... Anyway, we managed to release it about 16 days later than expected (I already had a lot of it written before, just not fully ready for release) I would say that all this is behind us, as it is... but as you may have heard it... Croatia got hit by major earthquake today. This one was 50km from Zagreb where we're located, but it was 30 times stronger than the one that hit Zagreb in March. After 6.2 by Richter scale, local Nuclear Power Plant (Krsko) was shut down, and power is all but stable, which is why I may not be online as regularly as before in the coming days! -
Delphi Event-based and Asynchronous Programming eBook complete version released
Dalija Prasnikar replied to Dalija Prasnikar's topic in Tips / Blogs / Tutorials / Videos
It looks like the download link was not reset properly for the original version buyers. It should work now! If you're still having problems, please contact us through the website form at dalija.prasnikar.info. We may need additional information. -
Weak reference is dangerous
Dalija Prasnikar replied to vfbb's topic in RTL and Delphi Object Pascal
Well, there is really nothing to document because nothing is really thread safe in Delphi. Unless you want every single documentation page having "This is not thread safe" (I am exaggerating a bit, but this is generally true in 99.99%) -
Weak reference is dangerous
Dalija Prasnikar replied to vfbb's topic in RTL and Delphi Object Pascal
You use weak when you cannot guarantee that weak reference lifespan will be less or equal to original strong reference. For instance, let's pretend that TComponent is truly reference counted class, and that same goes for its descendants TEdit and TMenu. Edit has PopupMenu property that can be set to independent TMenu instance. Currently destroying such TMenu instance would leave dangling pointer in Edit PopupMenu property, but we have notification mechanism that notifies Edit that Menu will be destroyed and that Edit should clear reference to the menu. In imagined reference counting scenario, you could have popup menu marked as weak reference and you would not need any notification mechanism to clear that reference when PopupMenu instance is deleted because weak would zero out (clear) that reference when menu is destroyed. If you use unsafe instead of weak, then you would have dangling pointer in PopouMenu property. -
Weak reference is dangerous
Dalija Prasnikar replied to vfbb's topic in RTL and Delphi Object Pascal
No. While unsafe can also be used to break reference cycles, it cannot be used interchangeably with weak, meaning unsafe used in wrong place can lead to dangling pointers. Again, using weak is perfectly fine. If you read my book example again, you will see that you cannot even safely take strong reference from strong reference if original has been written to. So now what? Don't use interfaces at all because they are not thread safe. Don't use strings and dynamic arrays because they are not thread safe? -
Weak reference is dangerous
Dalija Prasnikar replied to vfbb's topic in RTL and Delphi Object Pascal
You are mixing apples and oranges. Weak references work fine and have their purpose when used in single thread scenario. Their purpose is not achieving some magical thread safety, but breaking reference cycles. I will say it again, assignment of any non trivial type in Delphi is not thread safe. Never has been. As soon as one thread is writing some data, all access to that data must be protected. There is no way around it. -
Weak reference is dangerous
Dalija Prasnikar replied to vfbb's topic in RTL and Delphi Object Pascal
Assignment operation in Delphi are not thread safe. If you have one thread writing and other threads reading, such access must be protected or you will get crashes or buggy behavior. You can safely write and read 32bit aligned simple types, in terms that such code will not cause any crashing, but such code will also not work as intended, unless you don't care about actual values stored. So, weak references in Delphi are not thread safe. Also even strong interface references in Delphi are not thread safe. So there is no bug here, just bad test case. Excerpt from my book Delphi Event-based and Asynchronous Programming (Part 5. Thread Safety) If you have multiple threads accessing - reading and writing - the same reference, one thread can easily interfere with the other. Let's say we have a shared interface reference `Data`, pointing to a previously created object, one thread that sets that reference to nil, and another thread that tries to take another strong reference from that one with an assignment to another variable `Tmp`. The first thread will execute the `_IntfClear` function that will result in object destruction. The second thread, trying to grab a strong reference preventing object destruction, will execute the `_IntfCopy` function: var Data, Tmp: IInterface; Data := nil; // Thread 1 -> _IntfClear Tmp := Data; // Thread 2 -> _IntfCopy If the `Source._AddRef` line from the `_IntfCopy` function executes before the call to `IInterface(P)._Release` manages to decrease the reference count to zero and subsequently calls the object's destructor, all is good. The second thread will successfully capture another strong reference to the object instance. However, the first thread can interrupt the second thread at the wrong moment, just after the `Source <> nil` check was successfully passed, but before `Source._AddRef` had the chance to increment the object's reference count. In that case, the first thread will happily destroy the object instance while the second thread will happily grab a strong reference to an already nuked object, and you can forget about happily ever after. Note: Besides interface references to objects, the reference counting mechanism is also used for other types like strings and dynamic arrays. Assignment of those types is also not thread-safe. -
WinUI in Delphi (planned for 10.5?)
Dalija Prasnikar replied to Der schöne Günther's topic in Windows API
64bit IDE is not question of easiness. It is also question of 3rd party libraries and design packages support. You cannot mix 32bit and 64bit DLLs so having 64bit IDE would mean everyone would need to provide their components and plugins as 64bit versions, too. Also, because you cannot mix 32bit and 64bit DLLs in a process, there is question of other parts of the Delphi IDE - this is not just Editor, but also compiler, linker, debugger... not everything is written in Delphi and porting any of the necessary parts to 64bit may not be straight forward. Easy part of 64bit transition is about Delphi applications, even though depending on your code you may have smooth or bumpy ride, too. I didn't have to do any adjustments for my code, because I never used any hacks around integer - pointers casting and I always had bitness explicitly specified for any data types I have used. So, generally most of transition to 64bit should be easy, but it also depends on specific projects and other libraries, plugins and interactions they might have and use. Scaling is bit different story... VCL Styles originally didn't have High DPI support, and when they were added to IDE theming in Rio they were still without High DPI support... now they have High DPI support, but tweaking the IDE to fully work under High DPI does require some work. This part would be the same with or without VCL Styles. I would first make IDE high DPI aware and then add styles... but that is me... why are some things done the way they are, I have no idea. -
Yes. It can have huge impact. If some feature is scheduled for improvement, that means it will be looked at, but the more work feature needs, the more chances is that some parts will not be done right or some accidental bugs might creep in. Also public API can change only at major releases. If some API gets designed in a wrong way, it must stay like that until the next major release. I personally don't use floating point much, so I don't know how broad are required changes to improve performance and whether this will be some compiler tweaking or changes involved require significant API changes, too. In any way, it is always hard to predict different use cases and most important pain points, so having feedback from people that are experts in the area and that know what they need is crucial. Subscription is needed for regular users, but there are also exceptions for cases where people are invited because they can provide valuable feedback in certain areas.
-
Why is public class method marked as used, while it's not actually used?
Dalija Prasnikar replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Runtime packages are not always ideal solution, so you have to weight your options. Just for fun... my installer application written in D7 is 1.1MB, same code recompiled in XE4 is 3.5MB, 10.4 4.7 MB. Windows 32bit Of course, no DLLs here... but even if you can argue that there are substantial differences and needed improvements between D7 and XE4, between XE4 and 10.4 when it comes to VCL new features were sparse, and certainly not large enough to justify size difference. I mean the whole darn setup in D7 with several forms fits into that difference (XE4-10.4) -
Why is public class method marked as used, while it's not actually used?
Dalija Prasnikar replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
If you have many DLLs with bloat is duplicated. Also code size is not something that should be observed from disk size perspective. How do you deliver that code also matters. If you need to make frequent updates to some remote place with poor or expensive Internet connection then every byte counts. Literally. -
Simple inlined function question
Dalija Prasnikar replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
DelphiCon schedule is totally unrelated to any release. It is just online Delphi conference. Previously we had CodeRage in about same timeframe. -
Extending string - How to call the default record helper?
Dalija Prasnikar replied to Incus J's topic in RTL and Delphi Object Pascal
There is feature request for expanding helpers functionality https://quality.embarcadero.com/browse/RSP-13340 -
I don't understand this CONST record argument behavior
Dalija Prasnikar replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
I agree with specific intent part. Not necessarily with always measuring (but this has tendency to push discussion in wrong direction). Measuring performance bottlenecks is important, but once you know that particular construct can cause bottleneck, you might want to avoid using it in performance critical code, even though you will not exactly measure how much of bottleneck that construct really is in that particular code. In other words, life is too short to measure everything, sometimes you just use what you know is the fastest code (with all downsides considered). I am using Items, when I am iterating over small collections in non-critical code. Yes, exposing internals is not the best practice in general. In case of TList it is necessary sacrifice because performance requires it. Why not something else - well you can use something else, but you are out of option when core RTL/VCL/FMX frameworks are concerned. For instance speed optimization of FMX framework described in https://dalijap.blogspot.com/2018/01/optimizing-arc-with-unsafe-references.html includes direct array access and using List property. With that optimization in just few places I got from "it barely scrolls" to "fast scrolling" on mobile applications. Yes, ARC also had huge impact in this case, but even without ARC compiler, you can still get better GUI performance on mobile apps with such optimization, and on low end devices every millisecond counts. -
I don't understand this CONST record argument behavior
Dalija Prasnikar replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
I think you are blowing this out of proportion. Understanding that accessing underlying array does not have range check error implemented is not nuclear physics. Nor is making sure that you don't screw up your indexes. If you expect some piece of code needs to be performant for any reason (called a lot, or operating on large data sets) you don't have to really measure every two lines of code to know how to make that part of code run faster. Why? I know that TList is not the most brilliant piece of code out there, but it is not that bad either. And iterating through dynamic array (List property) will not be any faster if you use some other collection. -
I don't understand this CONST record argument behavior
Dalija Prasnikar replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
It won't fully help you with accessing TList List property. Since List can have more items than actual number of items stored, you can access index at the end of array that will not trigger compiler range error, but you will be accessing items outside of TList Items range. -
I don't understand this CONST record argument behavior
Dalija Prasnikar replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Access through Items (GetItem) implements range check and throws exception if index is out of range. Using List gives you direct access to underlying dynamic array (which capacity may be larger than number of items) and there is no range check in place. If range checking does not bother you - because you know that you are looping through correct index range, then you can safely use List. -
Best type for data buffer: TBytes, RawByteString, String, AnsiString, ...
Dalija Prasnikar replied to Rollo62's topic in Algorithms, Data Structures and Class Design
Some common RTL string manipulation functions have several overloads - including RawByteString one. If you use such function on AnsiString, you will trigger automatic compiler Unicode conversion. Using RawByteString will just use appropriate overload. There might be other differences, I haven't been fiddling around 8-bit strings (writing new code, besides the one I already have) for at least 5 years now. None that I have found so far. I am using RawByteString for mixed ASCII/UTF8/binary data for 10 years now. No problems.