How to save/load Set of Types?

后端 未结 9 1299
傲寒
傲寒 2020-12-06 00:47

I have this code

type
  TXSample = (xsType1, xsType2, xsType3, xsType4, xsType5, xsType6, xsType6, xsTyp7, xsType8); // up to FXSample30;  
..

private
  FX         


        
相关标签:
9条回答
  • 2020-12-06 01:17

    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.

    0 讨论(0)
  • 2020-12-06 01:20

    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.
    
    0 讨论(0)
  • 2020-12-06 01:22

    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.

    0 讨论(0)
  • 2020-12-06 01:24

    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.
    
    0 讨论(0)
  • 2020-12-06 01:29

    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.
    
    0 讨论(0)
  • 2020-12-06 01:33

    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
    0 讨论(0)
提交回复
热议问题