Recent

Author Topic: [SOLVED] c callback function crash the program  (Read 6076 times)

mercury

  • Full Member
  • ***
  • Posts: 154
[SOLVED] c callback function crash the program
« on: March 06, 2016, 07:36:31 am »
I'm making a c lib for Pascal, but get crash on a callback function.
Where am I mistake?

libhoge.h
Code: C  [Select][+][-]
  1. int hoge_count = 0;
  2. extern int hoge1(int i);
  3. typedef void (*TCallback)(int, int);
  4. TCallback CallbackFunction;
  5. extern int hoge_SetCallback(TCallback ACallback);
  6.  

libhoge.c
Code: C  [Select][+][-]
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <windows.h>
  4.  
  5. #include "libhoge.h"
  6.  
  7. int hoge1(int i) {
  8.   printf("hoge1\n");
  9.  
  10.   Sleep(1000);
  11.  
  12.   if (CallbackFunction != NULL) {
  13.     printf("CallbackFunction\n");
  14.     CallbackFunction(111, 222);
  15.   }
  16.  
  17.   hoge_count = hoge_count + i;
  18.   return (hoge_count);
  19. }
  20.  
  21. int hoge_SetCallback(TCallback ACallback) {
  22.   printf("hoge_SetCallback\n");
  23.  
  24.   CallbackFunction = ACallback;
  25.   return 1;
  26. }
  27.  

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FFCallback(i, j: integer); cdecl;
  2. begin
  3.   Memo1.Append(IntToStr(i) + ' - ' + IntToStr(j));  <------- crash here
  4. end;
  5.  

Full source see the attachment.
Open .dev file with Dev-Cpp.

Thank you very much.

« Last Edit: March 07, 2016, 11:10:59 am by mercury »

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: c callback function crash the program
« Reply #1 on: March 06, 2016, 07:55:40 am »
The callback is not a procedure of object.
Code: Pascal  [Select][+][-]
  1. type
  2.   TFFCallback = procedure(i, j: integer) of object cdecl;
  3. // should be something like:
  4. type
  5.   TFFCallback = procedure(i, j: integer); cdecl;
  6.  

That's because a procedure of object has a hidden self  parameter and that is missing in the c code:
Code: Javascript  [Select][+][-]
  1. typedef void (*TCallback)(int, int);
  2. // should look like this for procedure of object, but I would not do it like that:
  3. typedef void (*TCallback)(void * , int, int);
It really looks like:
Code: Pascal  [Select][+][-]
  1. type
  2.   TFFCallback = procedure(_self, i, j: integer);cdecl;  //pseudo code
  3.  
So either take care that the C code reflects that hidden parameter or do away with it.


It is also not necessary. So simply remove of object.
« Last Edit: March 06, 2016, 08:01:58 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: c callback function crash the program
« Reply #2 on: March 06, 2016, 08:01:42 am »
In C you're declaring a callback function, but in pascal you declared it as a callback method.

You pretty much lied to yourself! By the time the callback is called there's no "self" parameter. A rubbish data is there, so calling to any property of the form (in your case it's Memo1.Add.. ) causes an access violation.

If you look at all possible C libraries, apart from Callback function they're typically accepting a callback data. The data is typically size of a pointer or a pointer itself. The data is also passed to callback function.
This approach is utilized in OOP to pass the reference to the object into the routine and later to be received as a callback parameter.

With that said, there're two ways on how to do that.
1) Using Global variables and not changing C.
1.1 change your libhoge.pas declaration as following:
Code: [Select]
type
  TFFCallback = procedure(i, j: integer) cdecl;

...
function hoge_SetCallback(ACallback: TFFCallback): longint;
  cdecl; external {$IFDEF shared}External_library{$ENDIF} Name 'hoge_SetCallback';

1.2 Modify your Unit1 code as following:
Code: [Select]
procedure FFCallbackForForm1(i,j: integer); cdecl;
begin
  Form1.FFCallback(i,j);
end;

procedure TForm1.BtnHoge_SetCallbackClick(Sender: TObject);
begin
  hoge_SetCallback(@FFCallbackForForm1);
end;

procedure TForm1.FFCallback(i, j: integer);
begin
  Memo1.Append(IntToStr(i) + ' - ' + IntToStr(j));
end;
2) Rewrite your library callback function to accept a "data" parameter (a pointer). and pass the same "data" at the time the callback is called.
In your pascal code, pass the reference to TForm as a data, and cast the data back to TForm within the callback.



Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: c callback function crash the program
« Reply #3 on: March 06, 2016, 08:05:15 am »
Basically the same as I wrote. Our messages crossed. Thanks for pointing out he really needs a function to obtain the result. I overlooked that.
Code: Pascal  [Select][+][-]
  1. type
  2.   TFFCallback = function (i, j: integer):integer; cdecl;
  3.  

It is possible to use that as a class member when it would be a class function.

« Last Edit: March 06, 2016, 08:13:00 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: c callback function crash the program
« Reply #4 on: March 06, 2016, 08:16:09 am »
Basically the same as I wrote. Our messages crossed. Thanks for pointing out he really needs a function to obtain the result. I overlooked that.
Code: Pascal  [Select][+][-]
  1. type
  2.   TFFCallback = function (i, j: integer):integer; cdecl;
  3.  

It is possible to use that as a class member when it would be a class function.
wait wait. TFFCallback is not function it's a procedure for pascal (void is used as the return type)

but the function hoge_SetCallback() is a function.

It's easy to cross on such question, requiring extensive answer :)

mercury

  • Full Member
  • ***
  • Posts: 154
Re: c callback function crash the program
« Reply #5 on: March 06, 2016, 08:59:38 am »
I see, the callback function shouldn't be an object.

But I see many declare like this in qt45.pas
Code: Pascal  [Select][+][-]
  1. QLCLWebView_createWindow_Override = function (WebWindowType : integer):QWebViewH of object cdecl;
  2.  

How to explain this?

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: c callback function crash the program
« Reply #6 on: March 06, 2016, 09:19:02 am »
I see, the callback function shouldn't be an object.

But I see many declare like this in qt45.pas
Code: Pascal  [Select][+][-]
  1. QLCLWebView_createWindow_Override = function (WebWindowType : integer):QWebViewH of object cdecl;
  2.  

How to explain this?
In short. This is because Qt4PassLib is written this way.

Long explanation:
That's because qt45 is using a special wrapper library Qt4PassX.lib (where X is a version of the library).
Where all C++ classes (Qt is C++ based library) are wrapped with C-like functions.

At this moment FPC is not good at calling C++ methods directly. Neither C++ could call FPC methods.

However C functions could be easily called. The C-wrappers are also written in FPC friendly manner.
For example "QLCLWebView_createWindow_Override" is passed to the library as QHook parameter. (You can find the QHook declaration if you donlowd Qt4Pass lib sources). And it 's declared like this:
Code: [Select]
typedef struct {
  void *func;
  void *data;
} QHook;
 
Exactly matching TMethod declaration.

It's also interesting to see on how the passed QHook is used.
Since it's necessary to add an additional hidden (Self) parameter the function type is re declared right before the call to accept the parameter and passes it.

Code: [Select]
    if (paintOverride.func) {
      typedef void (*func_type)(void *data, QPainterH painter, const QStyleOptionViewItemH option, const QModelIndexH index);
      (*(func_type)paintOverride.func)(paintOverride.data, (QPainterH) painter, (const QStyleOptionViewItemH)&option, (const QModelIndexH)&index);
      }

As a matter of fact, this is a hack, but it works.

Sergei

  • New member
  • *
  • Posts: 8
Re: [SOLVED] c callback function crash the program
« Reply #7 on: June 22, 2018, 04:02:28 pm »
Hi, posting my solution based on your guys ideas. Thanks.

callbacksformunit.pas
Code: Pascal  [Select][+][-]
  1. unit CallbacksFormUnit;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, CTypes, CallbacksUnit;
  10.  
  11. type
  12.  
  13.   { TCallbacksForm }
  14.  
  15.   TCallbacksForm = class(TForm)
  16.     procedure FormShow(Sender: TObject);
  17.   private
  18.     procedure Callback1(AIntVal: CInt); cdecl;
  19.     procedure Callback2(AIntVal: CInt; AStrVal: PChar); cdecl;
  20.   public
  21.  
  22.   end;
  23.  
  24. var
  25.   CallbacksForm: TCallbacksForm;
  26.  
  27. implementation
  28.  
  29. {$R *.lfm}
  30.  
  31. { TCallbacksForm }
  32.  
  33. procedure TCallbacksForm.FormShow(Sender: TObject);
  34. begin
  35.   SetCallbacks(@Callback1, @Callback2);
  36.   TestCallbacks;
  37.   Close
  38. end;
  39.  
  40. procedure TCallbacksForm.Callback1(AIntVal: CInt); cdecl;
  41. begin
  42.   ShowMessage(IntToStr(AIntVal));
  43. end;
  44.  
  45. procedure TCallbacksForm.Callback2(AIntVal: CInt; AStrVal: PChar); cdecl;
  46. begin
  47.   ShowMessage(IntToStr(AIntVal));
  48.   ShowMessage(AStrVal);
  49. end;
  50.  
  51. end.
  52.  
  53.  

callbacksunit.pas
Code: Pascal  [Select][+][-]
  1. unit CallbacksUnit;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, CTypes;
  9.  
  10. type
  11.   TCallback1 = procedure(AIntVal: CInt) of object; cdecl;
  12.   TCallback2 = procedure(AIntVal: CInt; AStrVal: PChar) of object; cdecl;
  13.  
  14. procedure SetCallbacks(ACallback1: TCallback1; ACallback2: TCallback2); cdecl;
  15. procedure TestCallbacks; cdecl;
  16.  
  17. implementation
  18. {$ifdef win32}
  19.   {$linklib callbacks.dll}
  20. {$else}
  21.   {$ifdef darwin}
  22.     {$linklib callbacks}
  23.   {$else}
  24.     {$linklib libcallbacks.so}
  25.   {$endif}
  26. {$endif}
  27.  
  28. procedure SetCallbacks(ACallback1: TCallback1; ACallback2: TCallback2); cdecl; external name 'setCallbacks';
  29. procedure TestCallbacks; cdecl; external name 'testCallbacks';
  30.  
  31. end.
  32.  
  33.  

callbacks.h
Code: C  [Select][+][-]
  1. typedef void TFunc1(void*, int);
  2. typedef void TFunc2(void*, int, char*);
  3.  
  4. typedef struct {
  5.         TFunc1* func;
  6.         void* self;
  7. } TCallback1;
  8.  
  9. typedef struct {
  10.         TFunc2* func;
  11.         void* self;
  12. } TCallback2;
  13.  
  14. extern void setCallbacks(TCallback1 callback1, TCallback2 callback2);
  15. extern void testCallbacks();

callbacks.c
Code: C  [Select][+][-]
  1. #include "callbacks.h"
  2.  
  3. // Compile with -fPIC
  4. // in Eclipse CDT: Project > Properties > C/C++ Build > Settings > Tool settings >
  5. // XX Compiler > Miscellaneous > check Position Independent Code (-fPIC)
  6. char st[] = "Hi from callback 2";
  7.  
  8. TCallback1 c1;
  9. TCallback2 c2;
  10.  
  11. extern void setCallbacks(TCallback1 callback1, TCallback2 callback2) {
  12.         c1 = callback1;
  13.         c2 = callback2;
  14. }
  15.  
  16. extern void testCallbacks() {
  17.         if (c1.func) {
  18.                 c1.func(c1.self, 1);
  19.         }
  20.  
  21.         if (c2.func) {
  22.                 c2.func(c2.self, 7, st);
  23.         }
  24. }
  25.  
  26.  
« Last Edit: June 23, 2018, 02:50:50 pm by Sergei »

 

TinyPortal © 2005-2018