Link to home
Start Free TrialLog in
Avatar of delphi112497
delphi112497

asked on

How can I copy a record ?

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.
 

ASKER CERTIFIED SOLUTION
Avatar of swift99
swift99

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of snehanshu
snehanshu

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
Avatar of delphi112497

ASKER

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!
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.
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
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.
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
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;
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Str1 := Str1 + ' ' + PMRArray1^[i].a;

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