How do you access different data types using Pascal pointers? - delphi

In general I have a problem figuring out how to handle pointer conversions in pascal. If I wanted a word from my byte pointer wouldn't I do something like:
type WordPtr = ^Word;
var P1, P2 : ^Byte; w, byteoffset : word;
begin
P1:=SomeGetMemVar; byteoffset:=8;
w:=WordPtr(#P1^[byteoffset])^; { invalid qualifiers at the [ }
w:=WordPtr(P1[byteoffset])^; { invalid qualifiers at the [ }
w:=WordPtr(P1+byteoffset)^; { type mismatch }
end;
Apparently not, because it doesn't work as shown in the comments.
How do I get this to work in Pascal? C would be:
uint16_t byteoffset=8;
uint8_t* P1=(uint8_t*) malloc(1000);
uint16_t w;
w=*((uint16_t*) (P1+byteoffset)); // method 1
w=*((uint16_t*) (&P1[byteoffset])); // method 2
Thanks!

In order to treat pointers as arrays, make sure that {$POINTERMATH ON} is enabled. It already is for PByte (don't use ^Byte directly), but not for most pointer types by default.
Try something more like this:
{$POINTERMATH ON}
var
P1 : PByte;
w, byteoffset : Word;
begin
P1 := SomeGetMemVar;
byteoffset := 8;
w := PWord(#P1[byteoffset])^;
//or:
w := PWord(P1+byteoffset)^;
end;
Alternatively, use Move() instead of casting pointers:
var
P1 : PByte;
w, byteoffset : Word;
begin
P1 := SomeGetMemVar;
byteoffset := 8;
Move(P1[byteoffset], w, SizeOf(w));
//or:
Move((P1+byteoffset)^, w, SizeOf(w));
end;
For older compilers that don't support {$POINTERMATH}, you can type-cast to PAnsiChar (which always supported pointer math by default):
var
P1 : PByte;
P2: PAnsiChar;
w, byteoffset : Word;
begin
P1 := SomeGetMemVar;
P2 := PAnsiChar(P1);
byteoffset := 8;
w := PWord(#P2[byteoffset])^;
//or:
w := PWord(P2+byteoffset)^;
end;
Or, just increment the PByte pointer manually:
var
P1, P2 : PByte;
w, byteoffset : Word;
begin
P1 := SomeGetMemVar;
byteoffset := 8;
P2 := P1;
Inc(P2, byteoffset);
w := PWord(P2)^;
end;

If your example is what you're trying to do, i.e. simply extract one byte of a word, you don't need to do all that pointer manipulation. Try this:
uses System.SysUtils;
type WordPtr = ^WordRec;
var w: word;
p1: WordPtr;
begin
p1=GetSomeMemVar;
w:=p1.Hi;
end;
WordRec is a record that has two byte fields in the space of a word, so that you can access each byte of the word. SysUtils also has LongRec and Int64Rec, and all have variant parts so that you can access separate bytes or separate words, etc, or treat them as arrays. And you can always create your own similar records.
If you want to pursue working with pointers, see https://docwiki.embarcadero.com/RADStudio/Sydney/en/Pointer_Math_%28Delphi%29

Related

Delphi: breaking record parameter down into fields

I have a record type
tLine = record
X, Y, Count : integer;
V : boolean;
end;
I have a
function fRotate(zLine: tLine; zAngle: double): tLine;
I want to pass zLine, but with its Y field reduced by 1. Is there a way to break a record down into its specific fields in a procedure or function? I tried
NewLine:=fRotate((zLine.X, zLine.Y-1, zLine.Count, zLine.V), zAngle);
which does not work.
Or do I have to do as follows:
dec(zLine.Y);
NewLine:=fRotate(zLine, zAngle);
inc(zLine.Y);
TIA
You would typically make a function for this. In modern Delphi with enhanced records, I like to use a static class function like this:
type
TLine = record
public
X: Integer;
Y: Integer;
Count: Integer;
V: Boolean;
public
class function New(X, Y, Count: Integer; V: Boolean): TLine; static;
end;
class function TLine.New(X, Y, Count: Integer; V: Boolean): TLine;
begin
Result.X := X;
Result.Y := Y;
Result.Count := Count;
Result.V := V;
end;
Then your function call becomes:
NewLine := fRotate(TLine.New(zLine.X, zLine.Y-1, zLine.Count, zLine.V), zAngle);
In older versions of Delphi you'd have to use a function at global scope.
For readability I like to use an alternative solution with record operators, like this: Note that this is updated in line with Kobik's suggestion
tLine = record
X, Y, Count : integer;
V : boolean;
class operator Subtract( a : tLine; b : TPoint ) : tLine;
end;
class operator tLine.Subtract(a: tLine; b : TPoint): tLine;
begin
Result.X := a.X - b.X;
Result.Y := a.Y - b.Y;
Result.Count := a.Count;
Result.V := a.V;
end;
This allows this type of construct:
fRotate( fLine - Point(0,1), fAngle );
which I think makes sense. You could obviously use a simple integer rather than an array if all you ever wanted to do was decrement Y, but this allows X and/or Y to be decremented at once.

array of byte absolute String: incorrect content while compiling for 64-bit-Windows

If I compile for 64-bit-Windows my Byte-Arrays haven't got the correct Input-Values.
If I compile this procedure for x32-Windows the values are correct.
Can anyone help me?
procedure doAnything(AText: String); //for example "<xml><s name="hello"/></xml>"
var
myArray:array of Byte absolute AText;
begin
... (* myArray for x32: correct Length and Values (60, 0, 120, 0, 109, 0, ...) *)
... (* myArray for x64: Length: 2 (60, 0) *)
end
The memory layout for a string is not the same as a dynamic array.
Using the absolute keyword here is plain wrong.
In 32 bit it happens that the length is read correctly, but the value is in characters, not in bytes.
You can do something like this to access the string as bytes:
procedure doAnything(AText: String); //for example "<xml><s name="hello"/></xml>"
var
pB : PByte;
i,len : Integer;
begin
pB := Pointer(AText);
len := Length(AText)*SizeOf(Char);
for i := 1 to len do
begin
WriteLn(pB^);
Inc(pB);
end;
// Or
for i := 0 to len-1 do
begin
WriteLn(pB[i]);
end;
end;
If you want to access the character data of a String as raw bytes, you have to use a type-cast instead, DO NOT use absolute as the memory layout of a String and a dynamic array are not compatible, as others have pointed out to you:
procedure doAnything(AText: String);
var
myBytes: PByte;
myBytesLen: Integer;
begin
myBytes := PByte(PChar(AText));
myBytesLen := ByteLength(AText);
// or: myBytesLen := Length(AText) * SizeOf(Char);
// use myBytes up to myBytesLen as needed...
end;
If you really wanted to use absolute, you would have to use it more like this instead:
procedure doAnything(AText: String);
var
myChars: PChar;
myBytes: PByte absolute myChars;
myBytesLen: Integer;
begin
myChars := PChar(AText);
myBytesLen := ByteLength(AText);
// or: myBytesLen := Length(AText) * SizeOf(Char);
// use myBytes up to myBytesLen as needed...
end;
From what I understand, the problem is that you are mapping apples and pears in the 64 bit world. If you look at this:
http://docwiki.embarcadero.com/RADStudio/XE5/en/Internal_Data_Formats#Dynamic_Array_Types
And the string:
http://docwiki.embarcadero.com/RADStudio/XE5/en/Internal_Data_Formats#Long_String_Types
You will see that the lengths have a different number of bytes for these two. The offsets also don't match. Basically they are not compatible.

Dynamic array reference count in record

I have an advanced record with a dynamic array field.
The record has a class operator for concatenation of a record and a byte. Also an Add method, adding a byte.
For what I'm about to use the record, the reference count of the dynamic array field is of importance. When running the two test procedures below, you can see that the concatenation results in a reference count of 2 while the add method results in a reference count of 1.
program TestReferenceCount;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
Type
TRec = record
class operator Add(const a: TRec; b: Byte): TRec;
private type
PDynArrayRec = ^TDynArrayRec;
TDynArrayRec = packed record
{$IFDEF CPUX64}
_Padding: LongInt; // Make 16 byte align for payload..
{$ENDIF}
RefCnt: LongInt;
Length: NativeInt;
end;
private
FArr: TBytes;
function GetRefCnt: Integer;
public
procedure Add(b : Byte);
property RefCnt: Integer read GetRefCnt;
end;
procedure TRec.Add(b : Byte);
var
prevLen: Integer;
begin
prevLen := System.Length(Self.FArr);
SetLength(Self.FArr, prevLen + 1);
Self.FArr[prevLen] := b;
end;
class operator TRec.Add(const a: TRec; b: Byte): TRec;
var
aLen: Integer;
begin
aLen := System.Length(a.FArr);
SetLength(Result.FArr, aLen + 1);
System.Move(a.FArr[0], Result.FArr[0], aLen);
Result.FArr[aLen] := b;
end;
function TRec.GetRefCnt: Integer;
begin
if Assigned(FArr) then
Result := PDynArrayRec(NativeInt(FArr) - SizeOf(TDynArrayRec)).RefCnt
else
Result := 0;
end;
procedure TestConcatenation;
var
r1 : TRec;
begin
WriteLn('RC:', r1.RefCnt); // <-- Writes 0
r1 := r1 + 65;
WriteLn('RC:', r1.RefCnt); // <-- Writes 2
end;
procedure TestAdd;
var
r1 : TRec;
begin
WriteLn('RC:', r1.RefCnt); // <-- Writes 0
r1.Add(65);
WriteLn('RC:', r1.RefCnt); // <-- Writes 1
end;
begin
TestConcatenation;
TestAdd;
ReadLn;
end.
The compiler takes care of the extra reference count when the record variable goes out of scope, so no problem really at this point.
But can this behavior be explained? Is it an undocumented implementation detail? Is there a way to avoid the extra count?
Let's take a look at this function:
procedure TestConcatenation;
var
r1 : TRec;
begin
r1 := r1 + 65;
end;
The compiler actually implements it like this:
procedure TestConcatenation;
var
r1 : TRec;
tmp : TRec;
begin
tmp := r1 + 65;
r1 := tmp;
end;
The compiler introduces a temporary local to store the result of r1 + 65. There's a very good reason for that. If it did not, where would it write the result of your addition operator? Since the ultimate destination is r1, if your addition operator writes directly to r1 it is modifying its input variable.
There is no way to stop the compiler generating this temporary local.

How to save/load Set of Types?

I have this code
type
TXSample = (xsType1, xsType2, xsType3, xsType4, xsType5, xsType6, xsType6, xsTyp7, xsType8); // up to FXSample30;
..
private
FXSample = Set of TXSample;
..
published
property Sample: TXSample read FXSample write FXSample;
..
//if Sample has a value of
Sample := [xsType2, xsType4, xsType5, xsType6, xsTyp7];
how can i save/load the property of Sample?
i would like to save it in the database.
is it possible?
Provided your set will never exceed 32 possibilities (Ord(High(TXSample)) <= 31), then it is perfectly fine to typecast the set into an Integer and back:
type
TXSamples = set of TXSample;
var
XSamples: TXSamples;
begin
ValueToStoreInDB := Integer(XSamples);
Integer(XSamples) := ValueReadFromDB;
end;
To be more specific: SizeOf(TXSamples) has to be precisely equal to SizeOf(StorageTypeForDB). Thus the following ranges apply for Ord(High(TXSample)) when typecasting TXSamples to:
Byte: Ord(High(TXSample)) < 8
Word: 8 <= Ord(High(TXSample)) < 16
Longword: 16 <= Ord(High(TXSample)) < 32
UInt64: 32 <= Ord(High(TXSample)) < 64
Directly typecasting a set variable is not possible in Delphi, but internally Delphi stores the set as a byte-value. By using an untyped move, it is easy copied into an integer. Note that these functions only go up to a size of 32 (bounds of an integer). To increase the bounds, use Int64 instead.
function SetToInt(const aSet;const Size:integer):integer;
begin
Result := 0;
Move(aSet, Result, Size);
end;
procedure IntToSet(const Value:integer;var aSet;const Size:integer);
begin
Move(Value, aSet, Size);
end;
Demo
type
TMySet = set of (mssOne, mssTwo, mssThree, mssTwelve=12);
var
mSet: TMySet;
aValue:integer;
begin
IntToSet(7,mSet,SizeOf(mSet));
Include(mSet,mssTwelve);
aValue := SetToInt(mSet, SizeOf(mSet));
end;
Personally, I would convert the set to an integer and store it in the database as an INT field, like others suggested. #teran suggested using the TIntegerSet type, and here is my approach working on native integers using bit operations.
Note that you can use SampleInInteger() to determine whether a certain element from the enumeration is present in the integer mask generated by SampleSetToInteger().
Here's the code:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
{ .: TXSample :. }
TXSample = (xsType1 = 0, xsType2, xsType3, xsType4, xsType5,
xsType6, xsType7, xsType8); // up to FXSample30;
TXSampleSet = set of TXSample;
// Converts a TXSampleSet to an integer.
function SampleSetToInteger(const S: TXSampleSet): Integer;
var
Sample: TXSample;
begin
Result := 0;
for Sample := Low(TXSample) to High(TXSample) do
if (Sample in S) then
Result := Result or (1 shl Ord(Sample));
end;
// Converts an integer to TXSampleSet.
function IntegerToSampleSet(const Int: Integer): TXSampleSet;
var
I: Integer;
begin
Result := [];
for I := 0 to Ord(High(TXSample)) do
if Int and (1 shl I) <> 0 then
Result := Result + [TXSample(I)];
end;
// Checks if a TXSample is present in the integer.
function SampleInInteger(const S: TXSample; const Int: Integer): Boolean;
begin
Result := Int and (1 shl Ord(S)) <> 0;
end;
var
XSample, XSample1: TXSampleSet;
Tmp: Integer;
begin
XSample := [xsType2, xsType4, xsType5, xsType6, xsType7];
XSample1 := [xsType1];
Tmp := SampleSetToInteger(XSample);
Writeln(Tmp);
XSample1 := IntegerToSampleSet(Tmp);
if (xsType5 in XSample1) then
Writeln('Exists');
if (SampleInInteger(xsType1, Tmp)) then
Writeln('Exists in int');
Readln;
end.
A Delphi set is simply a collection of (possibly) related boolean flags. Each boolean flag corresponds to whether or not the matching ordinal value is in the set.
You could certainly pack a set into an integer value by representing the set as a bitset. Or you could create a textual representation of the set.
However, both of these options leave you with no tractable ability to query the database at the SQL level. For this reason I would advise you to represent each value in the set, i.e. each boolean flag, as a separate field (i.e. column) of the database table. This gives you the most powerful representation of the data.
the easiest way to store set in database (as #DavidHeffernan mentioned in comment) is to convert your set to bit-mask.
in int32 (integer) value you have 32 bits and can save set up to 32 fields;
Delphi has TIntegerSet (see http://docwiki.embarcadero.com/Libraries/en/System.SysUtils.TIntegerSet) type defined in SysUtils. it is declared as:
TIntegerSet = set of 0..SizeOf(Integer) * 8 - 1;
so using it, it is simple to convert set to integer and back (just casting TIngeterSet to integer or vice versa);
bit-mask is also good option because it is only one INT field in your database table.
also you can create separate table in your DB to store set content (main table (id, ...), and setValuesTable (main_id, setElementValue)) (this option is good for using in db queries)
here is an example of using TIntegerSet:
program Project1;
{$APPTYPE CONSOLE}
uses System.SysUtils;
type
TXSample = (xsType1, xsType2, xsType3, xsType4, xsType5, xsType6, xsType7, xsType8);
TSampleSet = set of TXSample;
function SampleSetToInteger(ss : TSampleSet) : integer;
var intset : TIntegerSet;
s : TXSample;
begin
intSet := [];
for s in ss do
include(intSet, ord(s));
result := integer(intSet);
end;
function IntegerToSampleSet(mask : integer) : TSampleSet;
var intSet : TIntegerSet;
b : byte;
begin
intSet := TIntegerSet(mask);
result := [];
for b in intSet do
include(result, TXSample(b));
end;
var xs : TSampleSet;
mask : integer;
begin
xs := [xsType2, xsType6 .. xsType8];
mask := SampleSetToInteger(xs); //integer mask
xs := IntegerToSampleSet(mask);
end.
Set variables can be saved successfully to a TStream descendant. Here's an example.
Just create a new vcl forms application, add two TButton components to it and fill in the OnClick events for each button as illustrated in the example below.
This was created in XE4 so the uses clause might be different for other versions of Delphi but that should be trivial to change by removing the namespace indicators before each unit in the uses clause. Saving a set type variable with articulated values is possible to a binary file and easily with Delphi. In other words,
Also suggested is taking a look at the TypInfo unit if you have the source or just using the functions provided which make dissecting Set types down to their text representation fairly simple though no example is provided here. That is suggested if you want to include saving to a config or ini file or in a persistence format that is text editable.
The one below is the simplest one that I know of. Looking at the binary output of a set type saved to a stream like the one below implies that it is saved in the smallest possible bitmapped representation for the set based on its size. The one below maps to one byte on disk (the value is 5) which means that each value must be mapped to a power of 2 (seThis = 1, seThat = 2, seTheOther = 4) just like manually created constant bitmasked values. The compiler likely enforces that it follows rules that forces set to retain their ordinality. This example was tested an works in Delphi XE4.
Hope that helps.
Brian Joseph Johns
unit Unit1;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TSomeEnum = (seThis, seThat, seTheOther);
TSomeEnumSet = set of TSomeEnum;
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
SomeSetVar: TSomeEnumSet;
SomeBoolean: Boolean;
SomeInt: Integer;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
SomeSetVar := [seThis, seTheOther];
SomeBoolean := True;
SomeInt := 31415;
with TFileStream.Create('SetSave.bin',fmCreate or fmOpenWrite or fmShareCompat) do
try
Write(SomeSetVar,SizeOf(SomeSetVar));
Write(SomeBoolean,SizeOf(Boolean));
Write(SomeInt,SizeOf(Integer));
finally
Free;
end;
SomeSetVar := [];
SomeInt := 0;
SomeBoolean := False;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
ResponseStr: string;
begin
with TFileStream.Create('SetSave.bin',fmOpenRead or fmShareCompat) do
try
Read(SomeSetVar,SizeOf(SomeSetVar));
Read(SomeBoolean,SizeOf(Boolean));
Read(SomeInt,SizeOf(Integer));
finally
Free;
end;
ResponseStr := 'SomeSetVar = ';
if (seThis in SomeSetVar) then
ResponseStr := ResponseStr + 'seThis ';
if (seThat in SomeSetVar) then
ResponseStr := ResponseStr + 'seThat ';
if (seTheOther in SomeSetVar) then
ResponseStr := ResponseStr + 'seTheOther ';
ResponseStr := ResponseStr + ' SomeBoolean = ' + BoolToStr(SomeBoolean);
ResponseStr := ResponseStr + ' SomeInt = ' + IntToStr(SomeInt);
ShowMessage(ResponseStr);
end;
end.
With a little help from RTTI it can be achieved in a generic way:
program SetConverter;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.RTTI, System.SysUtils;
type
SetConverter<T> = class abstract
strict private
class var FRttiContext: TRttiContext;
public
class function ToInteger(aSet: T): Integer;
class function FromInteger(aValue: Integer): T;
end;
{ SetConverter<T> }
class function SetConverter<T>.FromInteger(aValue: Integer): T;
var
ResultValues: TIntegerSet;
ReturnType: TRttiType;
SetValues: TIntegerSet;
EnumSet: T absolute SetValues;
begin
ReturnType := FRttiContext.GetType(Self).GetMethod('FromInteger').ReturnType;
if not((ReturnType is TRttiSetType) and (TRttiSetType(ReturnType).ElementType is TRttiEnumerationType)) then
Exit;
SetValues := TIntegerSet(aValue);
Result := EnumSet;
end;
class function SetConverter<T>.ToInteger(aSet: T): Integer;
var
RttiParameter: TRttiParameter;
ResultValues: TIntegerSet;
SetValues: TIntegerSet;
EnumSet: T absolute SetValues;
EnumType: TRttiEnumerationType;
SetType: TRttiSetType;
i: Integer;
begin
Result := 0;
RttiParameter := FRttiContext.GetType(Self).GetMethod('ToInteger').GetParameters[0];
if not(RttiParameter.ParamType is TRttiSetType) then
Exit;
SetType := RttiParameter.ParamType as TRttiSetType;
if not(SetType.ElementType is TRttiEnumerationType) then
Exit;
EnumType := SetType.ElementType as TRttiEnumerationType;
EnumSet := aSet;
ResultValues := [];
for i := EnumType.MinValue to EnumType.MaxValue do
if i in SetValues then
Include(ResultValues, i);
Result := Integer(ResultValues);
end;
type
TXSample = (xsType1, xsType2, xsType3, xsType4, xsType5, xsType6, xsType7, xsType8);
TSampleSet = set of TXSample;
var
Before, After: TSampleSet;
i: Integer;
begin
Before := [xsType2, xsType6 .. xsType8];
i := SetConverter<TSampleSet>.ToInteger(Before);
After := SetConverter<TSampleSet>.FromInteger(i);
WriteLN('Before = After: ' + (Before = After).ToString(TUseBoolStrs.True));
Readln;
end.
You can use this unit to convert set to int. if you need more settoint functions you can add yours by looking code below.
Set may take only 1 byte memory space.
So you can obtain yourSet size and get result as modula of this result.
example: your set size: 1 byte you can get result -->
Result := pINT^ mod maxVal
You should obtain maxval by calculating maxvalue of variable type.
maxVal = Power( 2, (8*sizeof(MySet)-1) )
unit u_tool;
interface
uses Graphics;
type
TXSample = (xsType1, xsType2, xsType3, xsType4, xsType5, xsType6, xsType6, xsTyp7, xsType8); // up to FXSample30;
FXSample = Set of TXSample;
function FXSampleToInt(FXSample: FXSample ): Integer;
function IntToFXSample(Value: Integer): FXSample;
function FontStyleToInt(FontStyle: TFontStyles ): Integer;
function IntToFontStyle(Value: Integer): TFontStyles;
implementation
function FXSampleToInt(FXSample: FXSample ): Integer;
var
pInt: PInteger;
begin
pInt := #FXSample;
Result := pInt^;
end;
function IntToFXSample(Value: Integer): FXSample;
var
PFXSample: ^FXSample;
begin
PFXSample := #Value;
Result := PFXSample^;
end;
function FontStyleToInt(FontStyle: TFontStyles ): Integer;
var
pInt: PInteger;
begin
pInt := #FontStyle;
Result := pInt^;
end;
function IntToFontStyle(Value: Integer): TFontStyles;
var
PFontStyles: ^TFontStyles;
begin
PFontStyles := #Value;
Result := PFontStyles^;
end;
end.
Or we can make compiler forget about the types completly and then define what it should see (in case we know in compile-time what it sould see). This solution is so awful as it can be written on just one line.
type
// Controls.TCMMouseWheel relies on TShiftState not exceeding 2 bytes in size
TShiftState = set of (ssShift, ssAlt, ssCtrl,
ssLeft, ssRight, ssMiddle,
ssDouble, ssTouch, ssPen,
ssCommand, ssHorizontal);
var
Shifts : TShiftState;
Value : Integer;
begin
Shifts := TShiftState((Pointer(#Value))^):
Value := (PInteger(#Shifts))^;
if ssShift in TShiftState((Pointer(#Value))^) then
Exit;
end;
It happens that unused (top) bits are set (or not) but it has no influence on set operations (in, =, +, -, * .. ).
This line in Delphi:
Shifts := TShiftState((Pointer(#Value))^);
is like this in Assembler (Delphi XE6):
lea eax,[ebp-$0c]
mov ax,[eax]
mov [ebp-$06],ax
On Delphi 2007 (where is TShiftState is smaller so Byte can be used) this Assembler:
movzx eax,[esi]
mov [ebp-$01],al
Simplest solution - proceeding the set directly as numeric variable. The "absolute" is a keyword:
procedure Foo(FXSample: TFXSample);
var
NumericFxSample: Byte absolute FXSample;
begin
WriteLn(YourTextFile, NumericFxSample);//numeric value from a set
end;
If your type is wider than 8 bits you need to use wider numeric type like word (up to 16 items in a set) or dword.

Can I assign items in an array all at once in Delphi/Pascal?

I want to do something like in PHP, Python and most other programming languages:
my_array_name = [128, 38459, 438, 23674...]
So I tried to replicate this in Delphi/Pascal the best I could:
HSVtoRGB := [0, 0, 0];
(this is for a function which returns an RGB array given HSV values.)
But I am getting errors:
[DCC Error] Unit2.pas(44): E2001 Ordinal type required
[DCC Error] Unit2.pas(45): E2010 Incompatible types: 'HSVRealArray' and 'Set'
Any idea? This is school work - but my teacher didn't know the answer.
When it comes to dynamic arrays, yes:
type
TIntArray = array of integer;
procedure TForm1.Button1Click(Sender: TObject);
var
MyArr: TIntArray;
begin
MyArr := TIntArray.Create(10, 20, 30, 40);
end;
When it comes to static arrays, you need to write a helper function:
type
TIntArray = array[0..2] of integer;
function IntArray(const A, B, C: integer): TIntArray;
begin
result[0] := A;
result[1] := B;
result[2] := C;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MyArr: TIntArray;
begin
MyArr := IntArray(10, 20, 30);
end;
This resembles how the Point function creates a TPoint record. (Records and arrays are not the same thing, though.)
This is an area where Delphi turns something that is a simple one-line assignment statement in most languages into something more complicated.
One approach would to declare the value as a typed constant:
type
HSVRealArray = array[1..3] of real;
const
constHSVVal: HSVRealArray = (0, 0, 0);
var
currentValue: HSVRealArray;
begin
currentValue := constHSVVal;
end;
Another approach is to create utility functions that return the type you need:
function MakeHSVRealArray(H, S, V: Real): HSVRealArray;
begin
Result[1] := H;
Result[2] := S;
Result[3] := V;
end;
currentValue := MakeHSVRealArray(0,0,0);
With some extra work, you can achieve a clean implementation:
var
x: TRGB;
begin
x := TRGB.Init(0,0,0);
end;
TRGB = record
Red, Green, Blue: real;
class function Init(r,g,b: real): TRGB; static;
end;
class function TRGB.Init(r, g, b: real): TRGB;
begin
Result.Red := r;
Result.Green := g;
Result.Blue := b;
end;
For array handling, array initialization and array constant declarations, Delphi doesn't make simple things simple.
In some situations similar to yours, I initialize the array with a utility function taking one open array parameter and returning the appropiate static string.
const
MaxArray = 10;
type
TRealStaticArray = array[0..MaxArray] of Real;
function RealArray(const AnArray: array of real):TRealStaticArray;
const DefaultValue=0.0;
var i: integer;
begin
// EDIT: commented out, thanks Serg. for i:= 0 to low(AnArray)-1 do result[i]:=DefaultValue;
for i:= High(AnArray)+1 to MaxArray do
result[i]:=DefaultValue;
for i:= Low(AnArray) to High(AnArray) do
if (i>=0) and (i<=MaxArray) then
result[i]:=AnArray[i];
end;
Use it this way:
var MyArray: TRealStaticArray;
...
MyArray := RealArray([10.0, 20.0, 30.0]);
Delphi-XE7 introduced a new syntax for dynamic array initialization.
// compile time const declaration of dynamic array
const
my_ConstArray_name: TArray<Integer> = [128, 38459, 438, 23674];
// compile time var declaration of dynamic array
var
my_VarArray_name: TArray<Integer> = [128, 38459, 438, 23674];
Runtime assignment of dynamic arrays:
var
a : TArray<Integer>;
begin
a := [1,2,3];
Unfortunately this syntax cannot be used on ordinary static arrays:
Type
TMyArray = array[0..3] of Integer;
const
cMyArray: TMyArray = [0,1,2,3]; // E2010 Incompatible types: 'TMyArray' and 'Set'
cMyArray: TMyArray = (0,1,2,3); // Works, as in all Delphi versions
var
MyArray: TMyArray;
begin
// This fails as well
MyArray := [0,1,2,3]; // E2010 Incompatible types: 'TMyArray' and 'Set'
MyArray := (0,1,2,3); // E2029 ')' expected but ',' found
//-----------^-------
// This works in all Delphi versions !
MyArray := cMyArray;
I know this is an old post, but I came across this while looking into the auto-assignment technique for Delphi. This post has a very nice explanation, along with a way to auto-assign an array of records:
http://delphi.about.com/od/adptips2006/qt/const_array.htm
Unfortunately, the initialization can only be done with native or record types. Class objects need a helper function as others have shown above.
Can you try something like that:
TRGB = record
Red : integer;
Green: integer;
Bklue: integer;
end;
var Variable:TRGB;
Variable.Red:=0;
Variable.Green:=0;
Variable.Blue:=0;
Putting aside the general question, I'd like to point out that the specific use-case was never going to work anyway: Delphi can't return on the stack the contents of an array of unknown length.
The return type of a function needs to be known by the calling code. It may be a pointer to the heap: it may even be a memory-managed object on the stack: but it is not permitted to be an undefined number of bytes on the stack, decided dynamically by the target function.
And in many other languages, the example
return (0,0,0) //return pointer to automatic allocation
would be undefined, even if the compiler lets you do it.
A common method of defining, using and returning const values is
int myarray[8] = {128, 38459, 438 ... 23674};
memcpy (myparam, myArray, sizeof (myArray));
... and you can do that in Delphi
[0, 0, 0] is a set in Delphi. (0, 0, 0) is an array constant in Delphi! Just use:
HSVtoRGB := (0, 0, 0);
(Unless HSVtoRGB is a dynamic array. Did you declare it as var HSVtoRGB: array[1..3] of integer; or something similar?

Resources