Jump to content

Stefan Glienke

Members
  • Content Count

    1365
  • Joined

  • Last visited

  • Days Won

    130

Everything posted by Stefan Glienke

  1. Good comments are not about *what* the code does but *why* because that is typically the question a future reader will have when they read the code. And experience teaches you to anticipate such questions and put comments where appropriate.
  2. Even Chuck Norris refactors his code (with a roundhouse kick though but still...)
  3. Stefan Glienke

    array of weak references

    Nope, that would be an array of unsafe references - an important characteristic of a weak reference is that it will be cleared when the referenced instance gets destroyed. Since you cannot declare TArray<[weak]IInterface> or something like that you need to make a record type with a weak reference field. Since Object ARC will be history with 10.4 I don't care - but weak reference for interfaces will still be a thing: {$APPTYPE CONSOLE} uses System.SysUtils; type weakref = record [weak] ref: IInterface; class operator Implicit(const value: IInterface): weakref; inline; class operator Implicit(const value: weakref): IInterface; end; class operator weakref.Implicit(const value: IInterface): weakref; begin Result.ref := value; end; class operator weakref.Implicit(const value: weakref): IInterface; begin Result := value.ref; end; procedure Test; var refs: TArray<IInterface>; weaks: Tarray<weakref>; intf: IInterface; begin SetLength(refs, 3); refs[0] := TInterfacedObject.Create; refs[1] := TInterfacedObject.Create; refs[2] := TInterfacedObject.Create; SetLength(weaks, 3); weaks[0] := refs[0]; weaks[1] := refs[1]; weaks[2] := refs[2]; refs := nil; Writeln(Assigned(IInterface(weaks[0]))); Writeln(Assigned(IInterface(weaks[1]))); Writeln(Assigned(IInterface(weaks[2]))); end; begin Test; end. FWIW Spring4D has Weak<T> that works for interfaces and objects and has the added feature that when used for objects on non ARC platform it also clears then and thus protects against dangling references.
  4. How about fixing the issues that FastMM4 has rather than inventing yet another newly mostly untested and unproven memory manager?
  5. Official one - shipped one does not have the ability to report the exact allocation callstacks or detect use after free. It's nice to see *that* you have a memory leak with the shipped one but not knowing where exactly it comes from (unless the class name gives a direct hint) is pretty useless.
  6. Stefan Glienke

    What are your compiler settings for debug builds?

    Just saying... RSP-16751
  7. Stefan Glienke

    What are your compiler settings for debug builds?

    Unfortunately there is not - iterating any RTL list in a proper way (for i := 0 to list.count-1) will execute range checking code (not the compiler inserted but explicitly coded) and even for x in list code does (check System.Classes.TStringsEnumerator.GetCurrent as example)
  8. Stefan Glienke

    What are your compiler settings for debug builds?

    Range checking is an interesting thing - lets look at the compiler output for the following code with range checking: procedure Test; var numbers: array of Integer; i: Integer; begin SetLength(numbers, 10000000); for i := Low(numbers) to High(numbers) do numbers[i] := i; end; Project376.dpr.18: for i := Low(numbers) to High(numbers) do 004CF162 8B45FC mov eax,[ebp-$04] 004CF165 85C0 test eax,eax 004CF167 7405 jz $004cf16e 004CF169 83E804 sub eax,$04 004CF16C 8B00 mov eax,[eax] 004CF16E 8BD0 mov edx,eax 004CF170 4A dec edx 004CF171 85D2 test edx,edx 004CF173 7C1B jl $004cf190 004CF175 42 inc edx 004CF176 33C0 xor eax,eax Project376.dpr.19: numbers[i] := i; 004CF178 8B4DFC mov ecx,[ebp-$04] 004CF17B 85C9 test ecx,ecx 004CF17D 7405 jz $004cf184 004CF17F 3B41FC cmp eax,[ecx-$04] 004CF182 7205 jb $004cf189 004CF184 E86F96F3FF call @BoundErr 004CF189 890481 mov [ecx+eax*4],eax 004CF18C 40 inc eax Project376.dpr.18: for i := Low(numbers) to High(numbers) do 004CF18D 4A dec edx 004CF18E 75E8 jnz $004cf178 Now without range checking: Project376.dpr.18: for i := Low(numbers) to High(numbers) do 004CF162 8B45FC mov eax,[ebp-$04] 004CF165 85C0 test eax,eax 004CF167 7405 jz $004cf16e 004CF169 83E804 sub eax,$04 004CF16C 8B00 mov eax,[eax] 004CF16E 8BD0 mov edx,eax 004CF170 4A dec edx 004CF171 85D2 test edx,edx 004CF173 7C0D jl $004cf182 004CF175 42 inc edx 004CF176 33C0 xor eax,eax Project376.dpr.19: numbers[i] := i; 004CF178 8B4DFC mov ecx,[ebp-$04] 004CF17B 890481 mov [ecx+eax*4],eax 004CF17E 40 inc eax Project376.dpr.18: for i := Low(numbers) to High(numbers) do 004CF17F 4A dec edx 004CF180 75F6 jnz $004cf178 The question is: does this code even need range checking? The answer is no - the loop range itself guarantees it. What if the compiler would notice if I mistakenly would write: for i := Low(numbers) to Length(numbers) do numbers[i] := i; instead of putting some code that slows things down and can optionally be turned off. Of course this is a simple example of an out of range error and there are others that are not easily noticeable at compiletime - but those that are the compiler (or static code analysis) could do a whole lot of potential error detection.
  9. Stefan Glienke

    Overloaded generic methods

    Overloads based on generic type constraint are not possible. Personally I would just make one method without constraint and use GetTypeKind(T) internally to decide if its tkInterface or tkClass and simply raise an exception if anyone put anything but a class or interface into T.
  10. Stefan Glienke

    Vcl Test Runner -> group by category

    There is - the second button from the left (the "fast forward" one) does that - it actually has two usages - if no tests are selected at all it does "Discover tests" - when any tests are selected it only runs those. However TestInsight does not display tests hierarchical but only grouped by type or fixture and I have no plans to change that.
  11. Stefan Glienke

    Dynamic Test creation possible?

    Yes but the real question is: why do you want to switch to DUnitX when you have working DUnit tests already?
  12. Stefan Glienke

    Memory Management with many objects

    I just scribbled something together that should be good enough to start with and get some basic information about how many objects are created at a given time - needs to be first or directly after the memory manager unit in the dpr: Replace the dictionary with a Spring4D one and you have some easy sorting and filtering on top 🙂 unit ObjectCounter; interface uses Generics.Collections; function GetObjectCounts: TDictionary<TClass,Integer>; implementation uses madCodeHook; type TObjectCounter = record class var ObjectCounts: TDictionary<TClass,Integer>; class var OldInitInstance: function (Self: TClass; Instance: Pointer): TObject; class var OldCleanupInstance: procedure (Self: TObject); class function InitInstance(Self: TClass; Instance: Pointer): TObject; static; class procedure CleanupInstance(Self: TObject); static; class procedure Init; static; class procedure Finalize; static; end; function GetObjectCounts: TDictionary<TClass,Integer>; begin Result := TObjectCounter.ObjectCounts; end; { TObjectCounter } class function TObjectCounter.InitInstance(Self: TClass; Instance: Pointer): TObject; var count: Integer; begin if ObjectCounts.TryGetValue(Self, count) then ObjectCounts[Self] := count + 1 else ObjectCounts.Add(Self, 1); Result := OldInitInstance(Self, Instance); end; class procedure TObjectCounter.CleanupInstance(Self: TObject); var count: Integer; begin if ObjectCounts.TryGetValue(Self.ClassType, count) then if count = 1 then ObjectCounts.Remove(Self.ClassType) else ObjectCounts[Self.ClassType] := count - 1; OldCleanupInstance(Self); end; class procedure TObjectCounter.Init; begin ObjectCounts := TDictionary<TClass,Integer>.Create; HookCode(@TObject.InitInstance, @InitInstance, @OldInitInstance); HookCode(@TObject.CleanupInstance, @CleanupInstance, @OldCleanupInstance); end; class procedure TObjectCounter.Finalize; begin UnhookCode(@OldInitInstance); UnhookCode(@OldCleanupInstance); ObjectCounts.Free; end; initialization TObjectCounter.Init; finalization TObjectCounter.Finalize; end.
  13. Stefan Glienke

    This implementation is Thread-safe?

    What? Assignment of these three types consist of multiple operations - just look into _UStrAsg, _IntfCopy or _DynArrayAsg - hence they are not atomic.
  14. Stefan Glienke

    This implementation is Thread-safe?

    No, they are not - apart from alignment as David pointed out it depends on the data type - string, interface or dynamic array assignments for example are far from being atomic.
  15. Stefan Glienke

    This implementation is Thread-safe?

    Using a CS for many-read-few-write scenarios might be slow but only you can know and measure.
  16. Stefan Glienke

    Why can't I install this monospaced font in Delphi ?

    Version 1.0.1 now properly identifies as monospace.
  17. Stefan Glienke

    Null value for T

    In C# there is no Nullable<T> where T is a reference type (because the declaration is like this: public struct Nullable<T> where T : struct) unless you are using C# 8 and have enabled nullable reference types - and then there still is no Nullable<T> for any T that is not a struct but the compiler prevents null in a reference type unless you specify it as nullable. I am pretty much sure that if we ever get nullables in Delphi they don't implement such a feature as C# 8 does to prevent null/nil in reference types.
  18. Stefan Glienke

    Null value for T

    Sure, write your own compiler
  19. Stefan Glienke

    Null value for T

    It's a "magic" function implemented by the compiler without code in System.pas
  20. Stefan Glienke

    Why can't I install this monospaced font in Delphi ?

    It doesn't matter - they all look terrible in the Delphi IDE on High DPI
  21. I was not talking about making classes for UI controls but exposing your dataobjects in a way that you can bind them in some way to the UI. Personally I don't like LiveBindings that much - and I gave up on some MVVM-ish approach for Delphi. What works kinda well is going the DB aware approach with datasets (can use things like the TObjectDataSet from Spring4D or other similar ones - DevArt for example has also one) that just are adapters to non dataset/database data such as lists of objects. There are other approaches such as my TTreeViewPresenter that connects an IObjectList from Spring4D to a TVirtualStringTree to display and even edit data.
  22. I think in the DI book there is a chapter about injectables and creatables. Stuff that you bind to UI are usually creatables thus objects and should not have a problem. Binding interfaces is almost if not entirely impossible in a clean way because they simply don't have property RTTI.
  23. Stefan Glienke

    What's the best common folder for...

    I think so but I would rather consult the Windows documentation on roaming profiles to be sure.
  24. Stefan Glienke

    Bug in Delphi string behavior?

    The refCount is -1 because its a reference to a string literal - UStrClr being called because passing to out does not deallocate but still put nil into the passed variable. And fwiw the empty string also happens for WideString because then WStrClr is being called before passing as out. And while we are at it - yes AnsiString as well - LStrClr in that case.
  25. Stefan Glienke

    What's the best common folder for...

    There is no C:\Users\Public\Public Documents - that's just how they get displayed in the explorer - click into the address bar and you see its C:\Users\Public\Documents (by default - it can be anywhere else) Is the file ... for the program itself and ... only on this machine and for every user -> %programdata%\<program name> only on this machine and only for current user -> %localappdata%\<program name> available on other machines for the current user -> %appdata%\<program name> some document/file for the current user only -> %userprofile%\documents something that should be available for other users on this machine as well -> public documents (I think there is no variable by default for that, only for the public folder, but the public documents can actually stored somewhere else, I think registry holds that information)
×