I have a record that contains a bunch of pointers that I would like to free.
type
FreeStruct = record
Addr1 : Pointer;
Addr2 : Pointer;
Addr3 : Pointer;
// ....
end;
So I would like to do something like:
var
MyStruct : FreeStruct:
begin
//MyStruct.Addr1 := ...;
for i := 0 to NumberOfFieldsInRecord do begin
VirtualFree (ValueOfFieldNumberOfMyStruct[I],0, MEM_RELEASE);
end;
end;
I heard there is something like RTII but I don't know how to use it properly with records and Delphi 7.
As far as I'm aware there are no methods to support record field iteration.
You can however play with the record structure on a low level basis (look here: http://www.delphigroups.info/2/18/295611.html)
Maybe you are better off restructuring your code so you will not need records for pointer storage.
There is no Rtti solution for your Delphi-7 version.
If you don't want to implement #ain's suggestion with an array of pointers inside a record,
here is a suggestion that uses a variant part in the record to accomplish a similar thing.
You can freely use myStruct.AddrX names for clarity in code, but also myStruct.allAddresses[X-1] to address the same field.
Type
FreeStruct =
record
intExample : Integer; // not in the variant part
case boolean of // Variant structure where the different parts overlap in memory
true : ( Addr1 : Pointer;
Addr2 : Pointer;
Addr3 : Pointer);
false : (allAddresses : array[0..2] of Pointer);
end;
procedure ClearFreeStruct( var AStruct : FreeStruct);
var
i: Integer;
begin
for i := 0 to High(AStruct.allAddresses) do
begin
// Dispose of allocated pointers
end;
end;
You can expand the structure with more pointers, but also include other fields (before the variant part).
It is possible to use an array of pointers within your record without giving up clarity.
By using an enumeration, it is possible to address each pointer with a relevant name.
Example: myStruct.Addr[peAddr1]
Type
PointerEnum = (peAddr1,peAddr2,peAddr3);
FreeStruct =
record
intExample : Integer;
Addr : array[PointerEnum] of Pointer;
end;
procedure ClearFreeStruct( var AStruct : FreeStruct);
var
pe : PointerEnum;
begin
for pe := Low(PointerEnum) to High(PointerEnum) do
begin
// Dispose(AStruct.Addr[pe]);
end;
end;
Related
I want to use SQL like condition in Delphi as
if VarI in SOMESET then
...
[SOMESET] to read from any text file.
Store few numbers in any text file like ini/txt and read it from file and replace it with set so that we can use in operator with if condition.
If I understand what you are asking correctly, the simple answer is yes. Your terminology may be a bit off - set means something quite specific in Delphi, but I don't think that is what you mean. Also, I don't think you are asking about the specifics of loading and saving so I have not included that. Instead I think you are asking how to allow 'in' to be defined in this specific case, and here is how
unit Unit1;
interface
uses
System.SysUtils;
type
TMySet = record
private
function GetEntry(const i: integer): integer;
public
Entries : array of integer;
// procedure LoadFromFile( const pFromFile : TFileName );
class operator in ( const pTest : integer; pMyset : TMyset ) : boolean;
property Entry[ const i : integer ] : integer
read GetEntry; default;
end;
implementation
{ TMySet }
function TMySet.GetEntry(const i: integer): integer;
begin
Result := Entries[ i ]; // allow default exceptions to occur
end;
class operator TMySet.in(const pTest: integer; pMyset: TMyset): boolean;
var
i: Integer;
begin
for i := Low(pMyset.Entries) to High(pMyset.Entries) do
begin
if i = pMyset[ i ] then
begin
Result := TRUE;
exit;
end;
end;
// else
Result := FALSE;
end;
end.
I hope I have understood your question, and that this helps.
Im trying to write some code which will read/write a dynamic array of objects to a file. The objects represent the structure of the java source code. I need to be able to scan the whole source code and gather information on Fields, Methods and Classes. I have an algorithm which does this and the result is kept in a structure of TFieldStruc, TMethodStruc and TClassStruc, all descendants of the TCoreStruc (a descendant of TObject). The java source code takes a couple of minutes to be scanned and have the virtual structure generated. Because of this my application scans all the source code once and saves it into a much more accessible format which is loaded when ever the IDE launches.
Is there a way (other than exporting the objects 'to string' and then re-creating them again when they are loaded) to stream the entire three arrays of TFieldStruc, TMethodStruc and TClassStruc, to a file so they can be read later?
I have tried reading and writing to a 'File of TFieldStruc..' and the TFileStream to save the objects to a file and read them back, but in both cases I get 'inaccessible value' in the debugger followed by an 'Access Violation' error as soon as the object is accessed again. If anyone has ideas on how to solve this problem it would be appreciated. Below is the code to TCodeStruc if any of its fields/methods may be causing issues:
type
TCoreStruc = class(TObject)
public
LowerPointer : integer;
HigherPointer : integer;
Line : integer;
Word : integer;
Char : integer;
CoreType : ansistring;
IsPublic : boolean;
IsPrivate : boolean;
IsStatic : boolean;
IsFinal : boolean;
Name : ansistring;
NestedStruc : TCoreStruc;
constructor Create(Name, CoreType : ansistring; NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
procedure UpdateValues(NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
procedure SetPosition(Line, Word, Char : integer);
end;
Here is an example using your structure.
A few notes about this:
There are lots of different ways to go about this. Take David Heffernan's advice and do some searches for serialization. I use the approach include below in one of my applications but other include using RTTI/Persistent objects to iterate the published properties of an object. There are libraries that will iterate object for you and do all the work.
You need to actually write out to the string the sizes of dynamic objects. That includes things like arrays and strings.
In my example each object knows how to read and write itself to the stream.
I use a struct for the fixed length parts of the object. That saves me from needing to write each data element individually.
Strings need to be written on their own to include their size (you could used a fixed length buffer like Delphi short strings but it is not that much work to write out a regular string. You need to decide what type of format you want string data written in. I picked UTF8 for my application.
For your other arrays you can write their data (including length) after the first array is written out. Sometime there will be a header section that includes the lengths for all the dynamic sections at the top, others will write the length write before the structure starts. The key part is to always write things in the same order and included somewhere it can be re-read how many there are.
There is no error checking or verification in the file structure below. If anything is different between the read and write it will blow up - probably with a stream read error.
Any change to the structures will cause old files to not be read properly. There are a number of ways to version a file to ensure you can still read old formats. Not included here.
In your application you would pass a TFileStream to the read and write function. I like to write the actual read/write functions with just a TStream. Then the object does not care where the data is coming from. It could be file, or it could already be in memory.
If you drop the following unit into a console application you should be able to add a call to Main and step through the example.
unit CoreStruct;
interface
uses Classes, Types;
type
TCoreStructData = packed record
LowerPointer : integer;
HigherPointer : integer;
Line : integer;
Word : integer;
Char : integer;
IsPublic : boolean;
IsPrivate : boolean;
IsStatic : boolean;
IsFinal : boolean;
HasNested: boolean;
end;
TCoreStruc = class(TObject)
private
FCoreData: TCoreStructData;
FNestedStruc : TCoreStruc;
procedure SetNestedStruc(AValue: TCoreStruc);
public
CoreType : String;
Name : String;
constructor Create(); overload;
procedure WriteToStream(Stream: TStream);
procedure ReadFromStream(Stream: TStream);
//constructor Create(Name, CoreType : ansistring; NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean); overload;
//procedure UpdateValues(NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
//procedure SetPosition(Line, Word, Char : integer);
property LowerPointer: integer read FCoreData.LowerPointer write FCoreData.LowerPointer;
property HigherPointer: integer read FCoreData.HigherPointer write FCoreData.HigherPointer;
property Line: integer read FCoreData.Line write FCoreData.Line;
property Word: integer read FCoreData.Word write FCoreData.Word;
property Char: integer read FCoreData.Char write FCoreData.Char;
property NestedStruc: TCoreStruc read FNestedStruc write SetNestedStruc;
end;
procedure Main();
implementation
function ReadUTF8StringFromStream(const Stream: TStream): String;
var
n: Integer;
Buffer8: Utf8String;
begin
Result := '';
Stream.ReadBuffer(n, SizeOf(n));
if n = 0 then
Exit;
SetLength(Buffer8, n);
Stream.ReadBuffer(Pointer(Buffer8)^, n);
Result := String(Buffer8);
end;
procedure WriteUtf8StringToStream(const Data: String; Stream: TStream);
var
Buffer8: Utf8String;
n: Integer;
begin
// When writing strings we need to make sure the length of the
// string is written out to the stream. That goes first so the
// reader knows how long the buffer is.
//
// You could you different formats to write to the file depending on
// needs. I like using UTF8 when writing out to file, but it does
// require an extra buffer copy when turning it back into a native
// Delphi unicode string.
Buffer8 := Utf8String(Data);
n := Length(Buffer8);
Stream.WriteBuffer(n, SizeOf(n));
Stream.WriteBuffer(Pointer(Buffer8)^, n);
end;
procedure Main();
var
Structs: array of TCoreStruc;
ArraySize: integer;
DataStream: TMemoryStream;
ArraySize_A: integer;
Structs_A: array of TCoreStruc;
i: integer;
begin
// Create and write some data
SetLength(Structs, 3);
Structs[0] := TCoreStruc.Create();
Structs[0].HigherPointer := 1;
Structs[0].Name := 'Testing';
Structs[0].NestedStruc := TCoreStruc.Create();
Structs[0].NestedStruc.HigherPointer := 100;
Structs[1] := TCoreStruc.Create();
Structs[1].HigherPointer := 2;
Structs[2] := TCoreStruc.Create();
Structs[2].HigherPointer := 3;
DataStream := TMemoryStream.Create();
// We need to start with the count we are writing out so
// the reader knows how many times to loop.
ArraySize := Length(Structs);
DataStream.WriteBuffer(ArraySize, SizeOf(integer));
for i := 0 to ArraySize - 1 do
begin
Structs[i].WriteToStream(DataStream);
end;
// Read the data into a new set of objects
DataStream.Position := 0;
DataStream.ReadBuffer(ArraySize_A, SizeOf(integer));
SetLength(Structs_A, ArraySize_A);
for i := 0 to ArraySize_A - 1 do
begin
Structs_A[i] := TCoreStruc.Create();
Structs_A[i].ReadFromStream(DataStream);
end;
end;
{ TCoreStruc }
constructor TCoreStruc.Create;
begin
Self.LowerPointer := 0;
Self.HigherPointer := 0;
Self.Line := 0;
Self.Word := 0;
Self.Char := 0;
Self.NestedStruc := nil;
end;
procedure TCoreStruc.WriteToStream(Stream: TStream);
begin
Stream.WriteBuffer(FCoreData, SizeOf(TCoreStructData));
WriteUtf8StringToStream(Name, Stream);
WriteUtf8StringToStream(CoreType, Stream);
if FCoreData.HasNested = true then
begin
FNestedStruc.WriteToStream(Stream)
end;
end;
procedure TCoreStruc.ReadFromStream(Stream: TStream);
begin
Stream.ReadBuffer(FCoreData, SizeOf(TCoreStructData));
Name := ReadUtf8StringFromStream(Stream);
Name := ReadUtf8StringFromStream(Stream);
if FCoreData.HasNested = true then
begin
FNestedStruc := TCoreStruc.Create();
FNestedStruc.ReadFromStream(Stream);
end;
end;
procedure TCoreStruc.SetNestedStruc(AValue: TCoreStruc);
begin
FNestedStruc := AValue;
if AValue = nil then
FCoreData.HasNested := false
else
FCoreData.HasNested := true;
end;
end.
If I am trying to call a procedure which has a record type (not object) as a parameter, is it possible to somehow pass details of that parameter "inline" without having to declare a variable of that type first?
eg assume I have this simple record type:
type TMyRecord = record
AString: string;
AnInt: Integer;
end;
and this procedure declaration:
procedure MyProcedure(Rec: TMyRecord);
If I want to call MyProcedure do I have to declare a variable of type TMyRecord or can I do something like:
MyProcedure(TMyRecord("Test", 10));
That doesn't work (XE2) (get a compiler error about it expecting a ")").
So, can I do something like that? Or not possible.
Thanks
It is possible using the advanced record structure.
For more information about advanced records, see the Records (advanced) section in Delphi help.
This is a small prototype to see how it works in your case to preinitialize a record in a function/procedure call :
Type
TRecord = record
AString : String;
AnInt : Integer;
Constructor Create( Const s : String; i : Integer);
end;
constructor TRecord.Create(const s: String; i: Integer);
begin
AString := s;
AnInt := i;
end;
procedure DoSomething( theRec : TRecord);
begin
WriteLn(theRec.AString, ' ',theRec.AnInt);
end;
begin
DoSomeThing( TRecord.Create('S',1));
ReadLn;
end.
Looking at the Delphi RTL, see the definitions of the record types TPoint and TRect in unit system.types (XE2).
They define some overloaded Create constructors, which are used in lots of places to preinitialize the record structures in function/procedure calls.
The question you are asking relates to code readability and there is a solution that avoids having to create a variable. The VCL uses this solution with the records TPoint and TRect.
Consider the definition of TPoint:
type
TPoint = record
X,Y integer
end;
To pass a TPoint to a procedure you might do:
var
MyPoint : TPoint;
begin
MyPoint.X := 5;
MyPoint.Y := 7;
DoSomething( MyPoint );
end;
This is fine but takes 3 lines when one is also possible using the factory function Point:
begin
DoSomething( Point(5,7) );
end;
In Delphi, a function has been declared as follows:
function Point( X, Y : integer ) : TPoint;
begin
Result.X := X;
Result.Y := Y;
end;
You can then call this function 'inline' to create the record 'on the fly' to to quickly
You will see the same has been provided for TRect etc. I often put such a factory function together with the record declaration as follows, even if I don't plan to use them yet:
type
TMyRecord = record
A : integer;
B : string;
end;
function MyRecord( A : integer; const B : string ) : TMyRecord;
begin
Result.A := A;
Result.B := B;
end;
Use of this technique can improved the readability of code and also ensures that you don't accidently omit setting a record element.
Just having fun with John Easley's idea:
type TRec = record
X: string;
Y: Integer;
end;
procedure TestRec(const Rec: array of const);
var
R: TRec;
begin
R.X:= string(Rec[0].VUnicodeString);
R.Y:= Rec[1].VInteger;
ShowMessage(R.X + IntToStr(R.Y));
end;
procedure TForm1.Button7Click(Sender: TObject);
begin
TestRec(['Test', 22]);
end;
It is possible to pass record fields as array of const parameters and assign these parameters to local record variable.
It would be nice! But, no.
If passing things inline is really your objective, then perhaps Open Array Parameters would suit you.
Procedure MyProcedure(const Vars: Array of Variant);
begin
ShowMessage(VarToStr(Vars[0])+' '+VarToStr(Vars[1]));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
MyProcedure(['Test', 12]);
end;
You could also pass an Array of Const, which is basically an array of TVarRec which is a variant record that also includes type information as VType. This is fun stuff..
An excellent article can be found on Rudy's Delphi Corner here:
Rudy's Delphi Corner, Open Array Parameters
I have a structure defined like this:
const
MaxSignalRecords=255;
type
TSignalRecord=record
signal1 : integer;
signal2 : integer;
signal3 : integer;
signal4 : integer;
signal5 : integer;
signal6 : integer;
bsignal1 : Boolean;
bsignal2 : Boolean;
bsignal3 : Boolean;
bsignal4 : Boolean;
bsignal5 : Boolean;
bsignal6 : Boolean;
end;
TListSignals = Array[0..MaxSignalRecords-1] of TSignalRecord;
This structure is used to make thousands of calculations in an algorithm like this:
for i:=1 to 900000 do
begin
CleartheList(MyList);
DotheMath(MyList);
DotheChart(MyList);
end;
I am looking for a fast way to initializate the values of my TListSignals to 0 and false.
Now I am using this :
procedure ClearListSignals(var ListSignals:TListSignals);
var
i :Integer;
begin
for i := 0 to MaxSignalRecords - 1 do
with ListSignals[i] do
begin
signal1 :=0;
signal2 :=0;
signal3 :=0;
signal4 :=0;
signal5 :=0;
signal6 :=0;
bsignal1 :=false;
bsignal2 :=false;
bsignal3 :=false;
bsignal4 :=false;
bsignal5 :=false;
bsignal6 :=false;
end;
end;
How can I improve the performance of the ClearListSignals procedure?
you can use the ZeroMemory procedure located in the Windows unit.
var
MyList : TListSignals;
begin
ZeroMemory(#Mylist,SizeOf(MyList));
end;
You should use the standard Delphi language function Default()!
const
MaxSignalRecords = 255;
type
TSignalRecord = record
...
bsignal6 : Boolean;
end;
var
X: TSignalRecord
...
X := Default(TSignalRecord);
...
And no hack.. as suggested before..
FillChar(ListSignals, SizeOf(ListSignals), 0);
In addition to what was said about FillChar and ZeroMemory (which internally just calls FillChar anyway), you can reduce the size of your record by using bsignal: set of 1..6; instead of individual booleans which should speed up the clearing slightly.
You can use SecureZeroMemory
To avoid undesired effects of
optimizing compilers, use the
SecureZeroMemory function.
The SecureZeroMemory function fills a
block of memory with zeros. It is
designed to be a more secure version
of ZeroMemory.
Use this function instead of
ZeroMemory when you want to ensure
that your data will be overwritten
promptly, as the compiler can optimize
a call to ZeroMemory by removing it
entirely.
http://msdn.microsoft.com/en-us/library/aa366877%28v=vs.85%29.aspx
LE: here you have how to use it in Delphi if your version does not contain it:
Using SecureZeroMemory in Delphi
Is it possible to use records as a method parameter, and call it without implicitly declaring an instance of said record?
I would like to be able to write code like this.
type
TRRec = record
ident : string;
classtype : TClass;
end;
procedure Foo(AClasses : array of TRRec);
then calling the method like this or something similar.
Foo([('Button1', TButton), ('Lable1', TLabel)]);
I'm still stuck on Delphi 5 by the way.
Yes. Almost.
type
TRRec = record
ident : string;
classtype : TClass;
end;
function r(i: string; c: TClass): TRRec;
begin
result.ident := i;
result.classtype := c;
end;
procedure Foo(AClasses : array of TRRec);
begin
;
end;
// ...
Foo([r('Button1', TButton), r('Lable1', TLabel)]);
It is also possible to work with a const array, but it isn't so flexible as the solution given by "gangph":
(especially that you have to give the size ([0..1]) of the array in the array declaration. The records are anomymous, the array isn't).
type
TRRec = record
ident : string;
classtype : TClass;
end;
procedure Foo(AClasses : array of TRRec);
begin
end;
const tt: array [0..1] of TRRec = ((ident:'Button1'; classtype:TButton),
(ident:'Lable1'; classtype:TLabel));
Begin
Foo(tt);
end.