How can I make sure RTTI is available for a class without instantiating it?
Asked Answered
N

2

7

I've recently posted a question in this forum asking for any advice regarding missing RTTI information in a DXE2 executable.

That post was a stripped down version of my actual case. RRUZ came to the rescue, and so the stripped down version was quickly resolved. The original problem, though, is still standing, and so I'm posting it in full now. "Main":

program MissingRTTI;
{$APPTYPE CONSOLE}
uses
  System.SysUtils, RTTI, MyUnit in 'MyUnit.pas', RTTIUtil in 'RTTIUtil.pas';
var
  RHelp:  TRttiHelper;
begin
  RHelp := TRttiHelper.Create();
  if (RHelp.IsTypeFound('MyUnit.TMyClass')) then WriteLn('TMyClass was found.')
  else WriteLn('TMyClass was not found.');
  ReadLn;
  RHelp.Free();
end.

RTTIUtil.pas:

unit RTTIUtil;
interface
uses
  MyUnit;
type
  TRttiHelper = class(TObject)
  public
    function IsTypeFound(TypeName: string) : boolean;
  end;
implementation
uses
  RTTI;
function TRttiHelper.IsTypeFound(TypeName: string): boolean;
var
  rCtx:   TRttiContext;
  rType:  TRttiType;
begin
  Result := false;
  rCtx := TRttiContext.Create();
  rType := rCtx.FindType(TypeName);
  if (rType <> nil) then
    Result := true;
  rCtx.Free();
end;
end.

and finally MyUnit.pas:

unit MyUnit;
interface
type
  TMyClass = class(TObject)
  end;
implementation
end.

The desired type is not found. However, if I change TRttiHelper.IsTypeFound so that it instantiates (and immediately frees) an instance of TMyClass, the type is found. Like so:

function TRttiHelper.IsTypeFound(TypeName: string): boolean;
var
  rCtx:   TRttiContext;
  rType:  TRttiType;
  MyObj:  TMyClass;
begin
  Result := false;
  MyObj:= TMyClass.Create();
  MyObj.Free();
  rCtx := TRttiContext.Create();
  ...

So I'm wondering, is there any way I can force RTTI to be emitted for TMyClass without actually instantiating it?

Update:

On a side not, I might mention that if I try to fetch the TRttiType using TRttiContext.GetType, the desired type is found. So there is some RTTI emitted. Checking the TRttiType.IsPublic property as retrieved by TRttiContext.GetType yields a true value, i.e. the retrieved type is public (and hence should be possible to locate using TRttiContext.FindType).

Nestornestorian answered 16/5, 2012 at 6:26 Comment(3)
Possible duplicate Delphi 2010 RTTI - RttiContext.FindType. The class in never used in the application and stripped by the compiler.Cadman
Yup, that's confirmed. Thanks @LURD. :)Nestornestorian
They're about the same topic, @Lurd, but they're not duplicates. That question asks what's going on. This one asks how to solve it without instantiating.Stonedeaf
B
17

Add a reference to the class and make sure that the compiler/linker cannot strip it from the executable.

unit MyUnit;

interface

type
  TMyClass = class(TObject)
  end;

implementation 

procedure ForceReferenceToClass(C: TClass);
begin
end;

initialization
  ForceReferenceToClass(TMyClass);

end.

In production code you would want to place ForceReferenceToClass in a base unit so that it could be shared. The initialization section of the unit that declares the class is the most natural place for the calls to ForceReferenceToClass since the unit is then self-contained.

Regarding your observation that GetType can locate the type, the very act of calling GetType(TMyClass) adds a reference to the type to the program. It's not that the RTTI is present and FindType cannot find it. Rather, the inclusion of GetType(TMyClass) adds the RTTI to the resulting program.

Banderole answered 16/5, 2012 at 6:36 Comment(4)
Thank's David. Yeah (smack to forhead) - that's true, the reference will of course cause emission of RTTI. :) +1Nestornestorian
Would be nice, though, if Embarcadero would come up with a compiler directive that forced output for non-referenced types. I even have a name for it: {$FORCERTTI} and the accompanying shortcut {$M!}.Nestornestorian
Calling some class method of TMyClass (like ClassInfo) would be enough - no need for some dummy routine there.Blackcap
@Nestornestorian if you uses the {$STRONGLINKTYPES ON} directive all the types will be added to final exe.Shulman
D
4

I used {$STRONGLINKTYPES ON} and worked very well. Put it on main unit.

Detector answered 19/9, 2015 at 22:23 Comment(1)
+1 This includes RTTI for all types that support it. (It is a correct answer). It's very useful if that's what you want. David Heffernan's solution is better (smaller executable size) if you just want RTTI for a small number of (otherwise) unreferenced types. (I used this for a data migration tool, imports/exports binary flat files, given the file and the name of its record definition at runtime).Ream

© 2022 - 2024 — McMap. All rights reserved.