View Issue Details

IDProjectCategoryView StatusLast Update
0038381FPCRTLpublic2021-01-31 21:43
Reporterravi dion Assigned To 
PrioritynormalSeverityminorReproducibilityalways
Status newResolutionopen 
Product Version3.3.1 
Summary0038381: Add TValue.Make functions
DescriptionFPC does not yet support Delphi methods:

class procedure Make(AValue: NativeInt; ATypeInfo: PTypeInfo; var Result: TValue); overload; static;
class procedure Make<T>(const Value: T; var Result: TValue); overload; static; inline;

see http://docwiki.embarcadero.com/Libraries/Sydney/en/System.Rtti.TValue.Make
TagsNo tags attached.
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files

Activities

Bi0T1N

2021-01-23 17:53

reporter   ~0128528

Last edited: 2021-01-23 17:54

View 2 revisions

The generic implementation is pretty straight forward, see below. The NativeInt version should be similar to the Pointer method but I don't see why one would need a version for NativeInt?

Implementation:
  TValue = record
  public
    ...
{$ifndef NoGenericMethods}
    generic class procedure Make<T>(const Value: T; out Result: TValue); overload; static; inline;
{$endif}
    ...
  end;

{$ifndef NoGenericMethods}
generic class procedure TValue.Make<T>(const Value: T; out Result: TValue);
begin
  TValue.Make(@Value, TypeInfo(T), Result);
end;
{$endif}


Simple tests:
procedure TTestCase1.TestMakeGenericNil;
var
  value: TValue;
begin
  TValue.specialize Make<TObject>(Nil, value);
  CheckTrue(value.IsEmpty);
  CheckTrue(value.IsObject);
  CheckTrue(value.IsClass);
  CheckTrue(value.IsOrdinal);
  CheckFalse(value.IsArray);
  CheckTrue(value.AsObject=Nil);
  CheckTrue(value.AsClass=Nil);
  CheckTrue(value.AsInterface=Nil);
  CheckEquals(0, value.AsOrdinal);

  TValue.specialize Make<TClass>(Nil, value);
  CheckTrue(value.IsEmpty);
  CheckTrue(value.IsClass);
  CheckTrue(value.IsOrdinal);
  CheckFalse(value.IsArray);
  CheckTrue(value.AsObject=Nil);
  CheckTrue(value.AsClass=Nil);
  CheckTrue(value.AsInterface=Nil);
  CheckEquals(0, value.AsOrdinal);

  TValue.specialize Make<LongInt>(Nil, value);
  CheckTrue(value.IsOrdinal);
  CheckFalse(value.IsEmpty);
  CheckFalse(value.IsClass);
  CheckFalse(value.IsObject);
  CheckFalse(value.IsArray);
  CheckEquals(0, value.AsOrdinal);
  CheckEquals(0, value.AsInteger);
  CheckEquals(0, value.AsInt64);
  CheckEquals(0, value.AsUInt64);

  TValue.specialize Make<String>(Nil, value);
  CheckFalse(value.IsEmpty);
  CheckFalse(value.IsObject);
  CheckFalse(value.IsClass);
  CheckFalse(value.IsArray);
  CheckEquals('', value.AsString);
end;

procedure TTestCase1.TestMakeGenericObject;
var
  AValue: TValue;
  ATestClass: TTestValueClass;
begin
  ATestClass := TTestValueClass.Create;
  ATestClass.AInteger := 54329;
  TValue.specialize Make<TTestValueClass>(ATestClass, AValue);
  CheckEquals(AValue.IsClass, False);
  CheckEquals(AValue.IsObject, True);
  Check(AValue.AsObject=ATestClass);
  Check(PPointer(AValue.GetReferenceToRawData)^ = Pointer(ATestClass));
  CheckEquals(TTestValueClass(AValue.AsObject).AInteger, 54329);
  ATestClass.Free;
end;

procedure TTestCase1.TestMakeGenericDouble;
var
  fd: Double;
  v: TValue;
  hadexcept: Boolean;
begin
  fd := 3.14;

  TValue.specialize Make<Double>(fd, v);
  CheckEquals(v.IsClass, False);
  CheckEquals(v.IsObject, False);
  CheckEquals(v.IsOrdinal, False);
  Check(v.AsExtended=fd);
  Check(v.GetReferenceToRawData <> @fd);

  try
    hadexcept := False;
    v.AsInt64;
  except
    hadexcept := True;
  end;

  CheckTrue(hadexcept, 'No signed type conversion exception');

  try
    hadexcept := False;
    v.AsUInt64;
  except
    hadexcept := True;
  end;

  CheckTrue(hadexcept, 'No unsigned type conversion exception');
end;

Sven Barth

2021-01-24 12:24

manager   ~0128551

The NativeInt variant is required, because Delphi provides it. Though back when I gave it a try I think there were a few problems when making a TypeInfo(AnsiString) and such (or at least to write nice, consistent tests for it...)

Bi0T1N

2021-01-24 21:41

reporter   ~0128574

I thought the code for NativeInt would be:
class procedure Make(Value: NativeInt; TypeInfo: PTypeInfo; out Result: TValue); overload; static; inline;
...
class procedure TValue.Make(Value: NativeInt; TypeInfo: PTypeInfo; out Result: TValue);
begin
  TValue.Make(@Value, TypeInfo, Result);
end;

and that's why I said I don't see why this was added by Delphi in the first place.

Sven Barth

2021-01-25 14:19

manager   ~0128587

Probably some low level functionality inside the Rtti unit uses it (e.g. the Invoke or virtual interface stuff).

Issue History

Date Modified Username Field Change
2021-01-21 16:42 ravi dion New Issue
2021-01-23 17:53 Bi0T1N Note Added: 0128528
2021-01-23 17:54 Bi0T1N Note Edited: 0128528 View Revisions
2021-01-24 12:24 Sven Barth Note Added: 0128551
2021-01-24 21:41 Bi0T1N Note Added: 0128574
2021-01-25 14:19 Sven Barth Note Added: 0128587