Trying the convert an Integer to a Packed Record of 32 booleans.
TUserRightsRecord = packed record
r1:boolean;
.
.
end;
yet, I am unable to find a function which convert a variable to a packed record, since the direct assignment does not work.
What function convert a variable ( or at least Integer ) to a Packed Record of the same bite size ?
Trying the convert an Integer to a Packed Record of 32 booleans.
Please note that SizeOf(Integer) = 4 while SizeOf(<packed record of 32 booleans>) = 32 because SizeOf(Boolean) = 1 (1 byte = 8 bits). You seem to be under the impression that a Boolean is a single bit; it isn't.
If that had been the case, however, you could simply have cast the integer to such a record.
(But of course it is trivially possible to write a function that "converts" an integer to a record of 32 booleans.)
The standard approach to use the bits in an integer is to use bitwise operators:
const
HasScreen = 1;
HasSound = 2;
HasKeyboard = 4;
HasMouse = 8;
HasInternet = 16;
var
ComputerProperties: Integer;
begin
// Test a bit
if ComputerProperties and HasInternet = 0 then
ShowMessage('You need an Internet connection.');
// Set a bit
Computer := Computer or HasInternet;
// Clear a bit
Computer := Computer and not HasInternet;
In Delphi, it is more idiomatic to use sets:
type
TComputerFeature = (cfScreen, cfSound, cfKeyboard, cfMouse, cfInternet);
TComputerFeatures = set of TComputerFeature;
var
Features: TComputerFeatures;
begin
Features := [cfScreen, cfKeyboard];
if not (cfInternet in Features) then
ShowMessage('You need an Internet connection.');
Include(Features, cfInternet);
Exclude(Features, cfInternet);
end;
You may, however, easily simulate your original design approach using advanced records:
type
TBit32 = type Integer;
TBit32Helper = record helper for TBit32
strict private
function GetBit(Index: Integer): Boolean;
procedure SetBit(Index: Integer; const Value: Boolean);
public
property Bit[Index: Integer]: Boolean read GetBit write SetBit;
end;
function TBit32Helper.GetBit(Index: Integer): Boolean;
begin
Result := (Self shr Index) and 1 <> 0;
end;
procedure TBit32Helper.SetBit(Index: Integer; const Value: Boolean);
begin
if Value then
Self := Self or (1 shl Index)
else
Self := Self and not (1 shl Index);
end;
begin
var x: Integer := 123;
Writeln(TBit32(x).Bit[4]); // read
TBit32(x).Bit[6] := False; // write
It might be enough to replace boolean with longbool
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
var
i: integer;
begin
try
i := Sizeof(boolean);
writeln('Sizeof(boolean): ', i);
i := Sizeof(LongBool);
writeln('Sizeof(LongBool): ', i);
readln(i);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Otherwise you may have to define your own data type as a record. Here you have to pay attention to the memory size of your data type "SizeOf". Here an excample:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TConvert=packed record
public
function ToString: string;
public
//Redefined memory. Each field has the same start address.
case integer of
0: (b: boolean);
1: (G: LongBool);
2: (i: integer);
3: (data: array[0..3]of byte);
end;
function TConvert.ToString: string;
begin
//Low is left !!!
Result := Format('[%.2x|%.2x|%.2x|%.2x]', [data[0], data[1], data[2], data[3]]);
end;
var
i: integer;
r: TConvert;
begin
try
i := Sizeof(TConvert);
writeln('Sizeof(TConvert): ', i);
r.b := True;
writeln('boolean(true): ', r.ToString, ' ',BoolToStr(r.G, true));
r.G := false;
writeln('LongBool(false): ', r.ToString, ' ',BoolToStr(r.G, true));
r.G := True;
writeln('LongBool(true): ', r.ToString, ' ',BoolToStr(r.G, true));
r.i := 1;
writeln('LongBool(i=1): ', r.ToString, ' ',BoolToStr(r.G, true));
readln(i);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Related
Ihave value from nor or xor gate with represented as TBits and i want to convert it to generic variable like integer or unsigned integer my current working ide Tokyo 10.2
var
ABits: TBits;
AComulative: UInt32;
const
PosBitFromSensor1 = 0;
PosBitFromSensor2 = 1;
begin
ABits := TBits.Create;
try
ABits.Size := 32;
{GetValFromSensor return Boolean type}
ABits.Bits[PostBitFromSensor1] := GetValFromSensor(PosBitFromSensor1);
ABits.Bits[PostBitFromSensor2] := GetValFromSensor(PosBitFromSensor2);
AComulative := SomeBitsConvertToInteger(ABits); {some function like this}
finally
ABits.Free;
end;
end;
or any simple solution.
It won't be very fast but you can do regular bit manipulation, set each bit that corresponds to a "true" in the boolean array . For example:
function SomeBitsConvertToInteger(ABits: TBits): UInt32;
var
i: Integer;
begin
if ABits.Size <> SizeOf(Result) * 8 then
raise EBitsError.Create('Invalid bits size');
Result := 0;
for i := 0 to Pred(SizeOf(Result) * 8) do
Result := Result or UInt32((Ord(ABits[i]) shl i));
end;
this solution provided by #Victoria and #LURD it maybe usefull for the other that have same solving problem. sorry about my English.
type
TBitsHelper = class helper for TBits
public
function ToUInt32: UInt32;
end;
{ TBitsHelper }
function TBitsHelper.ToUInt32: UInt32;
type
PUInt32 = ^UInt32;
begin
if Size > SizeOf(Result) * 8 then
raise EOverflow.Create('Size overflow!');
with Self do
Result := PUInt32(FBits)^;
end;
maybe something like this :
type
{$IF CompilerVersion > 32} // tokyo
{$MESSAGE Fatal 'Check if TBits still has the exact same fields and adjust the IFDEF'}
{$ENDIF}
TPrivateAccessBits = class
public
FSize: Integer;
FBits: Pointer;
end;
Move(#TPrivateAccessBits(ABits).FBits, AComulative, sizeOf(AComulative));
I need to do a "smart" detection of a generic type and return as a string, but at the moment i don't understand why delphi put some strange identification on the PTypeInfo.Name property.
What i have so far is this:
program SO_29674887;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
System.TypInfo;
type
TypeResolver = class
strict private
class function ReCast<T>(const AValue) : T;
public
class function Format<T>(const AValue : T) : string;
end;
Compare = class
public
class function IsEqual<A;B>(const AVal : A; BVal : B) : string;
end;
{ TypeResolver }
class function TypeResolver.ReCast<T>(const AValue): T;
begin
Result := T(AValue);
end;
class function TypeResolver.Format<T>(const AValue: T): string;
var Info : PTypeInfo;
begin
Info := TypeInfo(T);
Result := 'undefined';
if(Info.Kind = tkInteger) then
begin
if(Info.Name = GetTypeName(TypeInfo(Byte))) then
Result := IntToStr(ReCast<Byte>(AValue))
else if(Info.Name = GetTypeName(TypeInfo(ShortInt))) then
Result := IntToStr(ReCast<ShortInt>(AValue))
else if(Info.Name = GetTypeName(TypeInfo(SmallInt))) then
Result := IntToStr(ReCast<SmallInt>(AValue))
else if(Info.Name = GetTypeName(TypeInfo(Integer))) then
Result := IntToStr(ReCast<Integer>(AValue));
end;
Result := Info.Name + ':' + Result;
end;
{ Compare }
class function Compare.IsEqual<A, B>(const AVal: A; BVal: B): string;
begin
Result := Format('%s = %s', [
TypeResolver.Format<A>(AVal),
TypeResolver.Format<B>(BVal)]);
end;
var PAUSE : string;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
WriteLn(Compare.IsEqual(0, 255));
WriteLn(Compare.IsEqual(-127, 127));
WriteLn(Compare.IsEqual(0, 65535));
WriteLn(Compare.IsEqual(-32768, 32767));
WriteLn(Compare.IsEqual(0, 4294967295));
WriteLn(Compare.IsEqual(-2147483648, 2147483647));
Readln(PAUSE);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
What happens here is that instead of give me the actual type name of the integer, delphi gives me strange identifier like :2, :4, :6, etc.
For instance:
Compare.IsEqual(0, 255); //Gives Info.Name = :2, Byte
Compare.IsEqual(-127, 127); //Gives Info.Name = ShortInt, :4
Compare.IsEqual(0, 65535); //Gives Info.Name = :6, Word
Compare.IsEqual(-32768, 32767); //Gives Info.Name = SmallInt, :8
Compare.IsEqual(0, 4294967295); //Gives Info.Name = :01, Cardinal
Compare.IsEqual(-2147483648, 2147483647); //Gives Info.Name = Integer, :21
So my question is how i can find the right type to cast if it seems to me that delphi doesn't provide any clue of the actual type when he deliver those identifiers and why exactly it gives those odd identifiers.
Here's my reproduction, somewhat shorter.
{$APPTYPE CONSOLE}
uses
System.TypInfo;
type
TypeResolver = class
public
class function Format<T>(const AValue : T) : string;
end;
class function TypeResolver.Format<T>(const AValue: T): string;
var
Info: PTypeInfo;
begin
Info := TypeInfo(T);
Result := Info.Name;
end;
begin
Writeln(TypeResolver.Format(0));
Writeln(TypeResolver.Format<Byte>(0));
Writeln(TypeResolver.Format(255));
Readln;
end.
In XE2 the output is:
:3
Byte
Byte
In XE6 and later the output is:
ShortInt
Byte
Byte
It looks as though the earlier versions of Delphi create a private type with an unspeakable name when inferring from a literal of 0. I cannot say why that should be so. Since the behaviour has changed, one can only assume that the Embarcadero engineers made the change to fix what they deemed to be a defect.
In other words, it would seem that the behaviour that you are observing is a bug.
My hypothesis that a private type is created is backed up by this program:
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.TypInfo;
type
TypeResolver = class
public
class function Format<T>(const AValue : T) : string;
end;
class function TypeResolver.Format<T>(const AValue: T): string;
var
Info: PTypeInfo;
TypeData: PTypeData;
begin
Info := TypeInfo(T);
Result := Info.Name;
if Info.Kind=tkInteger then begin
TypeData := GetTypeData(Info);
Result := Result + ', min = ' + IntToStr(TypeData.MinValue) +
', max = ' + IntToStr(TypeData.MaxValue);
end;
end;
begin
Writeln(TypeResolver.Format(0));
Readln;
end.
On XE2 the output is:
:3, min = 0, max = 127
On XE6 the output is:
ShortInt, min = -128, max = 127
So I think that this is an issue with generic type inference, that we can probably ascribe to a bug fixed in XE6.
I don't have any advice for how you should work around this because I don't know your actual problem. That said, comparing type names is generally something that is best avoided if possible.
It seems for me like you are trying to invent TValue which is located in RTTI.pas
I've rewritten your example using TValue.
Place a TMemo on a form and the following code:
uses
RTTI;
type
TypeResolver = class
public
class function ReCast<T>(const AValue): T;
class function Format<T>(const AValue: T): string;
end;
Compare = class
public
class function IsEqual<A; B>(const AValA: A; const AValB: B): string;
end;
class function Compare.IsEqual<A, B>(const AValA: A; const AValB: B): string;
begin
Result := Format('%s = %s', [TypeResolver.Format<A>(AValA), TypeResolver.Format<B>(AValB)]);
end;
{ TypeResolver }
class function TypeResolver.ReCast<T>(const AValue): T;
begin
Result := T(AValue);
end;
class function TypeResolver.Format<T>(const AValue: T): string;
begin
Result := TValue.From(AValue).TypeInfo.Name;
end;
procedure TForm13.FormCreate(Sender: TObject);
begin
Memo1.Lines.Add(Compare.IsEqual(0, 255));
Memo1.Lines.Add(Compare.IsEqual(-127, 127));
Memo1.Lines.Add(Compare.IsEqual(0, 65535));
Memo1.Lines.Add(Compare.IsEqual(-32768, 32767));
Memo1.Lines.Add(Compare.IsEqual(0, 4294967295));
Memo1.Lines.Add(Compare.IsEqual(-2147483648, 2147483647));
Memo1.Lines.Add(Compare.IsEqual(Form13, Memo1));
end;
I belive it does the thick for you?
Here is the output of above:
ShortInt = Byte
ShortInt = ShortInt
ShortInt = Word
SmallInt = SmallInt
ShortInt = Cardinal
Integer = Integer
TForm13 = TMemo
What is the easiest way to get the maximum value in a TList<Integer>?
function GetMaximum(AList: TList<Integer>): Integer;
begin
Assert(AList.Count > 0);
Result := ?;
end;
I read that C# has a AList.Max, is there something like that in Delphi?
Here's a fun example with a MaxValue implementation on a generic container:
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.Generics.Defaults, System.Generics.Collections;
type
TMyList<T> = class(TList<T>)
public
function MaxValue: T;
end;
{ TMyList<T> }
function TMyList<T>.MaxValue: T;
var
i: Integer;
Comparer: IComparer<T>;
begin
if Count=0 then
raise Exception.Create('Cannot call TMyList<T>.MaxValue on an empty list');
Comparer := TComparer<T>.Default;
Result := Self[0];
for i := 1 to Count-1 do
if Comparer.Compare(Self[i], Result)>0 then
Result := Self[i];
end;
var
IntList: TMyList<Integer>;
DoubleList: TMyList<Double>;
StringList: TMyList<string>;
begin
IntList := TMyList<Integer>.Create;
IntList.AddRange([10, 5, 12, -49]);
Writeln(IntList.MaxValue);
DoubleList := TMyList<Double>.Create;
DoubleList.AddRange([10.0, 5.0, 12.0, -49.0]);
Writeln(DoubleList.MaxValue);
StringList := TMyList<string>.Create;
StringList.AddRange(['David Heffernan', 'Uwe Raabe', 'Warren P', 'Jens Mühlenhoff']);
Writeln(StringList.MaxValue);
Readln;
end.
Because we cannot come up with a generic equivalent to low(Integer) I raise an exception when the method is called on an empty list.
The output is:
12
1.20000000000000E+0001
Warren P
Here's an alternative answer: Use the Spring.Collections.pas unit from the Spring4D framework: (found here: http://code.google.com/p/delphi-spring-framework/)
program ListEnumerableDemo;
{$APPTYPE CONSOLE}
uses
System.SysUtils
, Spring.Collections;
var
List: IList<Integer>;
Enumerable: IEnumerable<Integer>;
begin
try
List := TCollections.CreateList<Integer>;
List.AddRange([1,6,2,9,54,3,2,7,9,1]);
Enumerable := List;
WriteLn(Enumerable.Max);
ReadLn;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Using for .. in:
function GetMaximum(AList: TList<Integer>): Integer;
var
I: Integer
begin
Assert(AList.Count > 0);
Result := Low(Integer);
for I in AList do
if I > Result then
Result := I;
end;
I agree that using the Spring collections is probably the easiest way. However there may be reasons to not use them (already using Generics.Collections all over the place).
So here is how to make a new type that extends TEnumerable<T> with a function Max: T.
type
Enumerable<T> = record
private
source: TEnumerable<T>;
public
function Max: T;
class operator Implicit(const value: TEnumerable<T>): Enumerable<T>;
end;
class operator Enumerable<T>.Implicit(
const value: TEnumerable<T>): Enumerable<T>;
begin
Result.source := value;
end;
function Enumerable<T>.Max: T;
var
default: IComparer<T>;
x, y: T;
flag: Boolean;
begin
if not Assigned(source) then
raise EArgumentNilException.Create('Source');
default := TComparer<T>.Default;
flag := False;
for x in source do
begin
if flag then
begin
if default.Compare(x, y) > 0 then
y := x;
end
else
begin
y := x;
flag := True;
end;
end;
if flag then
Result := y
else
raise EListError.Create('source is empty');
end;
The code is basically a port of the .Net Enumerable.Max<T> extension method from System.Linq. You can use it just like in Nicks example.
The interesting thing for those interested in their binary size: linker is able to remove the methods that are never used.
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.
Is there a method in Delphi to check if a string is a number without raising an exception?
its for int parsing.
and an exception will raise if one use the
try
StrToInt(s);
except
//exception handling
end;
function TryStrToInt(const S: string; out Value: Integer): Boolean;
TryStrToInt converts the string S, which represents an integer-type number in either decimal or hexadecimal notation, into a number, which is assigned to Value. If S does not represent a valid number, TryStrToInt returns false; otherwise TryStrToInt returns true.
To accept decimal but not hexadecimal values in the input string, you may use code like this:
function TryDecimalStrToInt( const S: string; out Value: Integer): Boolean;
begin
result := ( pos( '$', S ) = 0 ) and TryStrToInt( S, Value );
end;
var
s: String;
iValue, iCode: Integer;
...
val(s, iValue, iCode);
if iCode = 0 then
ShowMessage('s has a number')
else
ShowMessage('s has not a number');
Try this function StrToIntDef()
From help
Converts a string that represents an integer (decimal or hex notation) to a number with error default.
Pascal
function StrToIntDef(const S: string; Default: Integer): Integer;
Edit
Just now checked the source of TryStrToInt() function in Delphi 2007. If Delphi 7 dont have this function you can write like this. Its just a polished code to da-soft answer
function TryStrToInt(const S: string; out Value: Integer): Boolean;
var
E: Integer;
begin
Val(S, Value, E);
Result := E = 0;
end;
XE4 and newer:
for ch in s do
TCharacter.IsNumber(ch);
Don't forget:
uses System.Character
In delphi 7 you can use the Val procedure. From the help:
Unit: System
Delphi syntax: procedure Val(S; var V; var Code: Integer);
S is a string-type expression; it must be a sequence of characters that form a signed real number.
V is an integer-type or real-type variable. If V is an integer-type variable, S must form a whole number.
Code is a variable of type Integer.
If the string is invalid, the index of the offending character is stored in Code; otherwise, Code is set to zero. For a null-terminated string, the error position returned in Code is one larger than the actual zero-based index of the character in error.
use this function
function IsNumber(N : String) : Boolean;
var
I : Integer;
begin
Result := True;
if Trim(N) = '' then
Exit(False);
if (Length(Trim(N)) > 1) and (Trim(N)[1] = '0') then
Exit(False);
for I := 1 to Length(N) do
begin
if not (N[I] in ['0'..'9']) then
begin
Result := False;
Break;
end;
end;
end;
For older Delphi versions from delphi 5 help example:
uses Dialogs;
var
I, Code: Integer;
begin
{ Get text from TEdit control }
Val(Edit1.Text, I, Code);
{ Error during conversion to integer? }
if Code <> 0 then
MessageDlg('Error at position: ' + IntToStr(Code), mtWarning, [mbOk], 0);
else
Canvas.TextOut(10, 10, 'Value = ' + IntToStr(I));
end;
In some languages decimal separators are different (for example, '.' is used in English and ',' is used in Russian). For these cases to convert string to real number the following procedure is proposed:
function TryStrToFloatMultiLang(const S : String; out Value : Extended) : Boolean;
var
dc : char;
begin
Result := false;
dc := DecimalSeparator;
DecimalSeparator := '.';
try
Result := TryStrToFloat(S, Value);
except
DecimalSeparator := ',';
Result := TryStrToFloat(S, Value);
end;
DecimalSeparator := dc;
end;
Update
As #Pep mentioned TryStrToFloat catch exceptions, but it returns boolean value. So the correct code is:
function TryStrToFloatMultiLang(const S : String; out Value : Extended) : Boolean;
var
dc : char;
begin
Result := false;
dc := DecimalSeparator;
DecimalSeparator := '.';
Result := TryStrToFloat(S, Value);
if not Result then begin
DecimalSeparator := ',';
Result := TryStrToFloat(S, Value);
end;
DecimalSeparator := dc;
end;
When you using procedure
val(s, i, iCode);
and set value xd ....
val('xd', i, iCode)
as a result we obtain: 13
standard unit Variants
function VarIsNumeric(v:Variant):Boolean