How to save/load Set of Types?

后端 未结 9 1300
傲寒
傲寒 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:33

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

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

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