TThread.Queue overload not found - delphi

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.

Related

How to pass generic procedure TProc<T1,T2> as a parameter and invoke it?

I have a logging class, which links to many modules. The main method of this class is a class method:
type
TSeverity = (seInfo, seWarning, seError);
TLogger = class
class procedure Log(AMessage: String; ASeverity: TSeverity);
end;
Somewhere else I have a function DoSomething() which does some things that I would like to log. However, I do not want to link all the modules of the logger to the module in which 'DoSomething()' is declared to use the logger. Instead I would like to pass an arbitrary logging method as a DoSomething's parameter and call it from its body.
The problem is that TLogger.Log requires parameter of TSeverity type which is defined in logger class. So I can't define a type:
type
TLogProcedure = procedure(AMessage: String; ASverity: TSeverity) of Object;
because I would have to include an unit in which TSeverity is declared.
I was trying to come up with some solution based on generic procedure but I am stuck.
uses
System.SysUtils;
type
TTest = class
public
class function DoSomething<T1, T2>(const ALogProcedure: TProc<T1,T2>): Boolean; overload;
end;
implementation
class function TTest.DoSomething<T1, T2>(const ALogProcedure: TProc<T1, T2>): Boolean;
var
LMessage: String;
LSeverity: Integer;
begin
//Pseudocode here I would like to invoke logging procedure here.
ALogProcedure(T1(LMessage), T2(LSeverity));
end;
Somewehere else in the code I would like to use DoSomething
begin
TTest.DoSomething<String, TSeverity>(Log);
end;
Thanks for help.
Update
Maybe I didn't make myself clear.
unit uDoer;
interface
type
TLogProcedure = procedure(AMessage: String; AErrorLevel: Integer) of Object;
// TDoer knows nothing about logging mechanisms that are used but it allows to pass ALogProcedure as a parameter.
// I thoight that I can somehow generalize this procedure using generics.
type
TDoer = class
public
class function DoSomething(const ALogProcedure: TLogProcedure): Boolean;
end;
implementation
class function TDoer.DoSomething(const ALogProcedure: TLogProcedure): Boolean;
begin
ALogProcedure('test', 1);
Result := True;
end;
end.
Separate unit with one of the logging mechanisms.
unit uLogger;
interface
type
TSeverity = (seInfo, seWarning, seError);
// I know that I could solve my problem by introducing an overloaded method but I don't want to
// do it like this. I thought I can use generics somehow.
TLogger = class
class procedure Log(AMessage: String; ASeverity: TSeverity); {overload;}
{class procedure Log(AMessage: String; ASeverity: Integer); overload;}
end;
implementation
class procedure TLogger.Log(AMessage: String; ASeverity: TSeverity);
begin
//...logging here
end;
{class procedure TLogger.Log(AMessage: String; ASeverity: Integer);
begin
Log(AMessage, TSeverity(ASeverity));
end;}
end.
Sample usage of both units.
implementation
uses
uDoer, uLogger;
procedure TForm10.FormCreate(Sender: TObject);
begin
TDoer.DoSomething(TLogger.Log); //Incompatible types: Integer and TSeverity
end;
Introducing generics here does not help. The actual parameters that you have are not generic. They have fixed type, string and Integer. The function you are passing them to is not generic and receives parameters of type string and TSeverity. These types are mis-matched.
Generics won't help you here because your types are all known ahead of time. There is nothing generic here. What you need to do, somehow, is convert between Integer and TSeverity. Once you can do that then you can call your function.
In your case you should pass a procedure that accepts an Integer, since you don't have TSeverity available at the point where you call the procedure. Then in the implementation of that procedure, where you call the function that does accept a TSeverity, that's where you convert.
In scenarios involving generic procedural types, what you have encountered is quite common. You have a generic procedural type like this:
type
TMyGenericProcedure<T> = procedure(const Arg: T);
In order to call such a procedure you need an instance of T. If you are calling the procedure from a function that is generic on T, then your argument must also be generic. In your case that argument is not generic, it is fixed as Integer. At that point your attempt to use generics unravels.
Having said all of that, what you describe doesn't really hang together at all. How can you possibly come up with the severity argument if you don't know what TSeverity is at that point? That doesn't make any sense to me. How can you just conjure up an integer value and hope that it matches this enumerated type? Some mild re-design would enable you to do this quite simply without any type conversions.
As David Heffernan says, you cannot use generics in this way. Instead you should use a function to map the error level to a severity type, and use that to glue together the two. Based on your updated example, one could modify it like this:
unit uDoer;
interface
type
TLogProcedure = reference to procedure(const AMessage: String; AErrorLevel: Integer);
// TDoer knows nothing about logging mechanisms that are used but it allows to pass ALogProcedure as a parameter.
type
TDoer = class
public
class function DoSomething(const ALogProcedure: TLogProcedure): Boolean;
end;
implementation
class function TDoer.DoSomething(const ALogProcedure: TLogProcedure): Boolean;
begin
ALogProcedure('test', 1);
Result := True;
end;
end.
You can then provide the glue procedure which converts the error level to a severity:
implementation
uses
uDoer, uLogger;
function SeverityFromErrorLevel(const AErrorLevel: Integer): TSeverity;
begin
if (AErrorLevel <= 0) then
result := seInfo
else if (AErrorLevel = 1) then
result := seWarning
else
result := seError;
end;
procedure LogProc(const AMessage: String; AErrorLevel: Integer);
var
severity: TSeverity;
begin
severity := SeverityFromErrorLevel(AErrorLevel);
TLogger.Log(AMessage, severity);
end;
procedure TForm10.FormCreate(Sender: TObject);
begin
TDoer.DoSomething(LogProc);
end;
Note I didn't compile this, but the essence is there. I used a procedure reference (reference to procedure) as they're a lot more flexible, which may come in handy later.

using SuperObject to call procedure inside a Class

i'm trying to call a procedure within a class using super object, but it won't work, what am i doing wrong here ?
Code sample:
program test_rpc;
{$IFDEF FPC}
{$MODE OBJFPC}{$H+}
{$ELSE}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
SysUtils, superobject;
type
TCC = class(TObject)
published
procedure controler_method1(const This, Params: ISuperObject; var Result: ISuperObject);
end;
procedure TCC.controler_method1(const This, Params: ISuperObject; var Result: ISuperObject);
var
i: Integer;
begin
write('action called with params ');
writeln(Params.AsString);
end;
var
s: ISuperObject;
CC: TCC;
begin
CC := TCC.Create;
s := TSuperObject.Create;
s.M['controler.action1'] := CC.MethodAddress('controler_method1');
try
s['controler.action1("HHAHAH")'];
finally
s := nil;
writeln('Press enter ...');
readln;
end;
end.
that will crash, what am i doing wrong here ?
it actually gets to "action called with Params" but fails to show the param...
The super method has signature as follows:
TSuperMethod = procedure(const This, Params: ISuperObject;
var Result: ISuperObject);
This means that you cannot use an instance method since an instance method has an incompatible signature. Your method must look like this:
procedure sm(const This, Params: ISuperObject; var Result: ISuperObject);
begin
....
end;
The reason you get a runtime error rather than a compile time error is that you abandoned the type system by using the # operator. Remove the # and your program will fail at compile time with an error message that is a terser version of what I said above.
It's one of the great fallacies of Delphi programming that one must use the # operator to obtain a function pointer. It's a bad habit that you would do well to unlearn.

How I make RTTI-call with safecall function method of interface?

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.

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

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.

delphi tlb_lib file from com component has no connection between interface and object

After importing into delphi the com dll file, the delphi in turn generated a lib_tlb.pas file.
Inspecting the file it shows
Iinterface1 = interface(IDispatch)
function func: Integer; safecall;
procedure proc(param:Iinterface1);
end;
Cointerface1 = class
class function Create: Iinterface;
class function CreateRemote(const MachineName: string): Iinterface1;
end;
Tinterface1 = class(TOleServer)
function func: Integer;
procedure proc(param:Iinterface1);
end;
Now its clear to see that there is no connection between Tinterface1 and Iinterface1.
The problem comes when one calls proc with an Tinterface1. this will not compile Tinterface1 does not inheritce Iinterface1.
So what suggested to do? change the lib that is auto generated? or do you have a better idea of what to do when wanting to pass Tinterface1 to proc.
The example is a simplification of the code, in the code there is anther object that needs to be the one to be passed to proc, however that proc knows only its interface, which is the same problem.
update: as it seems the manual of the com dll file, says that proc should be
procedure proc(param:^Tinterface1);
where the interface is only in delphi point of view.
TInterface1.Proc() is expecting a pre-existing IInterface1 object to be passed to it as input. Use Cointerface1.Create() to create that object, eg:
var
intf: Iinterface1;
begin
intf := Cointerface1.Create;
TheOleServerInstance.proc(intf);
end;
Tinterface1 is a TOleServer descendant that does not directly inherit from Iinterface1 (but it does wrap an Iinterface1 internally), so you have to cast it whenever you want to pass it where an Iinterface1 is expected, eg:
var
intf: Iinterface1;
svr: Iinterface1;
begin
intf := Cointerface1.Create;
if Supports(TheOleServerInstance, Iinterface1, svr) then
intf.proc(svr);
end;

Resources