Jump to content
JackT

procedure of class

Recommended Posts

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

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 by Remy Lebeau

Share this post


Link to post

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

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

Your descriptions are a bit confusing. Can you show the updated code that is not working the way you want?

Edited by Remy Lebeau

Share this post


Link to post

@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

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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×