I'm trying to call a function returning an interface from another unit; for instance consider the following:
program intf_sb1;
{$APPTYPE CONSOLE}
uses
myunit in 'myunit.pas';
var
MyBL: ISomeInterface;
begin
MyBL := GetInterface;
end.
where the content of myunit.pas is as follows:
unit myunit;
interface
type
ISomeInterface = interface
['{D25A26ED-7665-4091-9B0F-24DF37545E2A}']
end;
implementation
function GetInterface : ISomeInterface;
begin
end;
end.
My problem is that I get the error "E2003 Undecleared identifier GetInterface" when I try to run this program. Why isn't this allowed? Thanks in advance!
Declare the GetInterface function in the interface section as well. If you don't it is "private" to the unit.
IE:
type
ISomeInterface
...
end;
function GetInterface: ISomeInterface;
implementation
function GetInterface: ISomeInterface;
begin
...
end;
Related
This simple program doesn't compile. [Tested with XE5 and D10.]
program Project10;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.Classes;
function MakeProc: TThreadProcedure;
begin
Result := procedure begin end;
end;
begin
TThread.Queue(nil, MakeProc);
end.
Compiler reports error
[dcc32 Error] Project10.dpr(16): E2250 There is no overloaded version of 'Queue' that can be called with these arguments
in the TThread.Queue call.
Class TThread implements two Queue overloads.
class procedure Queue(const AThread: TThread; AMethod: TThreadMethod); overload; static;
class procedure Queue(AThread: TThread; AThreadProc: TThreadProcedure); overload; static;
I'm pretty sure that my code should match the second overload.
The only workaround I was able to find is this:
TThread.Queue(nil, procedure begin MakeProc; end);
Am I doing something wrong or is this a compiler bug? Is there a better workaround than my ugly hack?
The compiler evidently thinks you're trying to pass MakeProc itself as the argument. You can tell the compiler that you intend to call that function instead by adding parentheses, just as you would if the function took parameters:
TThread.Queue(nil, MakeProc());
Your workaround wouldn't seem to work. It would compile and run, but the function returned by MakeProc would never execute. Instead, the anonymous method wrapping MakeProc would run, call MakeProc, and then discard that function's result. (Since the function's result doesn't do anything in the code you've provided, you might not have noticed the difference.)
TThread.Queue method takes anonymous procedure as an argument. You cannot reference usual procedure in place of anonymous procedure. But you can call overloaded TThread.Queue method which takes class method reference as an argument. See example below:
type
TMyTestClass = class
public
procedure ThreadProc;
end;
{ TMyTestClass }
procedure TMyTestClass.ThreadProc;
begin
WriteLn('We are in thread');
end;
var
MyTestClass: TMyTestClass;
begin
with TMyTestClass.Create do
try
TThread.Queue(nil, ThreadProc);
finally
Free;
end;
end.
I have this test program https://gist.github.com/real-mielofon/5002732
RttiValue := RttiMethod.Invoke(RttiInstance, [10]);
and simple unit with interface:
unit Unit163;
interface
type
{$M+}
ISafeIntf = interface
function TestMethod(aI: integer): integer; safecall;
end;
{$M-}
type
TSafeClass = class(TInterfacedObject, ISafeIntf)
public
function TestMethod(aI: integer): integer; safecall;
end;
implementation
function TSafeClass.TestMethod(aI: integer): integer;
begin
result := aI+1; // Exception !!
end;
end.
and I have kaboom on
result := aI+1;
if it is procedure or isn't safecall, then it's all right :-(
Having now tried this myself, and looked at the the code, my conclusion is that there is a bug. The RTTI unit does indeed attempt to perform safecall method re-writing. It just appears to get it wrong. I recommend that you submit your project as a QC report, and workaround the problem by using stdcall with HRESULT return values.
I'm trying to get hold of an object using TRttiContext.FindType(QualifiedTypeName). Here's what I've got:
program MissingRTTI;
{$APPTYPE CONSOLE}
uses System.SysUtils, RTTI, Classes;
type
TMyClass = class(TObject) end;
var
rCtx: TRttiContext;
rType: TRttiInstanceType;
begin
rCtx := TRttiContext.Create();
rType := rCtx.GetType(TypeInfo(TMyClass)) as TRttiInstanceType;
if (rType <> nil) then begin
WriteLn('Type found using TypeInfo');
end;
rType := rCtx.FindType(TMyClass.QualifiedClassName) as TRttiInstanceType;
if (rType <> nil) then begin
WriteLn('Type found using qualified class name.');
end;
ReadLn;
rCtx.Free();
end.
Unfortunately, only rCtx.GetType seems to find the desired type. (I've also tried to list all types using GetTypes. The desired type does not appear in the resulting array.) Anyone know how to force the compiler to emit RTTI for this type?
Your call to the FindType method doesn't return Rtti info because this function works only for public types. So if you check the rType.IsPublicType property the value returned is false .
The public types must be declarated in the interface section of a unit (to be recognized as public). So if you move the TMyClass class definition to the interface part of a unit you will able to use the FindType without problems.
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).
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.
I used {$STRONGLINKTYPES ON} and worked very well. Put it on main unit.
I am using Delphi XE.
The following unit fails to compile with this error:
[DCC Error] GTSJSONSerializer.pas(27): E2506 Method of parameterized type declared
in interface section must not use
local symbol 'TSuperRttiContext.AsJson<GTSJSONSerializer.TGTSJSONSerializer<T>.T>'
Why is that? Is there a workaround?
unit GTSJSONSerializer;
interface
type
TGTSJSONSerializer<T> = class
class function SerializeObjectToJSON(const aObject: T): string;
class function DeserializeJSONToObject(const aJSON: string): T;
end;
implementation
uses
SuperObject
;
class function TGTSJSONSerializer<T>.SerializeObjectToJSON(const aObject: T): string;
var
SRC: TSuperRttiContext;
begin
SRC := TSuperRttiContext.Create;
try
Result := SRC.AsJson<T>(aObject).AsString;
finally
SRC.Free;
end;
end;
class function TGTSJSONSerializer<T>.DeserializeJSONToObject(const aJSON: string): T;
var
LocalSO: ISuperObject;
SRC: TSuperRttiContext;
begin
SRC := TSuperRttiContext.Create;
try
LocalSO := SO(aJSON);
Result := SRC.AsType<T>(LocalSO);
finally
SRC.Free;
end;
end;
end.
From the XE2 DocWiki:
This happens when trying to assign a literal value to a generics data field.
program E2506;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TRec<T> = record
public
class var x: Integer;
class constructor Create;
end;
class constructor TRec<T>.Create;
begin
x := 4; // <-- e2506 Fix: overload the Create method to
// take one parameter x and assign it to the x field.
end;
begin
Writeln('E2506 Method of parameterized type declared' +
' in interface section must not use local symbol');
end.
I can't tell which of the local variables it might be objecting to, though; you have one local in SerialObjectToJSON and two in DeserializeJSONToObject. I'm also not sure based on the linked fix exactly how that applies to the code you posted. Could it be related to TSuperRTTIContext?
I can compile your unit with D2010, DXE and DXE2 against SuperObject revision 46.