Jump to content
dormky

How can I force a record to be allocated on the heap ?

Recommended Posts

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

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
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. 

  • Like 2

Share this post


Link to post

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 by pmcgee

Share this post


Link to post
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
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 by pmcgee

Share this post


Link to post
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 by PeaShooter_OMO
  • Like 1

Share this post


Link to post
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
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. 

  • Like 1

Share this post


Link to post
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
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.

  • Like 1
  • Thanks 1

Share this post


Link to post
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. 

  • Like 1

Share this post


Link to post

 

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 by pmcgee

Share this post


Link to post
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 by Brandon Staggs
  • Like 4

Share this post


Link to post
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. 

  • Like 1

Share this post


Link to post

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 by pmcgee

Share this post


Link to post
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
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.

  • Like 1

Share this post


Link to post
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:

 

image.thumb.png.db7ca4d560b0e7eef932a3946c5c3c28.png

Share this post


Link to post
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
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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×