Jump to content
Andrea Raimondi

RTTI usage and edge cases :) Can't use GetType on T

Recommended Posts

Hello!

 

I am building a generic CSV importer. 

The basic idea is to have an importer that I can call like so:

var MyImporter : TCSVImporter;
     MyData : TMyData;

     MyDataList : TList<TMyData>;

begin

  MyImporter := TCSVImporter.Create;
  MyDataList := TList<TMyData>.Create;
  MyDataList.Capacity := 1000;
  MyImporter.LoadCSV<TMyData>( FileName, Separator );

  while MyImporter.NextLine<TMyData>( MyData ) do

    begin

       MyDataList.Add( MyData );

    end;

 MyImporter.Free;

end;

The problem is that Delphi has become picky over what constitutes a "type". 

This is the problematic code:

procedure TCSVSchemaBuilder.BuldSchema<T>(SchemaTemplate: T);
var AType : TRttiType;
begin
  FSchemaItems.Clear;
  FNumRequired := 0;
  // Get type
  AType := RTTIContext.GetType(  TypeInfo(SchemaTemplate) );
  // if it's one of the correct types
  if AType.TypeKind in [ tkRecord,tkClass,tkInterface ] then
    begin
      if AType.TypeKind = tkClass then
        BuildSchemaFromClass( AType )
      else if AType.TypeKind = tkRecord then
             begin
               if AType.IsRecord then
                 BuildSchemaFromRecord( AType.AsRecord );
             end
      else
        begin
          // If it's not a record or class, but it's still good, then it must be an interface type!
          BuildSchemaFromInterface( AType as TRttiInterfaceType );
        end;
    end
  else
    begin
      raise EImportSchema.Create('Cannot create schema by type '+AType.Name );
    end;
  if FSchemaItems.Count = 0 then
    raise EImportSchema.Create('Schema is missing critical information. Cannot proceed for '+AType.Name);
end;

The error I get is:

Quote

[dcc32 Error] BothSides.Import.Core.pas(317): E2019 Object type required

Which is obviously a load of infamy because I have T there and that should be considered a type. This is especially true if we're saying that generics are done at compile time, an invalid type would have to block compilation. 

What are your thoughts? Workarounds? Should this be submitted to QC because it is clearly a compiler bug?

 

Kind Regards,

 

A

Share this post


Link to post
Posted (edited)

Wrong: TypeInfo(SchemaTemplate)
Right: TypeInfo(T);

 

Also fwiw making the entire method generic although you only need the typeinfo of T supports code bloat - it would be better to make it non generic and a small generic overload that just passes down the typeinfo to the non generic one.

Edited by Stefan Glienke

Share this post


Link to post
3 hours ago, Stefan Glienke said:

Wrong: TypeInfo(SchemaTemplate)
Right: TypeInfo(T);

 

Also fwiw making the entire method generic although you only need the typeinfo of T supports code bloat - it would be better to make it non generic and a small generic overload that just passes down the typeinfo to the non generic one.

Yeah, makes sense what you're saying, I'll have a look at this, First thing though I want to make it work 🙂 

Share this post


Link to post
Posted (edited)
On 5/18/2019 at 6:42 AM, Stefan Glienke said:

Wrong: TypeInfo(SchemaTemplate)
Right: TypeInfo(T);

In addition, in XE7+ the compiler has an (undocumented) GetTypeKind() intrinsic function:

function GetTypeKind(T: TypeIdentifier): TTypeKind; 

So you can do this instead:

procedure TCSVSchemaBuilder.BuldSchema<T>(SchemaTemplate: T);
begin
  FSchemaItems.Clear;
  FNumRequired := 0;
  case GetTypeKind(T) of
    tkClass: BuildSchemaFromClass(...);
    tkRecord: BuildSchemaFromRecord(...);
    tkInterface: BuildSchemaFromInterface(...);
  else
    raise EImportSchema.Create('Cannot create schema by type');
  end;
  if FSchemaItems.Count = 0 then
    raise EImportSchema.Create('Schema is missing critical information. Cannot proceed');
end;

Prior to XE7, you can replace GetTypeKind() with this instead:

case PTypeInfo(TypeInfo(T)).Kind of

Either way, the advantage is that T's Kind is known at compile time, which allows this code to optimize out unused code branches at compile-time, whereas the original code does not allow that since the Kind is being queried dynamically at runtime.

Edited by Remy Lebeau
  • Like 1

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

×