tags:

views:

1398

answers:

6

Hi guys!

I want create a Playlist control. I have a lot of information to display into a TStringList. I want to assign a record to TStringList.Objects instead of an object because so many objects may take a while to create/destroy. It also take a lot of RAM.

A record will be much faster and slim. How can I do that?

TYPE
 AMyRec= packed record
        FullName     : string[255];    
        RelativePath : boolean;        
        IsInvalid    : boolean;        
        InCache      : boolean;        
        etc
       end;
+1  A: 

you can using the record Pointer.

List.AddObject(MyRecord.FullName, @MyRecord);
Cesar Romero
No, you can't do that. The record is bigger than four bytes. Square peg, round hole.
Rob Kennedy
Yes you can. Every Tlist has a 4-byte Object you can use for any purpose. You can allocate your record and set this object as a pointer to the record so you can easily find it. So this is the correct answer.
lkessler
Wouldn't you need to make sure you put the record on the heap instead of the stack? If it is on the stack then it will be stale when it goes out of scope.
Jim McKeeth
That would be @MyRecord and would not work outside of the method since the record is tack based.
Lars Truijens
This will not work, because a record is not an object and not a pointer either. You would have to make sure the record is stored on the heap and assign a pointer to it to the list.
Thomas Mueller
Thomas, you can becouse you can add the pointer to the record, and is what I usually do.
Cesar Romero
A: 

allocate the memory for the records takes also time.

create your record and put the pointer to the objects in stringlist.

Bernd Ott
+6  A: 

You can use a TList to a Pointer of your record.

Eg:

Type    
PMyrec = ^AMyRec;

usage

var
   MyRec : PMyRec;
new(MyRec);
MyRec^.Fullname := 'test';
MyRec^.RelativePath := false;

etc

{ MyList is a List you have create elsewhere }

MyList.Add(MyRec);

You'll have to handle disposing of items from the list eg

Dispose(PMyRec(MyList[Index]));

To use an item from the list:

var
  MyRec : PMyRec;

PMyRec := MyList.Items[i];
txtBox.Text = PMyRec^.Fullname;

etc

KiwiBastard
Please make the following correction: Dispose(PMyRec(MyList[Index])); The type-cast is required so that Dispose correctly disposes of the contents of the record, such as AnsiStrings, dynamic arrays, and interfaces.
Rob Kennedy
Whoops - well spotted
KiwiBastard
+1  A: 

SORRY!!!! I wanted to say TStringGrid instead of TStringList. My mistake.

I have tried something like this now (based on KiwiBastard's example):

Type
 AMyRec= packed record
        FullName     : string[255];
        RelativePath : boolean;
        IsInvalid    : boolean;
       end;
 PMyrec = ^AMyRec;

procedure TPlaylst.Button1Click(Sender: TObject);
VAR MyRec1, MyRec2: PMyRec;
    PlaylistCtrl: TStringGrid;
begin
 {SET}
 new(MyRec1);
 MyRec1^.Fullname := 'test';
 MyRec1^.RelativePath := false;
 PlaylistCtrl.Objects[1,1]:= MyRec1;    <------ Compiler error: "Incompatible types"


 {GET}
 ...
end;


"allocate the memory for the records takes also time." quantendrehung

Yes, but isn't an object (even the most rudimentary one, TObject) larger than a record? When you have 10000 objects, you will feel the difference. Am I wrong? :)

Altar
Again, just do a typecast: PlaylistCtrl.Objects[1,1]:= Pointer(MyRec1); and the reverse to get the data back out: MyRec := TMyRec(PlaylistCtrl.Objects[1,1]);
Mike Sutton
An object is four bytes larger than the corresponding record. And Mike's second type-cast should be to PMyRec, not TMyRec.
Rob Kennedy
So, using records instead of objects will "eat" about the same amount of RAM?
Altar
you really should edit the original or create a new question. The correct answer to the original question has already been given. Stack Overflow works best when its single question, all possible answers.
skamradt
+1  A: 

Are you sure you are willing to allocate all those objects? By the look on the record structure it looks like you want an object per row - not per cell. To do that you have at least 2 options:

  1. (My favorite because of the freedom it gives) You use TDrawGrid instead and draw the content of your cell manually. It's really not that hard!
  2. You make an object that encapsulates this record. It's an easy one as well, like for example:

type
  TMyRec= packed record
    FullName     : string[255];
    RelativePath : boolean;
    IsInvalid    : boolean;
  end;
  TMyData = object (TObject)
  private
    FData: TMRec;
  public
    constructor Create(AData: TMyRec);
    property FullName: String read FData.FullName write FData.FullName;
    property RelativePath: Boolean read FData.RelativePath write FData.RelativePath;
    property IsInvalid: Boolean read FData.IsInvalid write FData.IsInvalid;
  end;

...

constructor TMyData.Create(AData: TMyRec);
begin
  FData := AData;
end;

Now whenever you want to hook up your data to the grid you just pack it into that object and you can then use the Objects collection.

Now instead of going through all that hassle just create an event handler for TDrawGrid.DrawCell like

procedure TMainForm.GrdPathsDrawCell(Sender: Object; ...);

use GrdPaths.Canvas.Handle with DrawText or if Unicode is needed use DrawTextW (both come from Windows API so there's tons of examples of how to use it) and you'll save you and your client a lot of frustration, memory and above all - time.

Matthias Hryniszak
A: 

Doesn't creating a large number of objects (over 30000) take more memory than creating the same amount of records?

Altar