Conditional behaviour based on concrete type for generic class

后端 未结 4 1774
北恋
北恋 2020-12-06 05:06

Since my question from yesterday was perhaps not completely clear and I did not get the answer I wanted, I will try to formulate it in a more general way:

Is there a

相关标签:
4条回答
  • 2020-12-06 05:46

    If someone is interested how I did implement my "worst-case size with special treatment for strings"

    class function RTTIUtils.GetDeepSize <T> (Variable : T) : Integer;
    var
      StringLength          : Integer;
      Ptr                   : PInteger;
    begin
    if (TypeInfo (T) = TypeInfo (String)) then
      begin
      Ptr := @Variable;
      Ptr := PInteger (Ptr^);
      Dec (Ptr);
      StringLength := Ptr^;
      Result := StringLength * SizeOf (Char) + 12;
      end
    else
      Result := 0;
    end;
    

    For me, this does the job at hand. Thanks to all contributors!

    0 讨论(0)
  • 2020-12-06 05:46

    TypeInfo(T) is the right way. Moreover you can use all the stuff from TypInfo unit like TTypeData record to determine some specific properties of a type you use instead of generic. When you determine the the current type used instead of T, you may use pointer trick to get a value of a variable.

    Here's a sample code that accepts any enumeration type as generic. Note that it will work for usual enumerations only (without fixed values like

    TEnumWontWork = (first = 1, second, third)

    ) and the enum mustn't be declared as local type inside a procedure. In these cases compiler generates no TypeInfo for the enums.

    type
      // Sample generic class that accepts any enumeration type as T
      TEnumArr<T> = class
      strict private
        fArr: array of Byte;
        fIdxType: TOrdType;
        function IdxToInt(idx: T): Int64;
        procedure Put(idx: T; Val: Byte);
        function Get(idx: T): Byte;
      public
        constructor Create;
        property Items[Index: T]: Byte read Get write Put; default;
      end;
    
    constructor TEnumArr<T>.Create;
    var
      pti: PTypeInfo;
      ptd: PTypeData;
    begin
      pti := TypeInfo(T);
      if pti = nil then
        Error('no type info');
      // Perform run-time type check
      if pti^.Kind <> tkEnumeration then
        Error('not an enum');
      // Reach for TTypeData record that goes right after TTypeInfo record
      // Note that SizeOf(pti.Name) won't work here
      ptd := PTypeData(PByte(pti) + SizeOf(pti.Kind) + (Length(pti.Name)+1)*SizeOf(AnsiChar));
      // Init internal array with the max value of enumeration
      SetLength(fArr, ptd.MaxValue);
      // Save ordinal type of the enum
      fIdxType := ptd.OrdType;
    end;
    
    // Converts index given as enumeration item to integer.
    // We can't just typecast here like Int64(idx) because of compiler restrictions so
    //  use pointer tricks. We also check for the ordinal type of idx as it may vary
    //  depending on compiler options and number of items in enumeration.
    function TEnumArr<T>.IdxToInt(idx: T): Int64;
    var
      p: Pointer;
    begin
      p := @idx;
    
      case fIdxType of
        otSByte: Result := PShortInt(p)^;
        otUByte: Result := PByte(p)^;
        otSWord: Result := PSmallInt(p)^;
        otUWord: Result := PWord(p)^;
        otSLong: Result := PLongInt(p)^;
        otULong: Result := PLongWord(p)^;
      end;
    end;
    
    function TEnumArr<T>.Get(idx: T): Byte;
    begin
      Result := fArr[IdxToInt(idx)];
    end;
    
    procedure TEnumArr<T>.Put(idx: T; Val: Byte);
    begin
      fArr[IdxToInt(idx)] := Val;
    end;
    

    Sample of usage:

    type
      TEnum  = (enOne, enTwo, enThree);
    var
      tst: TEnumArr<TEnum>;
    begin
      tst := TEnumArr<TEnum>.Create;
      tst[enTwo] := $FF;
      Log(tst[enTwo]);
    

    As a resume, I used three tricks here:

    1) Getting TypeInfo for T with general props of T

    2) Getting TypeData for T with detailed props of T

    3) Using pointer magic to get the value of parameters given as of type T.

    Hope this help.

    0 讨论(0)
  • 2020-12-06 05:50

    in C#, you can do a typeof(T) which would allow you to do something like

    (T = String)
    

    or

    (T is class)
    

    I havent seen your other question (you didnt link to it), but what are you really looking for? In general, doing something conditional on type or a typecode via ifs like you are doing or a switch is generally best transformed into having an interface or abstract function somewhere that gets customised by context.

    0 讨论(0)
  • 2020-12-06 05:59

    You can fall back to RTTI, by using TypeInfo(T) = TypeInfo(string). To test to see if something is a class, you could use something like PTypeInfo(TypeInfo(T))^.Kind = tkClass.

    The PTypeInfo type and tkClass enumeration member are defined in the TypInfo unit.

    0 讨论(0)
提交回复
热议问题