Jump to content
dmitrybv

TValue.ToString for TAlphaColor type returns a strange result.

Recommended Posts

Hello.

My TValue.ToString for TAlphaColor type returns a strange result.
I can't figure out if it's a bug or if I'm using the TValue.ToString function incorrectly

Here's an example of code.


 

type
  TColorObj = class(TPersistent)
  private
    FColorProp: TAlphaColor;
  published
    property ColorProp: TAlphaColor read FColorProp write FColorProp;
  end;


procedure TFormSimpleDraw2.Button1Click(Sender: TObject);
var
  RttiContext: TRttiContext;
  Val: TValue;
  LType: TRttiType;
  ColorObj: TColorObj;
  AColorProp: TRttiProperty;
begin
  ColorObj := TColorObj.Create;
  ColorObj.ColorProp := 4294303411; //TAlphaColorRec.Wheat

  RttiContext := TRttiContext.Create;
  LType := RttiContext.GetType(TColorObj);
  AColorProp := LType.GetProperty('ColorProp');
  Val := AColorProp.GetValue(ColorObj);

  //Memo1.Lines.Add('ColorObj.ColorProp.ToString = ' +  ColorObj.ColorProp.ToString);
  Memo1.Lines.Add('ColorObj.ColorProp.ToString = ' +  Cardinal(ColorObj.ColorProp).ToString);
  Memo1.Lines.Add('TValue.ToString  = ' + Val.ToString);
  ColorObj.Free;
end;


Here's the result.

 

ColorObj.ColorProp.ToString = 4294303411
TValue.ToString  = 18446744073708887731

 

Embarcadero® RAD Studio 12 Version 29.0.53571.9782 

Delphi 12 Update 2.

 

Share this post


Link to post

I can reproduce the issue.  Interestingly, if you don't use the TRttiContext and just assign the ColorProp directly to the TValue then TValue.ToString() works as expected:

//Val := AColorProp.GetValue(ColorObj);
Val := ColorObj.ColorProp; // Val.ToString now returns '4294303411'

In both cases, the numeric value (inside the TValue.FAsULong field) is correct, but the RTTI for the value different - in the first case, the TValue.FTypeInfo belongs to TAlphaColor, but in the second case the TValue.FTypeInfo belongs to Cardinal instead!

 

Both RTTIs have Kind=tkInteger and OrdType=otULong, so the numeric value is stored in TValue.FAsULong and TValue.ToString() formats as a UInt64.  But, the TValue.AsUInt64 property getter behaves differently on the two RTTI types:

function TValue.AsUInt64: UInt64;
begin
  if not IsEmpty then
  begin
    if FTypeInfo = System.TypeInfo(Int64) then
      Exit(FAsSInt64)
    else if FTypeInfo = System.TypeInfo(UInt64) then
      Exit(FAsUInt64)
    else if FTypeInfo = System.TypeInfo(Cardinal) then
      Exit(FAsULong) // <-- SECOND CASE
    else if FTypeInfo^.Kind = tkInteger then
      Exit(AsInteger); // <-- FIRST CASE
  end;
  AsTypeInternal(Result, System.TypeInfo(UInt64));
end;

function TValue.AsInteger: Integer;
begin
  if not IsEmpty then
  begin
    if FTypeInfo = System.TypeInfo(Integer) then
      Exit(FAsSLong)
    else if FTypeInfo^.Kind = tkInteger then
      case GetTypeData(FTypeInfo)^.OrdType of
        otSByte: Exit(FAsSByte);
        otSWord: Exit(FAsSWord);
        otSLong: Exit(FAsSLong);
      else
        Exit(FAsULong); // <-- FIRST CASE
      end;
  end;
  AsTypeInternal(Result, System.TypeInfo(Integer));
end;

 

In the first case, TValue.FTypeInfo does not belong to either UInt64 or Cardinal (it belongs to TAlphaColor), so the TValue.FAsULong field (whose unsigned value is 4294303411, hex $FFF5DEB3) first gets converted to a signed Integer, resulting in a negative value of -663885 (hex $FFF5DEB3), which is then converted up to a sign-extended UInt64, resulting in a large unsigned value of 18446744073708887731 (hex $FFFFFFFFFFF5DEB3).

 

In the second case, the TValue.FTypeInfo belongs to Cardinal (an unsigned type), so the TValue.FAsUlong field (hex $FFF5DEB3) is converted as-is to a zero-extended UInt64, preserving the original value (hex $00000000FFF5DEB3).

 

Edited by Remy Lebeau
  • Like 7

Share this post


Link to post
 AColorProp := LType.GetProperty('ColorProp');
  Val := AColorProp.GetValue(ColorObj);

  //Memo1.Lines.Add('ColorObj.ColorProp.ToString = ' +  ColorObj.ColorProp.ToString);
  Memo1.Lines.Add('ColorObj.ColorProp.ToString = ' +  Cardinal(ColorObj.ColorProp).ToString);
  Memo1.Lines.Add('TValue.ToString  = ' + Val.ToString);

Should Val.ToString not be Val.AsType<TColorObj>.ToString ?  

 

I don’t think TValue.ToString is what you were expecting. It is more diagnostic in nature if you look at its implementation, as it supports all kinds of types. I suspect that what you’re seeing is simply an interpretation based on the contained value. However, in RTTI, these values are usually cast to their correct types—especially when passed to a TRttiMethod or similar, where FTypeInfo is considered appropriately.

 

 

 

Edited by darnocian

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×