Recent

Author Topic: Procedure of Object to Pointer  (Read 1397 times)

DelphiDinosaur

  • New Member
  • *
  • Posts: 23
Procedure of Object to Pointer
« on: October 14, 2025, 01:49:39 pm »
Hello,

I have a dll that parses data being passed to it, and when required triggers a call back routine/event.
Code: Pascal  [Select][+][-]
  1. //dll
  2. TCallBack = procedure();
  3. FCallBack: TCallBack;
  4.  
  5. procedure SetCallback(ACallBack: TCallBack);
  6. begin
  7.   FCallBack := ACallBack;
  8. end;
  9.  
  10. procedure TriggerCallBack();
  11. begin
  12.  if Assigned(FCallBack) then
  13.    FCallBack();
  14.  
  15. end;
  16.  

On a basic program I'd use:
Code: Pascal  [Select][+][-]
  1. procedure MyCallback();
  2. begin
  3. //do something
  4. end;
  5.  
  6. SetCallback(@MyCallback);
  7.  

This works. When TriggerCallBack() is called, MyCallback is called too. Happy days!

However, if I want to use the dll within a class structure I can't use an object routine:
Code: Pascal  [Select][+][-]
  1. procedure Foo.MyCallback();
  2. begin
  3. //do something
  4. end;
  5.  
  6. SetCallback(@self.MyCallback);
  7.  

I'm aware this does't work because the object routine is wrapped as a TMethod record with additional pointer information. I'm aware that changing the definition of TCallBack will fix the problem:
Code: Pascal  [Select][+][-]
  1. TCallBack = procedure() of object;
But then my existing code using non-object functions won't then work. Short of creating duplicate event handlers (one of object, one not) I'm at a loss as to how to have one interface.

Is there any way of assigning the object routine to the same type of pointer used by the basic function?


Khrys

  • Sr. Member
  • ****
  • Posts: 348
Re: Procedure of Object to Pointer
« Reply #1 on: October 14, 2025, 02:54:40 pm »
You could extract the function pointer from the underlying  TMethod:

Code: Pascal  [Select][+][-]
  1. SetCallback(TCallBack(TMethod(@Self.MyCallback).Code));

Needless to say, this is incredibly unsafe. You might get away with it as long as  MyCallback  doesn't use any parameters - not even the implicit  Self  parameter (e.g. no access to instance members, no virtual function calls), but it would be a loaded footgun nonetheless.

If your goal is simply to encapsulate the callback within a class, you could declare it as a static class procedure (making it equivalent to a plain function without any hidden parameters):

Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = class
  3.     class procedure Callback(); static;
  4.   end;
  5.  
  6. SetCallback(@TFoo.Callback);

cdbc

  • Hero Member
  • *****
  • Posts: 2477
    • http://www.cdbc.dk
Re: Procedure of Object to Pointer
« Reply #2 on: October 14, 2025, 03:30:54 pm »
Hi
Do you have control over the code that resides in the library?!?
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

old_DOS_err

  • New Member
  • *
  • Posts: 44
Re: Procedure of Object to Pointer
« Reply #3 on: October 14, 2025, 03:54:16 pm »
[Hi Benny, sending him the link to the discussion here about incorporating a dll, I found that very helpful. Hope this is ok.]

Hi, I suspect from your handle and that you are using old style procedural types that, like me, you come from Turbro Pascal. I had a major problem changing over from traditional procedural types to class types, and without knowing about Of Object, I got the dreaded zombie object (a recognised term, can't be killed and if left causes a memory leak).

I must admit I don't fully understand what your actual problem is and I don't to speculate as I got told off for that recently in this forum, which was fair enough.

Anyway, a couple of avenues you might want to look at that go slightly off the beaten path, incorporating the DLL into an executable (all kinds of issues with that, including legal) or execute a method.

This is a link to the incorporate discussion here, look at BobDog's code on page 2, I seem to recall trying it recently and it worked (my memory is not what it used to be).

https://forum.lazarus.freepascal.org/index.php/topic,58919.15.html

Again, I don't really want to expand on that, just putting it as something to look at for ideas.

This is the link for the execute method, I have this placed into one of my libraries:

https://gist.github.com/ik5/2950789

I would say finally, without speculating too much, that I'm not sure that free pascal will allow you the luxury of a single way to incorporate both old and new style procedural methods, but I’m very happy to be proved wrong on that. So I think you need an interface, a medium that both can go through. That would be my way of going. I’m happy to have a further look into this, give me a few days and I’ll come back to this unless one of the smart bods has given you a solution or you’ve found your own.

Phil

DelphiDinosaur

  • New Member
  • *
  • Posts: 23
Re: Procedure of Object to Pointer
« Reply #4 on: October 14, 2025, 03:57:33 pm »
@Khrys sadly the callback function returns a pointer to an array and the length of the array.
Code: Pascal  [Select][+][-]
  1. procedure MyCallback(Data: PByte; Len:word);  

I've tried the static class function call; It worked, but from what I've seen it appears to be a dead end, not being able to access methods within the rest of the class. I might be missing something though

@cbdc, yes I have control over the code that resides in the library, hence the ability to add the "as object" version of the callback function. This is what I currently have working.

To add to the complication, what ever I end up with also needs the dll to be C# compatible. The original code using a non-object routine was usable by both C and Laz/FPC. Ideally it also needs to be capable of cross compiling for Windows and Linux platforms.

Zvoni

  • Hero Member
  • *****
  • Posts: 3140
Re: Procedure of Object to Pointer
« Reply #5 on: October 14, 2025, 04:06:14 pm »
What about an overload in the DLL?
Something along the lines of
Code: Pascal  [Select][+][-]
  1. //dll
  2. TCallBack = procedure();
  3. TCallBackObj = procedure() Of Object;
  4. FCallBack: TCallBack;
  5. FCallBackObj: TCallBackObj;
  6. FOptionalObject:TObject;
  7.  
  8. procedure SetCallback(ACallBack: TCallBack);Overload;
  9. begin
  10.   FCallBack := ACallBack;
  11.   FOptionalObject:=Nil;
  12. end;
  13. procedure SetCallback(ACallBack: TCallBackObj; AClass:TObject);Overload;
  14. begin
  15.   FCallBackObj := ACallBack;
  16.   FOptionalObject:=AClass;
  17. end;
  18.  
  19. procedure TriggerCallBack();
  20. begin
  21.  If Assigned(FOptionalObject) Then
  22.    Begin
  23.       If Assigned(FCallBackObj) Then FCallBackObj;
  24.    End
  25.  Else
  26.    if Assigned(FCallBack) then FCallBack();
  27. end;

Just thinking aloud.....
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

DelphiDinosaur

  • New Member
  • *
  • Posts: 23
Re: Procedure of Object to Pointer
« Reply #6 on: October 14, 2025, 04:09:22 pm »
@old_DOS_err thanks for the links - I'll take a look.

Embedding the dll in the executable isn't workable - I need the dll to be replaceable without re-compiling the calling exe.

As an aside, to keep exe and dll together for initial distribution I do often add the dll as a resource which I extract on first run. I've also taken a different approach where the exe and the dll share the same functions, with the exe using the dll version of the function if it exists, but falling back to the internal function if it doesn't. This allows the dll to be replaced with an older/newer/custom version as required.

Despite the handle, I'm more than happy with object orientated code, including assigning and calling event handling functions from with in them. It's doing the call-backs from the dll which is new to me.


My current implementation is along the lines of:
Code: Pascal  [Select][+][-]
  1. //dll
  2. TCallBack = procedure();
  3. TCallBackObj = procedure() of object;
  4.  
  5. FCallBack: TCallBack;
  6. FCallBackObj: TCallBackObj;
  7.  
  8. procedure SetCallback(ACallBack: TCallBack);
  9. begin
  10.   FCallBack := ACallBack;
  11. end;
  12.  
  13. procedure SetCallback(ACallBack: TCallBackObj);
  14. begin
  15.   FCallBackObj:= ACallBack;
  16. end;
  17.  
  18. procedure TriggerCallBack();
  19. begin
  20.  if Assigned(FCallBack) then
  21.    FCallBack();
  22.  if Assigned(FCallBackObj) then
  23.    FCallBackObj();
  24.  end;

Whilst this works and allows me to move forward, I was hoping there was something a little more refined available.

Zvoni

  • Hero Member
  • *****
  • Posts: 3140
Re: Procedure of Object to Pointer
« Reply #7 on: October 14, 2025, 04:12:15 pm »
@old_DOS_err thanks for the links - I'll take a look.

Embedding the dll in the executable isn't workable - I need the dll to be replaceable without re-compiling the calling exe.

As an aside, to keep exe and dll together for initial distribution I do often add the dll as a resource which I extract on first run. I've also taken a different approach where the exe and the dll share the same functions, with the exe using the dll version of the function if it exists, but falling back to the internal function if it doesn't. This allows the dll to be replaced with an older/newer/custom version as required.

Despite the handle, I'm more than happy with object orientated code, including assigning and calling event handling functions from with in them. It's doing the call-backs from the dll which is new to me.


My current implementation is along the lines of:
Code: Pascal  [Select][+][-]
  1. //dll
  2. TCallBack = procedure();
  3. TCallBackObj = procedure() of object;
  4.  
  5. FCallBack: TCallBack;
  6. FCallBackObj: TCallBackObj;
  7.  
  8. procedure SetCallback(ACallBack: TCallBack);
  9. begin
  10.   FCallBack := ACallBack;
  11. end;
  12.  
  13. procedure SetCallback(ACallBack: TCallBackObj);
  14. begin
  15.   FCallBackObj:= ACallBack;
  16. end;
  17.  
  18. procedure TriggerCallBack();
  19. begin
  20.  if Assigned(FCallBack) then
  21.    FCallBack();
  22.  if Assigned(FCallBackObj) then
  23.    FCallBackObj();
  24.  end;

Whilst this works and allows me to move forward, I was hoping there was something a little more refined available.
Be careful: Above code executes both Callbacks if for any reason both CallBacks are set.
I'd go with an either/or
OTOH, only ou know how the DLL is used, so there is that :)  :P
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

varianus

  • New Member
  • *
  • Posts: 27
Re: Procedure of Object to Pointer
« Reply #8 on: October 14, 2025, 04:42:05 pm »
With some library I've used a mixed approach, adding an opaque parameter to callback.

Something along the lines:
Code: Pascal  [Select][+][-]
  1. //dll
  2. TCallBack = procedure(Data:pointer);
  3. FCallBack: TCallBack;
  4. FData:pointer;
  5.  
  6. procedure SetCallback(ACallBack: TCallBack; data:pointer);Overload;
  7. begin
  8.   FCallBack := ACallBack;
  9.   FData:=Data;
  10. end;
  11.  
  12. procedure TriggerCallBack();
  13. begin
  14.    if Assigned(FCallBack) then FCallBack(Data);
  15. end;
  16.  
  17.  
  18. //On the application
  19.  
  20. TFoo = class
  21. ...
  22.   procedure ProcessCallBack; // where you do something
  23.   procedure RegisterCallback;
  24. end;
  25.  
  26. procedure TFoo.RegisterCallback;
  27. begin
  28.   SetCallback(@MyCallback, self);
  29. end;
  30.  
  31.  
  32. procedure MyCallback(Data:pointer);
  33. begin
  34.   TFoo(Data).ProcessCallBack;
  35. end;

cdbc

  • Hero Member
  • *****
  • Posts: 2477
    • http://www.cdbc.dk
Re: Procedure of Object to Pointer
« Reply #9 on: October 14, 2025, 07:37:45 pm »
Hi
I was thinking the same as @varianus  ;D
So without further ado, here's a demo attached...  8-)
Very crude, but hey...
It's coded on a Linux-box, so you prolly have to change the paths etc...
Have fun & regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

Khrys

  • Sr. Member
  • ****
  • Posts: 348
Re: Procedure of Object to Pointer
« Reply #10 on: October 15, 2025, 07:26:29 am »
Since you have control over the DLL code I'd go with what @varianus suggested, which is a very common pattern:

Code: Pascal  [Select][+][-]
  1. type
  2.   TCallback = procedure (Context: Pointer; Data: PByte; Len: Word); stdcall;
  3.  
  4. procedure SetCallback(Callback: TCallback; Context: Pointer);

Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = class
  3.     class procedure GlobalCallback(Context: Pointer; Data: PByte; Len: Word); static; stdcall;
  4.     procedure Callback(Data: PByte; Len: Word);
  5.   end;
  6.  
  7. class procedure TFoo.GlobalCallback(Context: Pointer; Data: PByte; Len: Word);
  8. begin
  9.   TFoo(Context).Callback(Data, Len);
  10. end;
  11.  
  12. procedure TFoo.Callback(Data: PByte; Len: Word);
  13. begin
  14.   // Can access `Self` here
  15. end;
  16.  
  17. procedure Main();
  18. var
  19.   Foo: TFoo;
  20. begin
  21.  
  22.   Foo := TFoo.Create();
  23.   SetCallback(@TFoo.GlobalCallback, Foo); // <--- Pass `TFoo` instance as `Context`
  24.  
  25.   // Wait for completion
  26.   SetCallback(Nil, Nil);
  27.   Foo.Free();
  28. end;
« Last Edit: October 20, 2025, 10:28:39 am by Khrys »

PascalDragon

  • Hero Member
  • *****
  • Posts: 6195
  • Compiler Developer
Re: Procedure of Object to Pointer
« Reply #11 on: October 16, 2025, 11:11:16 pm »
Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = class
  3.     class procedure GlobalCallback(Context: Pointer; Data: PByte; Len: Word); stdcall;
  4.     procedure Callback(Data: PByte; Len: Word);
  5.   end;

Nearly. You also need to declare GlobalCallback as static (before or after the stdcall) in addition to class procedure, because a non-static class method still has a Self pointer (pointing to the class type instead of the instance).

Khrys

  • Sr. Member
  • ****
  • Posts: 348
Re: Procedure of Object to Pointer
« Reply #12 on: October 20, 2025, 06:39:50 am »
Nearly. You also need to declare GlobalCallback as static (before or after the stdcall) in addition to class procedure, because a non-static class method still has a Self pointer (pointing to the class type instead of the instance).

Yup, forgot the most important part.  %)
Fixed it to avoid confusion.

cdbc

  • Hero Member
  • *****
  • Posts: 2477
    • http://www.cdbc.dk
Re: Procedure of Object to Pointer
« Reply #13 on: October 20, 2025, 07:51:03 am »
Hi
@Khrys: Sorry mate, you fixed it in the wrong place... The 'static' keyword should be place here:
Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = class
  3.     class procedure GlobalCallback(Context: Pointer; Data: PByte; Len: Word); static; stdcall;
  4.     procedure Callback(Data: PByte; Len: Word);
  5.   end;
instead of
Code: Pascal  [Select][+][-]
  1. type  /// wrong!
  2.   TCallback = procedure (Context: Pointer; Data: PByte; Len: Word); static; WRONG;
Regards Benny
« Last Edit: October 20, 2025, 07:53:01 am by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

Thaddy

  • Hero Member
  • *****
  • Posts: 18376
  • Here stood a man who saw the Elbe and jumped it.
Re: Procedure of Object to Pointer
« Reply #14 on: October 20, 2025, 08:03:10 am »
@cdbc
Both are equivalent: a static class procedure has the same signature as a normal procedure.
Only the decoration with static in a normal procedure is not necessary.(but legal)
Code: Pascal  [Select][+][-]
  1. type  /// right!
  2.   TCallback = procedure (Context: Pointer; Data: PByte; Len: Word); static; //right

Oh, I see he left it out in the class.. that needs static indeed.
« Last Edit: October 20, 2025, 08:10:14 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

 

TinyPortal © 2005-2018