Stefan Glienke 2002 Posted September 14, 2020 You are simplifying things here - yes some collections such as list<T> can easily give ref access to its items but other collections might not. Working around the fact that you are dealing with value type semantics by accessing items by ref (or even giving raw access to the storage array such as the List property in the RTL TList<T> does) is borderline imo. 1 Share this post Link to post
Uwe Raabe 2057 Posted September 14, 2020 It is not very difficult to make a generic list for records that allows manipulating the record content directly: type TChangeableRecordList<T: record> = class(TList<T>) type PT = ^T; private function GetItem(Index: Integer): PT; procedure SetItem(Index: Integer; const Value: PT); public property Items[Index: Integer]: PT read GetItem write SetItem; default; end; implementation function TChangeableRecordList<T>.GetItem(Index: Integer): PT; begin Result := Addr(PList^[Index]); end; procedure TChangeableRecordList<T>.SetItem(Index: Integer; const Value: PT); begin inherited Items[Index] := Value^; end; A suitable test case could look like this: type TMyRec = record IntValue: Integer; end; var list: TChangeableRecordList<TMyRec>; myRec: TMyRec; begin list := TChangeableRecordList<TMyRec>.Create; try myRec.IntValue := 1; list.Add(myRec); Assert(list[0].IntValue = 1); list[0].IntValue := 3; Assert(list[0].IntValue = 3); finally list.Free; end; end; 1 Share this post Link to post
Stefan Glienke 2002 Posted September 14, 2020 And there the trouble starts - why on earth should the byref property be writeable. Share this post Link to post
Lars Fosdal 1792 Posted September 14, 2020 In my experience, it is far easier to use classes when you need to modify contents. I wish it weren't so, and it can be worked around - but then usually with pointer references, and at that point (pun intended), I might as well be doing objects and maintain type safety and support polymorphism. Share this post Link to post
David Heffernan 2345 Posted September 14, 2020 14 minutes ago, Stefan Glienke said: And there the trouble starts - why on earth should the byref property be writeable. Yes, this should be a read only property. In an ideal world we'd have proper language support for references, as I think is especially we done in D foreach (ref elem; arr) { elem = 0; } 1 Share this post Link to post
Fr0sT.Brutal 900 Posted September 14, 2020 3 hours ago, Stefan Glienke said: And there the trouble starts - why on earth should the byref property be writeable. Why not? Share this post Link to post
Stefan Glienke 2002 Posted September 14, 2020 40 minutes ago, Fr0sT.Brutal said: Why not? Because the API now provides read by ref access which already makes it possible to changes values (which was the point) but also set by ref which does not store the ref but dereferences and assigns it. Also the regular Items property is now hidden and cannot be used. While it might be the easy solution it's not a good one in my book. In fact I think there is not even a good one unless language support. What prevents anyone from keeping around a reference that they retrieved from the Items property and then something in the list changes and boom. Share this post Link to post
Remy Lebeau 1394 Posted September 14, 2020 (edited) 12 hours ago, David Heffernan said: TList<T> The RTL's TList<T> class does not provide access to elements by reference in the TList<T>.Items[] property, only access by value. You would have to use the TList<T>.List property instead, which gives you access to the underlying dynamic array so you can access the raw element data directly. Edited September 14, 2020 by Remy Lebeau Share this post Link to post
David Heffernan 2345 Posted September 14, 2020 55 minutes ago, Remy Lebeau said: The RTL's generic TList<T> does not provide access to elements by reference, only by value. At least for its Items[] property. Its List property gives you access to the underlying dynamic array, so you can access the elements directly. Your third sentence directly contradicts the first sentence. Share this post Link to post
Remy Lebeau 1394 Posted September 14, 2020 4 hours ago, David Heffernan said: Your third sentence directly contradicts the first sentence. Not when you take the 2nd sentence into account. But whatever. I reworded my previous comment, just for you. Share this post Link to post
Uwe Raabe 2057 Posted September 15, 2020 14 hours ago, Stefan Glienke said: What prevents anyone from keeping around a reference that they retrieved from the Items property and then something in the list changes and boom. Isn't that just the same with classes? Of course one has to know what can be done and what should be avoided. That is no difference to the standard generic record list, where you need to know that you are changing content of copies of records and not that of the records itself. My approach (which may be more a proof of concept than a solution for production) allows to work with a generic record list just like with the standard one, with the feature to change the record contents in the list, just like it was a simple array of records. I am pretty sure that it will work for the majority of use cases. Whenever the language gets a new feature that makes this obsolete - so then. It is near to impossible (and probably not even desirable) to make everything fool proof. IMHO fools shouldn't be allowed to write programs in the first place. Share this post Link to post
Stefan Glienke 2002 Posted September 15, 2020 54 minutes ago, Uwe Raabe said: Isn't that just the same with classes? Nope - with classes you are dealing with reference types - in this case you are pointing to a location inside the dynamic array being used as storage inside the list - any reallocation can change that. 56 minutes ago, Uwe Raabe said: It is near to impossible (and probably not even desirable) to make everything fool proof. True - however there is a difference between a language level feature and simply giving out some pointers to memory not directly under your control. ref return in C# for example has very strict rules to avoid running into any kind of problems. Share this post Link to post
David Heffernan 2345 Posted September 15, 2020 1 hour ago, Stefan Glienke said: Nope - with classes you are dealing with reference types - in this case you are pointing to a location inside the dynamic array being used as storage inside the list - any reallocation can change that. In a collection which owns its members then removing an item leads to a stale reference in either scenario (classes vs records). Share this post Link to post
Uwe Raabe 2057 Posted September 15, 2020 17 minutes ago, David Heffernan said: In a collection which owns its members then removing an item leads to a stale reference in either scenario (classes vs records). If I got that right, Stefan is referring to the case where the array is relocated, which invalidates the record pointers. This is not the case for a class list, where only pointers to the class instances are stored inside the array. Relocating such an array will keep the instance pointers intact. Share this post Link to post
David Heffernan 2345 Posted September 15, 2020 1 hour ago, Uwe Raabe said: If I got that right, Stefan is referring to the case where the array is relocated, which invalidates the record pointers. This is not the case for a class list, where only pointers to the class instances are stored inside the array. Relocating such an array will keep the instance pointers intact. Granted the indirection that is offered by a reference type does make some of the issues hard to trip over, but they still exist. Share this post Link to post
FPiette 383 Posted September 15, 2020 1 minute ago, David Heffernan said: Granted the indirection that is offered by a reference type does make some of the issues hard to trip over, but they still exist. It is possible to overcome some of the issues by implementing a notification mechanism. Much like it is in any TForm for components it owns. Share this post Link to post
Fr0sT.Brutal 900 Posted September 15, 2020 21 hours ago, Stefan Glienke said: What prevents anyone from keeping around a reference that they retrieved from the Items property and then something in the list changes and boom. Ah yes right I haven't noticed the getter returning pointer to array item. In my record list class I use dynamically allocated records and store pointers only so have no such issues. Share this post Link to post
David Heffernan 2345 Posted September 15, 2020 1 hour ago, Fr0sT.Brutal said: Ah yes right I haven't noticed the getter returning pointer to array item. In my record list class I use dynamically allocated records and store pointers only so have no such issues. Well, you avoid some of the issues, but not all. For instance you don't avoid the issue where an item is deleted, but a stale pointer is retained. Additionally you end up with a large number of heap allocations, and memory that can be scattered which can impact performance. Share this post Link to post
Fr0sT.Brutal 900 Posted September 16, 2020 23 hours ago, David Heffernan said: Well, you avoid some of the issues, but not all. For instance you don't avoid the issue where an item is deleted, but a stale pointer is retained. Additionally you end up with a large number of heap allocations, and memory that can be scattered which can impact performance. Yes I know that but that's how things are. If someone's going to shoot himself in the leg, even the most safe code couldn't prevent it. I had no intention of creating a fool-proof class without any real necessity. TList<TObject> has all the same weaknesses but I haven't seen anyone complaining about them. As for allocations, they're not so massive and quite acceptable compared to array-of-large-blocks allocations and moves when the list is sorted. Share this post Link to post
David Heffernan 2345 Posted September 16, 2020 4 minutes ago, Fr0sT.Brutal said: As for allocations, they're not so massive and quite acceptable compared to array-of-large-blocks allocations and moves when the list is sorted. That trade off depends on the size of the record. For small records then performance is better if the items are stored directly in a contiguous array. Share this post Link to post
Fr0sT.Brutal 900 Posted September 16, 2020 6 minutes ago, David Heffernan said: That trade off depends on the size of the record. For small records then performance is better if the items are stored directly in a contiguous array. Sure. Anything depends on sizes, contents and use cases 🙂 Share this post Link to post