Jump to content

Stefan Glienke

Members
  • Content Count

    1428
  • Joined

  • Last visited

  • Days Won

    141

Everything posted by Stefan Glienke

  1. I leave that as an exercise to anyone who needs it - I would not want to have it run through the virtual Get and GetObject functions for every iteration. As for "All that hacking"- it's only that one private field access. And even that could be done a little cleaner by using the helper with Self trick. I always use records for enumerators except when I don't šŸ˜‰
  2. program StringListHelper; {$APPTYPE CONSOLE} uses Classes, System.SysUtils; type TStringListItem = record str: string; obj: TObject; end; TStringListPairs = record private List: TStringList; type TEnumerator = record private List: TStringList; Index: Integer; function GetCurrent: TStringListItem; public function MoveNext: Boolean; property Current: TStringListItem read GetCurrent; end; TStringListAccess = class(TStrings) private FList: TStringItemList; end; public function GetEnumerator: TEnumerator; end; TStringListHelper = class helper for TStringList public function Pairs: TStringListPairs; inline; end; { TStringListPairs.TEnumerator } function TStringListPairs.TEnumerator.GetCurrent: TStringListItem; begin Result := TStringListItem(TStringListAccess(List).FList[Index]); end; function TStringListPairs.TEnumerator.MoveNext: Boolean; begin Inc(Index); Result := Index < List.Count; end; { TStringListHelper } function TStringListHelper.Pairs: TStringListPairs; begin Result.List := Self; end; { TStringListPairs } function TStringListPairs.GetEnumerator: TEnumerator; begin Result.List := List; Result.Index := -1; end; var sl: TStringList; begin sl := TStringList.Create; for var p in sl.Pairs do if p.str = 'foo' then Writeln(p.obj.ClassName); end.
  3. Wrong, string and object are in the same entry - the internal storage of a TStringList has an array of TStringItem which is a record which contains a string and an object. You simply find it like this: function findSomething( const aStringArg : string ) : TObject; var i: Integer; begin i := sl.IndexOf(aStringArg); if i >= 0 then Result := sl.Objects[i] else Result := nil; end; Make this a method in your own TStringListHelper and there you go. If you want easy and fast lookup of objects by string key, then use a dictionary.
  4. Second but without Egyptian begin/end.
  5. Stefan Glienke

    Maximum static memory

    I am sure you did not mean that as it came across because to me it sounded like an insult to any member of Delphi Praxis.
  6. Stefan Glienke

    Calling inherited in Destroy

    Should as in "it would be desirable" but the Delphi compiler does not do that. This is usually achieved by compilers by either automatically inlining calls and then noticing there is nothing in such call hence no call/code to emit at all or by applying WPO or LTO. Both are things the Delphi compiler does not do. The Delphi compiler does not inline inherited calls if the method is virtual which Destroy is.
  7. Most likely a sampling variation - the majority of time is spent on the div instruction inside the loop in the OneLoop routine and that did not change - and is not optimizable in any way. Quickly looking into the code that clang or gcc produce it looks pretty similar so I assume VC++ does the same.
  8. No, that only affects the ability to properly see the content of the list when debugging
  9. Most likely this: https://quality.embarcadero.com/browse/RSP-30870
  10. Stefan Glienke

    How to manage feature changes during release cycle?

    We used to do Git flow but recently changed to GitLab flow. It basically goes like this:
  11. Stefan Glienke

    Why empty dynamic arrays = NIL?

    Nope, depending on the target version I am writing "if StringVar.IsEmpty" šŸ˜‰
  12. Stefan Glienke

    Why empty dynamic arrays = NIL?

    Fixed it for you: const EmptyArray = nil; var numbers: TArray<Integer>; begin if numbers <> EmptyArray then Writeln('not empty'); end.
  13. Stefan Glienke

    Why empty dynamic arrays = NIL?

    Because a pointer to empty would be quite stupid. While you can technically build an allocated array with a capacity of zero with some hacking the RTL always cleans up the memory as soon as an arrays length becomes 0. Fun fact - checking against nil for the purpose of "is it empty or not" opposed to comparing Length to 0 is slightly faster: procedure test(const a: TBytes); begin if a <> nil then Writeln; if Length(a) > 0 then Writeln; end; CheckArray.dpr.12: begin 0041D57C 53 push ebx 0041D57D 8BD8 mov ebx,eax CheckArray.dpr.13: if a <> nil then 0041D57F 85DB test ebx,ebx 0041D581 740F jz $0041d592 CheckArray.dpr.14: Writeln; 0041D583 A1B4254200 mov eax,[$004225b4] 0041D588 E8CF7DFEFF call @WriteLn 0041D58D E8B270FEFF call @_IOTest CheckArray.dpr.15: if Length(a) > 0 then 0041D592 8BC3 mov eax,ebx 0041D594 85C0 test eax,eax 0041D596 7405 jz $0041d59d 0041D598 83E804 sub eax,$04 0041D59B 8B00 mov eax,[eax] 0041D59D 85C0 test eax,eax 0041D59F 7E0F jle $0041d5b0 CheckArray.dpr.16: Writeln; 0041D5A1 A1B4254200 mov eax,[$004225b4] 0041D5A6 E8B17DFEFF call @WriteLn 0041D5AB E89470FEFF call @_IOTest CheckArray.dpr.17: end; 0041D5B0 5B pop ebx 0041D5B1 C3 ret
  14. Stefan Glienke

    We use DUnitX and it discovers all our silly mistakes before release

    No, I took the state from SourceForge years ago and removed all the cruft that I did not need and refactored some things. Also due to the fragmentation of the project it was kinda hard to see what is actually the most recent one. I just learned that probably your fork is.
  15. Stefan Glienke

    Maximum static memory

    If your quicksort is taking 80% of the time then its implementation very likely is garbage. Worst case scenario for QuickSort is O(nĀ²) which is why usually one should not use pure QuickSort but IntroSort which mitigates against the worst case. Also depending on the type of the elements you are are sorting the swapping can be ridiculously expensive and in case you are using the generic RTL implementation from TArray it does not do that very well.
  16. Stefan Glienke

    ANN: DDevExtensions is now on GitHub

    Created PR https://github.com/ahausladen/DDevExtensions/pull/9
  17. Stefan Glienke

    RAD Studio 11 Alexandria is now available

    Has nothing to do with the debugger but with a refactoring to TList which I suggested: https://quality.embarcadero.com/browse/RSP-34681 (probably cannot see that as it still seems to be marked as beta report)
  18. Stefan Glienke

    The state of GExperts Support for Delphi 11

    To be honest - after your fixes, it's even worse than it was before under HighDPI. I was using GExperts during beta - it was not beautiful but most dialogs could be used. Now it looks like this:
  19. Stefan Glienke

    RAD Studio 11 Alexandria is now available

    You're welcome - it is just a slightly improved version of what Aleksandr Sharahov already wrote years ago. Surprisingly they never used his purepascal code which compiles to almost the exact same code as the x86 asm version was doing. There are more than a dozen other improvements in the RTL that I have worked on - I cannot fix their compiler but I can certainly improve some suboptimal RTL code.
  20. Stefan Glienke

    Maximum static memory

    I recommend SampingProfiler which simply works with either map file or td32 info in the exe. If you use the map2pdb Tool that Cristian linked you can use Intel VTune or uProf
  21. Stefan Glienke

    Maximum static memory

    I am absolutely putting my bet on this. The second issue after that will most likely be a non-cache-friendly access pattern. But you don't have to guess - run the code under a profiler and it will tell you the slow parts.
  22. Wait for a compiler fix? šŸ„
  23. Just to make sure: this offset depends on how many fields your object has and how many interfaces it implements - the IMT slots always follow after the fields. For more info take a look into "Delphi in a nutshell"
  24. Looks like a compiler defect - when changing this declaration: TOnMyIntfItemSelected<T: IMyIntfItem> = procedure(AItem: IMyIntfItem) of object; the code for TMyIntfItemA<T>.Select looks like this: List.Intf.pas.82: begin 007083E4 53 push ebx List.Intf.pas.83: if Assigned(FOnItemSelected) then 007083E5 6683781200 cmp word ptr [eax+$12],$00 007083EA 7411 jz $007083fd List.Intf.pas.84: FOnItemSelected(Self); 007083EC 8BD0 mov edx,eax 007083EE 85D2 test edx,edx 007083F0 7403 jz $007083f5 007083F2 83EAE8 sub edx,-$18 // this is where it turns Self into an IMyIntfItem, $18 is the offset where the interface method table pointer sits inside the object 007083F5 8BD8 mov ebx,eax 007083F7 8B4314 mov eax,[ebx+$14] 007083FA FF5310 call dword ptr [ebx+$10] List.Intf.pas.85: end; 007083FD 5B pop ebx 007083FE C3 ret but when it has the generic T parameter it looks like this: List.Intf.pas.82: begin 007083E4 53 push ebx List.Intf.pas.83: if Assigned(FOnItemSelected) then 007083E5 6683781200 cmp word ptr [eax+$12],$00 007083EA 740A jz $007083f6 List.Intf.pas.84: FOnItemSelected(Self); 007083EC 8BD8 mov ebx,eax 007083EE 8BD0 mov edx,eax // here it simply passes Self 007083F0 8B4314 mov eax,[ebx+$14] 007083F3 FF5310 call dword ptr [ebx+$10] List.Intf.pas.85: end; 007083F6 5B pop ebx 007083F7 C3 ret To explain this a bit more: when putting an interface type as generic type constraint this means for the compiler that the type you put for the generic type argument not only has to be that interface type but also that it can be a class that implements this interface. TMyIntfItemA<T> does this and thus satisfies the compiler when passing it to the argument of that event handler. However, inside the event handler, it is being treated as an interface and due to the lacking const parameter the compiler inserted an IntfAddRef call which blows up as the parameter that was passed was not really an interface reference but an object reference. Putting the const parameter makes it blow up a bit later though, namely when accessing Caption.
  25. If you don't want instances, then don't make it a class - this ain't Java.
Ɨ