Solved

How can I copy a record ?

Posted on 2003-11-03
14
3,060 Views
Last Modified: 2008-02-01
Hi!

How can I copy a pointer to a record into another variable of the same record type ?

For example, I have the following code which obviously doesn't work but might give you an idea of what I want to accomplish.

PSOME_RECORD = ^TSOME_RECORD;
TSOME_RECORD = record
  a:PChar;
  b:PChar;
  c: Cardinal;
end;

And then I have the following

function getRecord(offset: integer) : PSOME_RECORD;
begin
  //This function works correctly
  Result := PSOME_RECORD_Func(offset);
end;

var
  FRecords : array of TSOME_RECORD;
begin
  SetLength(FRecords, 10);
  for i=0 to 9 do
    FRecords[I] := GetRecord(i)^;  <- this is the part I'm having problems with ...
end;

I know I can easily do this by copying every member explicitly, but since TSOME_RECORD might change at some time, I want a solution that will require the least maintenance possible.  I.E. copy the record-pointers directly into a records-array.

If the solution is simpler, FRecords can be switched to
FRecords : array of PSOME_RECORD, but the pointers must not point to the original result of GetRecord();

Thanks.
 

0
Comment
Question by:delphi112497
  • 5
  • 3
  • 2
  • +2
14 Comments
 
LVL 6

Accepted Solution

by:
swift99 earned 64 total points
ID: 9676703
FRecord can be an array of PSomeRecord as easily as an array of Records

Then your code becomes


var
  FRecords : array of PSOME_RECORD; <- this is the easy part ...   :o)
begin
  SetLength(FRecords, 10);
  for i=0 to 9 do
    FRecords[I] := GetRecord(i);  <- this is the easy part ...   :o)
end;
0
 
LVL 5

Assisted Solution

by:snehanshu
snehanshu earned 62 total points
ID: 9676732
delphi,
  If you want to "Copy" the "Contents" of the pointer to a record type, you could use
 FRecords[I] := TSOME_RECORD(GetRecord(i)^);

Here is my test case which shows that the contents of the pointer and the array are independent of each other:
Note: Record modified slightly for my convenience ;-)

  PSOME_RECORD = ^TSOME_RECORD;
  TSOME_RECORD = record
    a:integer;
    b:integer;
    c:integer;
  end;


procedure TForm1.Button2Click(Sender: TObject);
Var
  i: Integer;
  MyPtr: PSOME_RECORD;
begin
  For i := 1 to 10 do
  begin
    MyPtr := GetRecord(i);
    RecAry[i] := TSOME_RECORD(MyPtr^);//copy values contained in the pointer
    RecAry[i].a := RecAry[i].a * 10;//change array

    //pointer contents remain unchanged:
    Showmessage(Format('ary: %d, Ptr: %d',[RecAry[i].a, MyPtr^.a]));

  end;

end;

function TForm1.GetRecord(i: integer): PSOME_RECORD;
begin
//create a new record for now
 Result :=  AllocMem(Sizeof(TSOME_RECORD));
 Result^.a := i;
 result^.b := i*100;
 result^.c := i*1000;
end;


Hope that helps.
...Shu
0
 
LVL 5

Expert Comment

by:snehanshu
ID: 9676755
Well, here's the complete code:

unit EnumUnit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, TypInfo;

type
  PSOME_RECORD = ^TSOME_RECORD;
  TSOME_RECORD = record
    a:integer;
    b:integer;
    c:integer;
  end;

  TForm1 = class(TForm)
    Button2: TButton;
    procedure Button2Click(Sender: TObject);
    function GetRecord(i:integer) : PSOME_RECORD;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  RecAry: Array [1 .. 10] of TSOME_RECORD;//I had missed this in the earlier post :-)

implementation

{$R *.dfm}

procedure TForm1.Button2Click(Sender: TObject);
Var
  i: Integer;
  MyPtr: PSOME_RECORD;
begin
  For i := 1 to 10 do
  begin
    MyPtr := GetRecord(i);
    RecAry[i] := TSOME_RECORD(MyPtr^);
    RecAry[i].a := RecAry[i].a * 10;
    Showmessage(Format('ary: %d, Ptr: %d',[RecAry[i].a, MyPtr^.a]))

  end;

end;

function TForm1.GetRecord(i: integer): PSOME_RECORD;
begin
//
 Result :=  AllocMem(Sizeof(TSOME_RECORD));
 Result^.a := i;
 result^.b := i*100;
 result^.c := i*1000;
end;

end.

Cheers!
...Shu
0
 

Author Comment

by:delphi112497
ID: 9676826
swift99,
your code stores pointers ... not the actual values.  If I later free all the memory involved in GetRecord(), FRecords will be affected also :(

snehanshu,
your code is basically what I ended up with, and everything works except for the null terminated PChars :(  ... is there any way I can copy the PChars also ?  Besides Cardinal and PChar, I also have an enum member ... the real record looks something more as:

type
  my_enum_values = (type1, type2, type3 ... typen);

TSOME_RECORD = record
  a: Cardinal;
  b: PChar;
  c: my_enum_values;
end;

I don't think there's any problem with 'c', but the PChar just messes things up :(

The reason for all this is TSOME_RECORD and GetRecord come from a DLL and the DLL uses too much memory;  I must free it as soon as I'm done with it and FRecords[] is used all through out the application.

Thanks for helping me out with this!
0
 

Author Comment

by:delphi112497
ID: 9676898
I am willing to increase the points to 500 if somebody gives me a good solution ...

I.E.  copies the records as specified above
frees FRecords from memory (including all of its PChar members) without explicitly making any name reference to them.
0
 
LVL 5

Expert Comment

by:snehanshu
ID: 9676904
delphi,
  Handling pointer record members is a tough one! I am not sure I know any workaround for that :-(
  I shall try to find out, meanwhile, hopefully the better experts might help.
...Shu
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 

Author Comment

by:delphi112497
ID: 9676921
snehanshu, after thinking the problem for a little longer I came to the conclusion it will require A LOT of memory handling and will only make things much harder to debug unless perfect code is provided.

I don't beleive this question will ever be answered to my satisfaction, but I do appreciate the effort and code you provided in your posts.

For the time being I will "hard-copy" each element of the record and will keep the question open.

Thanks for your help anyways.
0
 
LVL 5

Expert Comment

by:snehanshu
ID: 9677164
delphi,
  I am not sure what's happening here, but it seems that Delphi makes the pointer thing would perhaps work just as you like :-)
  Check this:

  PSOME_RECORD = ^TSOME_RECORD;
  TSOME_RECORD = record
    a:integer;
    b:integer;
    c:integer;
    MyProb: PChar;
  end;

...

procedure TForm1.Button2Click(Sender: TObject);
Var
  i: Integer;
  MyPtr: PSOME_RECORD;
begin
  For i := 1 to 5 do
  begin
    MyPtr := GetRecord(i);
    RecAry[i] := TSOME_RECORD(MyPtr^);
    RecAry[i].a := RecAry[i].a * 10;
    RecAry[i].MyProb := 'Snehanshu';//Array says Snehanshu

//Snehanshu and Hello both still remain
    Showmessage(String(RecAry[i].MyProb) + String(MyPtr^.MyProb));
    FreeMem(MyPtr);
    Showmessage(String(RecAry[i].MyProb){ + String(MyPtr^.MyProb)});

//    Showmessage(Format('ary: %d, Ptr: %d',[RecAry[i].a, MyPtr^.a]))

  end;

end;

function TForm1.GetRecord(i: integer): PSOME_RECORD;
begin
//
 Result :=  AllocMem(Sizeof(TSOME_RECORD));
 Result^.a := i;
 result^.b := i*100;
 result^.c := i*1000;
 result^.MyProb := AllocMem(10);
 result^.MyProb := 'Hello';//DLL says Hello
end;

Cheers!
...Shu
0
 
LVL 5

Expert Comment

by:snehanshu
ID: 9677244
Sorry, that wasn't a proper example.
So, the answer is that you cannot get RTTI for records in Delphi so you cannot do what you want.
the only solution would be to write a wrapper class: but your code would have to be recompiled every time your record structure changes.
...Shu

procedure TForm1.Button2Click(Sender: TObject);
Var
  i: Integer;
  MyPtr: PSOME_RECORD;
begin
  For i := 1 to 5 do
  begin
    MyPtr := GetRecord(i);
    RecAry[i] := TSOME_RECORD(MyPtr^);
    RecAry[i].a := RecAry[i].a * 10;
    Showmessage(String(RecAry[i].MyProb) + String(MyPtr^.MyProb));//Shows SHUSHU
    RecAry[i].MyProb[0] := 'U';
    RecAry[i].MyProb[1] := char(0);
    Showmessage(String(RecAry[i].MyProb) + String(MyPtr^.MyProb));//Shows UU
  end;
end;

function TForm1.GetRecord(i: integer): PSOME_RECORD;
begin
//
 Result :=  AllocMem(Sizeof(TSOME_RECORD));
 Result^.a := i;
 result^.b := i*100;
 result^.c := i*1000;
 result^.MyProb := AllocMem(10);
 result^.MyProb[0] := 'S';
 result^.MyProb[1] := 'H';
 result^.MyProb[2] := 'U';
 result^.MyProb[3] := char(0);

end;
0
 
LVL 12

Assisted Solution

by:Lee_Nover
Lee_Nover earned 62 total points
ID: 9677940
well if your structures have pointers and you want to copy the "contents" of those pointers then there's no other way than 'hard copying' the values
if you used a class it'd be easier coz of RTTI
in your base class you'd just implement some general field copying method and that's it
note that all of your fields need to be published and the class must descend from TPersistent
0
 
LVL 33

Assisted Solution

by:Slick812
Slick812 earned 62 total points
ID: 9692196
hello delphi, I am puzzled by your methods, it seems that for information exchange between a DLL, you'd use more Pointer oriented code. .
I would declare an Array Type of your TSOME_RECORD, and a pointer type to that array type, then you would just get a Pointer to the first Member of the DLL array and then you can access the data from your PMyRecAry variable

PSOME_RECORD = ^TSOME_RECORD;
TSOME_RECORD = record
  a:PChar;
  b:PChar;
  c: Cardinal;
end;

   PMyRecAry = ^TMyRecAry;
   TMyRecAry = Array[Word] of TSOME_RECORD;

Maybe some code like this

var
  PMRAry : PMyRecAry; // this is a Global variable that you can use in several procedures
 High1: Cardinal;  // this is the High (max index) of the array in the DLL, you do not say how you get this

procedure SetArrayPointer;
begin
  PSOME_RECORD( PMRAry) := GetRecord(0);
// now  PMRAr will have the CURRENT DATA in it, even if the DLL changes it, I am assuming that the DLL array is at a static address
end;

procedure UsePMRAry;
var
Amt1: Cardinal;
Str1: String;
begin
// you can easily access the data, and it will be upto date if DLL change it
Amt1 := 0;
for i := 0 to High1 do
  begin
  Str1 := Str1 + ' ' + PMRArray1^[i].a;
  Amt1 := Amt1 +  PMRArray1^[i].c;
  end;
end;

- - - - - - - - - - - - - - - - - - - - - - -
now if you need to KEEP this data after the DLL frees it, then you will need to just copy it, I will guess that the DLL will have some way to let you know when the Data Memory is released (maybe you FreeLibrary)

procedure WhateverFreeMemoryWarning;
var
PMRAry002 : PMyRecAry;
begin
PMRAry002 := AllocMem((High1+1)*SizeOf(TSOME_RECORD));
CopyMemory(PMRAry002, PMRAry,  (High1+1)*SizeOf(TSOME_RECORD));
// now just change the original pointer and you can keep using the code in other procedures
PMRAry := PMRAry002;
end;

you will need to FREE THE MEMORY if you ALLOCMEM, maybe when you app closes With FreeMem(PMRAry, (High1+1)*SizeOf(TSOME_RECORD));, be sure NOT to call FreeMem(PMRAry); if you are still using the DLL memory

this procedure will now still work even after you change the Pointer PMRAry

procedure UsePMRAry;
var
Amt1: Cardinal;
Str1: String;
begin
Amt1 := 0;
for i := 0 to High1 do
  begin
  Str1 := Str1 + ' ' + PMRArray1^[i].a;
  Amt1 := Amt1 +  PMRArray1^[i].c;
  end;
end;

0
 
LVL 33

Expert Comment

by:Slick812
ID: 9692213
Str1 := Str1 + ' ' + PMRArray1^[i].a;

should be
Str1 := Str1 + ' ' + PMRArray^[i].a;
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

23 Experts available now in Live!

Get 1:1 Help Now