dormky 2 Posted December 11, 2023 I have a very, very large record that may cause memory issues for the stack if I use multiples instances of it in a procedure. I'd like to force delphi to allocate it on the heap so this doesn't happen. I need it to stay a record, it can't be a class. I have no problem with doing a bit of manual work in the procedure to get this, it doesn't have to be something baked in the framework and invisible to me as a dev. I'm sure this is possible in a simple way, I just can't find it through Google... Thanks ! Share this post Link to post
Alexander Sviridenkov 357 Posted December 11, 2023 var p: PMyRec; New(p); Share this post Link to post
dormky 2 Posted December 11, 2023 That does simple to be the simple thing I was looking for, thanks 🙂 How do I free this though ? Share this post Link to post
Alexander Sviridenkov 357 Posted December 11, 2023 Just now, dormky said: That does simple to be the simple thing I was looking for, thanks 🙂 How do I free this though ? https://docwiki.embarcadero.com/Libraries/Alexandria/en/System.Dispose Share this post Link to post
Cristian Peța 103 Posted December 11, 2023 With pointers like Alexander suggested or an array or records. Share this post Link to post
David Heffernan 2345 Posted December 11, 2023 9 minutes ago, dormky said: That does simple to be the simple thing I was looking for, thanks 🙂 How do I free this though ? This is all documented. Obviously we can tell you the answers but once you know about New, you can read the documentation to find out the rest of the details. It's really useful to know how to do that. 2 Share this post Link to post
dormky 2 Posted December 11, 2023 @Alexander Sviridenkov Perfect, thank you very much ! Share this post Link to post
pmcgee 10 Posted December 21, 2023 (edited) Wouldn't this be better expressed as a class containing a record? type TMyRec = record ... end; TMyRecC = class r : TMyRec; end; If C++ can declare "no raw pointers", then we certainly should avoid it in Delphi, right? This way you can maintain the usual convention with TMyRecC.Create and .Free, have constructor & destructor ... or use an interface ... or create your own smart pointer class ... Edited December 21, 2023 by pmcgee Share this post Link to post
David Heffernan 2345 Posted December 22, 2023 9 hours ago, pmcgee said: Wouldn't this be better expressed as a class containing a record? type TMyRec = record ... end; TMyRecC = class r : TMyRec; end; If C++ can declare "no raw pointers", then we certainly should avoid it in Delphi, right? This way you can maintain the usual convention with TMyRecC.Create and .Free, have constructor & destructor ... or use an interface ... or create your own smart pointer class ... How is this materially different? You still need to explicitly Free, and you need try/finally. The class is just extra baggage. For what gain? Share this post Link to post
pmcgee 10 Posted December 22, 2023 (edited) 4 hours ago, David Heffernan said: How is this materially different? You still need to explicitly Free, and you need try/finally. The class is just extra baggage. For what gain? I would say, in probably every language, the recommendation should be to code in a way that is familiar and idiomatic for that language. If we wanted stack-allocation and hands-off memory safety, then we can use records .. even custom managed records. But if we want to use heap memory, then I think it only makes sense to do it in the same idiomatic language as all our other Delphi code. I don't think it's a wild opinion in 2023 that pretty much nobody outside of C code should be cooking up raw pointers. At least C++ has unique pointer and shared pointer. I guess we could cook up a smart pointer record to look after the heap-allocated memory ... that would still be avoiding raw pointers. In summary, I think the lesson from the C++ ecosystem is: pointers can be ok ... but {owning, raw} pointers are something to avoid. Edited December 22, 2023 by pmcgee Share this post Link to post
PeaShooter_OMO 11 Posted December 22, 2023 (edited) 4 hours ago, pmcgee said: In summary, I think the lesson from the C++ ecosystem is: pointers can be ok ... but {owning, raw} pointers are something to avoid. Pointers should not be avoided and I highly suggest programmers should learn to use them properly and embrace them. They can make life so much easier. Edited December 22, 2023 by PeaShooter_OMO 1 Share this post Link to post
dummzeuch 1505 Posted December 22, 2023 34 minutes ago, PeaShooter_OMO said: Pointers should not be avoided and I highly suggest programmers should learn to use them properly and embrace them. They can make life so much easier. Pointers have their uses, but they come with risks. Skilled programmers know how and when to use them, but most important, they don't use them when they are not necessary. They also know that pointers can make debugging hell. Unfortunately there are many programmers who use pointers where they are not necessary. Of course they still think they are skilled, because they use pointers. Share this post Link to post
Dalija Prasnikar 1396 Posted December 22, 2023 5 hours ago, pmcgee said: I don't think it's a wild opinion in 2023 that pretty much nobody outside of C code should be cooking up raw pointers. Typed pointers are quite different from raw pointers. Both class and pointer in make sense, but which one you will chose, pretty much depends on the other code in the context. Declaring a class merely for a single procedure might be an overkill. From the safety perspective typed pointers in Delphi are equally safe or unsafe as classes. You still have to manage their memory and pay attention to what you are doing with them. 1 Share this post Link to post
pmcgee 10 Posted December 22, 2023 4 minutes ago, Dalija Prasnikar said: From the safety perspective typed pointers in Delphi are equally safe or unsafe as classes. You still have to manage their memory and pay attention to what you are doing with them. Yes. I was arguing from a consistency and code-style view point. Quote Typed pointers are quite different from raw pointers. I don't agree. I don't think it is the generally accepted meaning of 'raw pointer'. Maybe you are thinking of 'void pointer' ? >> A raw pointer is a pointer whose lifetime isn't controlled by an encapsulating object, such as a smart pointer. A raw pointer can be assigned the address of another non-pointer variable, or it can be assigned a value of nullptr. Microsoft Learn - Raw Pointers Share this post Link to post
Dalija Prasnikar 1396 Posted December 22, 2023 14 minutes ago, pmcgee said: I don't think it is the generally accepted meaning of 'raw pointer'. Well, in Delphi we commonly use phrases untyped and typed pointers. so when you say raw pointer in Delphi context my immediate association is untyped pointer, especially since your next comparison with C++ unique pointer and shared pointer which are automatically managed was completely unrelated feature to Delphi objects which are not automatically managed. So I assumed that you wanted to say untyped, as I haven't made the connection with the rest and what you are trying to say. I am still not certain what was your original point... as it seems that you were arguing for both style and memory management, otherwise why mentioning unique and shared pointer. I have chosen to disregard the automatic memory management part as this would be adding additional level of complexity and focus on the class part alone. 22 minutes ago, pmcgee said: Yes. I was arguing from a consistency and code-style view point. Again, that depends on the rest of the code. In general, if you are dealing with records, then using typed pointer to that record is more consistent and idiomatic than wrapping that record into a class, just to have it allocated on the heap. Especially, since Delphi no longer requires dereferencing typed pointers when working with their content. TRec = record x: Integer; end; PRec = ^TRec; TRecObject = class public r: TRec; end; var p: PRec; begin New(p); try p.x := 5; finally Dispose(p); end; end; var o: TRecObject; begin o := TRecObject.Create; try o.r.x := 5; finally o.Free; end; end If you look at the above code, the using pointer makes code more readable than having wrapped record as you can directly access its content through pointer reference instead of having another level of indirection between. 1 1 Share this post Link to post
David Heffernan 2345 Posted December 22, 2023 2 hours ago, pmcgee said: I don't agree. I don't think it is the generally accepted meaning of 'raw pointer'. Maybe you are thinking of 'void pointer' ? This is correct. Raw pointer in C++ is directly analogous to typer pointer in Delphi. However a reference to a class instance is actually no different from a raw pointer. You have to manage allocation and deallocation of both. Which is my point. 1 Share this post Link to post
pmcgee 10 Posted December 22, 2023 (edited) Yes. 100% I'm not arguing about it requiring management as is, ... or added structure to automate that. But of the code below, the new / dispose is (imo) ugly code that is unsuited to 2023, and to the long-term goal of regaining wider recognition of Delphi as a modern and relevant language. My standard rant / pedestal is that over the coming years we need to see Delphi improve it's language .. and it's practice ... to not be left behind by the general progress of other languages. Currently Delphi doesn't really qualify as a good teaching language any more - which I think is really sad. Without some more modern language facilities, it would be unfair to modern (say university level) students, and I'd like to see that change. begin var p:PRec := New(p); try p.x := 5; finally Dispose(p); end; end; begin var o := TRecObject.Create; try o.r.x := 5; finally o.Free; end; end Edited December 22, 2023 by pmcgee Share this post Link to post
Brandon Staggs 278 Posted December 22, 2023 (edited) 3 hours ago, pmcgee said: But of the code below, the new / dispose is (imo) ugly code that is unsuited to 2023, and to the long-term goal of regaining wider recognition of Delphi as a modern and relevant language. As long as we are talking about personal opinions, mine is just not moved much by this reasoning. Record pointers are nothing exotic or anachronistic in Delphi. That is a lot more common than having classes with no methods, imo. Also, there is nothing superior about a free call on a class compared to calling dispose on a record pointer. The Delphi language absolutely requires learning memory management. The removal of the misguided ARC baggage from the language shows that will not be changing. No reason to try to find ways to dumb it down. Edited December 22, 2023 by Brandon Staggs 4 Share this post Link to post
David Heffernan 2345 Posted December 22, 2023 4 hours ago, pmcgee said: But of the code below, the new / dispose is (imo) ugly code that is unsuited to 2023, and to the long-term goal of regaining wider recognition of Delphi as a modern and relevant language. begin var p:PRec := New(p); try p.x := 5; finally Dispose(p); end; end; begin var o := TRecObject.Create; try o.r.x := 5; finally o.Free; end; end The two versions are identical, apart from the double dereference with the class wrapper version. That's the only part that seems ugly to me. 1 Share this post Link to post
pmcgee 10 Posted December 22, 2023 (edited) I have no problem with pointer to record ... I have been pretty scathing about C-style function declarations that are not broken down into more readable sub-types. We could separate ownership from access with something like : begin var o := TRecObject.Create; begin var p:PRec := @o.r; // use p for whatever end o.Free; end That pointer could be copied, passed to functions, whatever ... and simply pass out of scope. It wouldn't have ownership of the data object, and has no responsibility to release it. [edit - forgot] Or of course, you can have methods in the class to control and implement the access to the data object. I think this also highlights that the outer wrapper can be something other than manually managed. Edited December 22, 2023 by pmcgee Share this post Link to post
Pat Foley 51 Posted December 22, 2023 4 hours ago, pmcgee said: Currently Delphi doesn't really qualify as a good teaching language any more - which I think is really sad. Without some more modern language facilities, it would be unfair to modern (say university level) students, and I'd like to see that change. // Define a record type as a data class data class cpRecord( var UI: TStringGrid, // Assuming this is a custom type var compute: TProc, // Assuming this is a functional type var Value: PDouble // Assuming this is a pointer type ) // Define a custom generic list as a subclass of MutableList class TcustomList : MutableList<cpRecord> { // Delegate the implementation to an ArrayList private val list = ArrayList<cpRecord>() // Implement the abstract methods of MutableList override val size: Int get() = list.size ... I think "port"able coding should be what is taught. Starting with Excel. Above code from a "Bing" prompt porting a Delphi TList<PRecord> to Kotlin. The "Precords" allow more shallow code on D jobs and should allow a port on the Android side. In the past one could simply use JS.evaluate(some script). Share this post Link to post
Dalija Prasnikar 1396 Posted December 22, 2023 44 minutes ago, pmcgee said: I have to problem with pointer to record ... I have been pretty scathing about C-style function declarations that are not broken down into more readable sub-types. I am not following. What function declarations have with records and record pointers we are talking about here? 44 minutes ago, pmcgee said: We could separate ownership from access with something like : begin var o := TRecObject.Create; begin var p:PRec := @o.r; // use p for whatever end o.Free; end That pointer could be copied, passed to functions, whatever ... and simply pass out of scope. It wouldn't have ownership of the data object, and has no responsibility to release it. You cannot separate ownership from access. The pointer no longer has the responsibility to release it, but instead of having one problem - one reference that needs to be tracked, managed and released. You now have two you need to pay attention to. Just because you only need to release one, does not mean that you can do just about anything with the other. And if you pass such pointer to the record out of scope, you have just created access violation. Once you free the wrapper object, your record pointer becomes dangling reference. I am not even going to comment about whole wrapping record into object and then working with pointer to that record instead of just using pointer and be done with it... 44 minutes ago, pmcgee said: [edit - forgot] Or of course, you can have methods in the class to control and implement the access to the data object. I think this also highlights that the outer wrapper can be something other than manually managed. Whether or not you would do all that depends on the context of the code you are dealing with. Wrapping object in a object or an automatically managed object could make sense, but it could also be the opposite. first you need to write such wrapper code and then you need to maintain it. Again, it could make sense in some scenarios where you want to add some additional functionality to a record you cannot otherwise change, but just for allocation it on the heap it doesn't. 1 Share this post Link to post
pmcgee 10 Posted December 22, 2023 3 hours ago, pmcgee said: I have no problem with pointer to record ... > I am not following. What function declarations have with records and record pointers we are talking about here? I just meant this type of situation where the C declarations are impenetrable, and Delphi is so much clearer: Share this post Link to post
David Heffernan 2345 Posted December 22, 2023 1 hour ago, pmcgee said: I just meant this type of situation where the C declarations are impenetrable, and Delphi is so much clearer: That's fair. But it's not the subject of this discussion. Share this post Link to post
JiyaHana 0 Posted January 2 On 12/11/2023 at 2:05 PM, dormky said: I have a very, very large record that may cause memory issues for the stack if I use multiples instances of it in a procedure. I'd like to force delphi to allocate it on the heap so this doesn't happen. I need it to stay a record, it can't be a class. I have no problem with doing a bit of manual work in the procedure to get this, it doesn't have to be something baked in the framework and invisible to me as a dev. I'm sure this is possible in a simple way, I just can't find it through Google... Thanks ! You can try with the GetMem function to dynamically allocate memory for the record. Share this post Link to post