JackT 0 Posted Monday at 03:00 PM I am trying to cast a class function to a pointer as opposed to a procedure of object. For example TProcReportError = function(Sender:Tobject;const CrashMsg:String) of object; creates a pointer to a function of an object. I want do the same thing with a class function - for example TProcReportError = procedure(Sender:TObject;const CrashMsg:String) of class; I tried this and then used TProcReportError = procedure(Sender:TObject;const CrashMsg:String); In this real example type TPrepareNewState = function(Sender, Commander: TObject): String; function TN1.ControllerCommand: AnsiString; var CRef:TClass; NS:String; P:Pointer; PNS:TPrepareNewState; Z:TN1; begin if Assigned(Commander) then begin // Commander.PrepareNewState(Self,Commander); NS:=''; CRef := Commander.ControllerClass; if CRef <> nil then begin if CRef.InheritsFrom(TN1BaseAutoCommander) then begin P:= Cref.MethodAddress('PrepareNewState'); if P = nil then begin //raise Exception.Create(CRef.ClassName + ' does not implement PrepareNewState'); RESULT := 'ERROR '+CRef.ClassName + ' does not implement PrepareNewState'; exit; end; ODS('Attempting call on ' + CRef.ClassName); PNS:=P; Z:=Self as TN1; NS:=PNS(Z,Commander); ODS(NS); Commander.TrySwitchState(NS); RESULT := 'OK'; end; end else begin RESULT :='ERROR COMMANDER DOES NOT HAVE A CONTROLLER CLASS'; end; end else begin RESULT := 'ERROR UNASSIGNED COMMANDER'; end; end; It calls this command. The line N1:=Sender As TN1 throws an exception as it doesn't think Sender is a class of TN1, but in the previous procedure we tested this assertion by setting Z := Self As TN1; The only thing I can think is happening is that parameters are being passed incorrectly ? I assumed a class function would behave like a module level function for casting purposes ? Quote class function TN1PassiveController.PrepareNewState(Sender, Commander: TObject): String; var N1:TN1; CS:String; Sarge:TN1Commander; CParams:TArray<String>; begin N1 := Sender As TN1; CParams := N1.GetCommandParameters; N1.DumpCSL; Sarge := Commander As TN1Commander; if Length(CParams) >1 then begin CS := CParams[1]; if CS ='ACTIFY' then begin Sarge.ActiveChannels end; Sarge.TrySwitchState(CParams[1]); Result :='OK'; end else begin Result := 'ERROR NOT ENOUGH PARAMETERS PASSED TO TN1PassiveController'; end; end; Share this post Link to post
Remy Lebeau 1642 Posted Monday at 03:11 PM (edited) Obviously, I'm aware of the existence of class procedures/functions, but I've never seen a "procedure of class" declaration before. I didn't know that was a possibility. In any case, a "class" procedure/function still has a hidden Self parameter, it simply points at the class type instead of an object instance. Your code is not taking that parameter into account. You have to declare a "class" procedure/function as "static" in order to remove its Self parameter. Edited Monday at 08:29 PM by Remy Lebeau Share this post Link to post
JackT 0 Posted Monday at 03:50 PM Thanks for the reply there isn't a procedure of class function but that is what I am trying to do. It's a bit crazy complicated - I have a class reference to an interfaced object which I pass into a threaded object that creates that class. Somewhere down the line I read a string from a piece of hardware and then load up a new class interface to replace the original one depending on the hardware system. This is because there might be multiple versions of the hardware firmware out in the wild. I then want to issue commands to the discovered piece of hardware, but I need to get back the new class reference, so I can call class functions to prepare data for other calls without knowing the specifics of the class I am dealing with. I was using virtual methods, but this doesn't look like it is possible so I will try with static methods and remove the function from the base class. Share this post Link to post
JackT 0 Posted Monday at 04:10 PM Cheers! That worked, so getting the address of a class function that has been declared as a virtual function causes the parameters to that function to be passed incorrectly. This is ok for me but it means you can't use inherit code from ancestor classes I guess. Share this post Link to post
Remy Lebeau 1642 Posted Monday at 08:30 PM (edited) Your descriptions are a bit confusing. Can you show the updated code that is not working the way you want? Edited Monday at 08:31 PM by Remy Lebeau Share this post Link to post
Lars Fosdal 1872 Posted Tuesday at 07:15 AM @JackT So you retrieve a signature from the hardware, and want to have custom classes, either descending from a single parent (with children overriding virtual methods) - or - implementing a common interface (if the classes need to have different parents) - and create device specific class instances that do the appropriate software operations on specific hardware? Sounds to me that you need a Factory that produces your class depending on the signature - where each class register itself with the corresponding signature(s). It would be safer to pass an instance of the class, than a reference to a function of the class. Share this post Link to post
JackT 0 Posted 7 hours ago This is is what I am attempting to do. I have a hardware controller that is running two threads The outer threads accepts commands through a pipe from a controlling program UI. When commanded to do the outer thread spawns a generic controller program on the inner thread, so as not to block the control pipe. From the outer thread I specify a control module, that the inner thread is going to use to do the actual controlling. I don't want to pass an actual object across threads because this is bad thing to do as it can lead to objects getting destroyed at the wrong time therefore I pass a class reference and create that object in the controlling thread. The referenced class is an interfaced object that may or not support specific interfaces, but generally the first object creates a very basic hardware interface that just queries the hardware firmware details. When the initial interfaced object gets the results of the hardware query interface it looks up a specific controller from an internal list and if it is present it creates another controller in the internal thread and sets this to the current controller interface. This destroys the original controller interface and the new controller carries on. If a specific controller isn't found the inner thread throws an exception, stores the error result and terminates. That aside there are circumstances where it is handy to know in the outer thread the current class reference of the controller. So I want to be able to pass back the class reference of the current loaded controller and then use class functions in that class reference to retrieve meta data and helper routines from the class reference that can be used to prepare data before pushing information to the inner thread. There is no language mechanism to declare a procedure of a class, or a function of a class. Even though you can declare class procedures as virtual. I can however get the method address of a virtual class function but I can not cast it to a suitable function Type. I thought class function would all be static in nature so I just tried casting it to a standard module procedure some thing like this. type TMyproc = function(P1,P2:Variant):Variant; class function (P1,P2:Variant):Variant;virtual; can not be cast to a suitable method pointer because there is no language mechanism to do this class function DoSomething():Integer;static; can be cast to TMyProc pointer type because the static key word removes the self reference This means that it is not possible to get a function pointer in a class function or procedure that has been declared virtual and then call it because the function call isn't being fixed up correctly. If the novel language mechanism "procedure / function of class" were implemented then this might be possible. This would then be a handy way of passing metadata between threads, for example it could pass a list of supported commands or a data decompression routine without having to instantiate an object an actual object, which could be overridden with updated versions in descendant classes. ------>OUTER THREAD--------> START INNER THREAD(INIITAL CLASSREF) CREATE BASIC CONTROLLER HERE USING CLASSREF QUERY HARDWARE REPLACE CONTROLLER WITH DESENDANT CONTROLLER THAT INHERITS FROM BASIC CONTROLLER ASK FOR INNER CLASS REF --> LOOP DO CONTROLLER ACTIONS UNTIL TERMINATED ------>OUTER THREAD-------->INNER THREAD Share this post Link to post