Andrea Raimondi 13 Posted May 18, 2019 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
Stefan Glienke 2002 Posted May 18, 2019 (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 May 18, 2019 by Stefan Glienke Share this post Link to post
Andrea Raimondi 13 Posted May 18, 2019 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
Remy Lebeau 1393 Posted May 20, 2019 (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 May 20, 2019 by Remy Lebeau 1 Share this post Link to post