Recent

Author Topic: [SOLVED] Incompatible type, Got address of procedure, expected procedure...  (Read 3612 times)

Hansaplast

  • Hero Member
  • *****
  • Posts: 539
  • Tweaking4All.com
    • Tweaking4All
I'm working with Apple's DiskArbitration and translated the .h files myself (attached), but I am a noob in the matter (just did take a good look at other examples).
I do keep running into a strange issue, and hopefully somebody sees or knows what I have been overlooking.
I did consult with Mr. Google, Stack Exchange, this forum, etc, etc. and have been staring at this for about a week now without finding a fix.


My environment:
MacOS Mojave 10.14.3, Lazarus 2.1.0 r60408M (SVN), and FPC 3.0.4 x86_64-darwin-cocoa (alpha).


I have defined the following:


Code: Pascal  [Select]
  1. type
  2.   DADiskAppearedCallback = procedure( disk: DADiskRef; context: univptr); cdecl;
  3.   DADiskDescriptionChangedCallback = procedure( disk: DADiskRef; keys: CFArrayRef; context: univptr); cdecl;
  4.   DADiskDisappearedCallback = procedure( disk: DADiskRef; context: univptr); cdecl;
   
 
Code: Pascal  [Select]
  1. procedure DARegisterDiskAppearedCallback( session: DASessionRef;
  2.                                           match: CFDictionaryRef;
  3.                                           callback: DADiskAppearedCallback;
  4.                                           context: UnivPtr ); external name '_DARegisterDiskAppearedCallback';
  5.  
  6.  
  7. procedure DARegisterDiskDescriptionChangedCallback( session: DASessionRef;
  8.                                                     match: CFDictionaryRef;
  9.                                                     watch: CFArrayRef;
  10.                                                     callback: DADiskDescriptionChangedCallback;
  11.                                                     context: UnivPtr ); external name '_DARegisterDiskDescriptionChangedCallback';
  12.  
  13.  
  14. procedure DARegisterDiskDisappearedCallback( session: DASessionRef;
  15.                                              match: CFDictionaryRef;
  16.                                              callback: DADiskDisappearedCallback;
  17.                                              context: UnivPtr ); external name '_DARegisterDiskDisappearedCallback';


Code: Pascal  [Select]
  1. procedure DiskAppeared( disk: DADiskRef; context: univptr); cdecl;
  2. begin
  3.   // Do something when a disk appeared
  4. end;
  5.  
  6.  
  7. procedure DiskDescriptionChanged( disk: DADiskRef; keys: CFArrayRef; context: univptr); cdecl;
  8. begin
  9.   // Do something when a disk description changed (eg. Mount, Unmount, etc)
  10. end;
  11.  
  12.  
  13. procedure DiskDisappeared( disk: DADiskRef; context: univptr); cdecl;
  14. begin
  15.   // Do something when a disk disappeared
  16. end;


And calling the registration functions as such:


Code: Pascal  [Select]
  1.   DARegisterDiskAppearedCallback(daSession,nil,@DiskAppeared,nil);
  2.   DARegisterDiskDescriptionChangedCallback(daSession,nil,nil,@DiskDescriptionChanged,nil);
  3.   DARegisterDiskDisappearedCallback(daSession,nil,@DiskDisappeared,nil);
 
The DiskAppeared and DiskDisappeared work perfectly fine, even using the context parameter works as expected. I tested this by temporary commenting out the DARegisterDiskDescriptionChangedCallback call. 
However when uncommenting that 3rd registration line (DARegisterDiskDisappearedCallback), the compiler throws an error:


Code: [Select]
(line 3 in this example)
Error: Incompatible type for arg no. 4: Got "<address of procedure(DADiskRef;CFArrayRef;Pointer);CDecl>", expected "<procedure variable type of procedure(DADiskRef;CFArrayRef;Pointer);CDecl>"
DiskArbitration.pas(483,11) Hint: Found declaration: DARegisterDiskDescriptionChangedCallback(DASessionRef;CFDictionaryRef;CFArrayRef;DADiskDescriptionChangedCallback;Pointer);


Note:
If I change the DARegisterDiskDescriptionChangedCallback and DADiskDescriptionChangedCallback definition, by removing the keys: CFArrayRef;, like shown below, everything compiles fine and actually works - obviously this is completely wrong (the missing parameter) and as expected it will throw on a very few occasions an Access Violation error.


Code: Pascal  [Select]
  1. DADiskDescriptionChangedCallback = procedure( disk: DADiskRef; context: univptr); cdecl;
  2.  
  3.  
  4. procedure DARegisterDiskDescriptionChangedCallback( session: DASessionRef;
  5.                                                     match: CFDictionaryRef;
  6.                                                     watch: CFArrayRef;
  7.                                                     callback: DADiskDescriptionChangedCallback;
  8.                                                     context: UnivPtr ); external name '_DARegisterDiskDescriptionChangedCallback';
  9.  
  10.  
  11. procedure DiskDescriptionChanged( disk: DADiskRef; context: univptr); cdecl;
  12. begin
  13.   // Do something when a disk description changed (eg. Mount, Unmount, etc)
  14. end;
           
Also note that the functions DiskAppeared, DiskDescriptionChanged and DiskDisappeared are in one and the same unit file, and are not part of an object or class.
                                         
I'm at a loss here trying to find what the problem may be ... does anyone know what may be wrong here?
I did check for potential double declarations, verified the functions in the Apple documentation over and over again, etc.


Isn't "procedure(DADiskRef;CFArrayRef;Pointer);CDecl" = "procedure(DADiskRef;CFArrayRef;Pointer);CDecl" ?
Seems that way with the DiskAppeared and DiskDisappeared calls, yet doesn't compile with DiskDescriptionChanged.
Why does it work with 2 out of 3 times?
« Last Edit: February 16, 2019, 01:32:09 pm by Hansaplast »

Thaddy

  • Hero Member
  • *****
  • Posts: 9176
It is rather simple I guess. Just declare procedure variables like you did for DADiskDescriptionChangedCallback and pass those instead of using @, because @ is a pointer type and not a procedure variable.
The compiler tells you exactly that.
« Last Edit: February 14, 2019, 12:43:12 pm by Thaddy »
also related to equus asinus.

Hansaplast

  • Hero Member
  • *****
  • Posts: 539
  • Tweaking4All.com
    • Tweaking4All

Thanks for responding Thaddy, and apologies for being inexperienced with this topic.

It is rather simple I guess. Just declare procedure variables like you did for DADiskDescriptionChangedCallback and pass those instead of using @, because @ is a pointer type and not a procedure variable.
The compiler tells you exactly that.


I'm not sure I understand what you mean.
DADiskDescriptionChangedCallback is the one failing.
However it's defined the same way as what I did for the DADiskAppearedCallback and DADiskDisappearedCallback related code?

I modified my post, since it may not have been obvious that the first 3 lines are types (my bad).

Code: Pascal  [Select]
  1. type
  2.   DADiskAppearedCallback = procedure( disk: DADiskRef; context: univptr); cdecl;
  3.   DADiskDescriptionChangedCallback = procedure( disk: DADiskRef; keys: CFArrayRef; context: univptr); cdecl;
  4.   DADiskDisappearedCallback = procedure( disk: DADiskRef; context: univptr); cdecl;

Thaddy

  • Hero Member
  • *****
  • Posts: 9176
The address of (@) should be valid in mode objfpc, but try mode Delphi for your unit and leave @ out. (Just in case: you do not need mode macpas… those modes also work on Apple)
If that doesn't work I have to get my mini running again, which is in the attic at the moment.
« Last Edit: February 14, 2019, 01:54:05 pm by Thaddy »
also related to equus asinus.

Hansaplast

  • Hero Member
  • *****
  • Posts: 539
  • Tweaking4All.com
    • Tweaking4All
Thanks Thaddy! That did seem to to the trick - I didn't have to change the code, just replaced "{$mode objfpc}" with "{$mode delphi}".
It does strike me as weird though that with 2 cases it works just fine (diskappear and diskdisappear), and a 3rd nearly identical setup (diskdescriptionchange) fails (in the same unit!). As you can see in the code: it's all the "same", ...

Hansaplast

  • Hero Member
  • *****
  • Posts: 539
  • Tweaking4All.com
    • Tweaking4All
Yeah I was cheering to early ...
So this does cause an Access Violation on some occasions as well, the exact same when I previously changed the function (intentionally) the wrong way;


Quote
If I change the DARegisterDiskDescriptionChangedCallback and DADiskDescriptionChangedCallback definition, by removing the keys: CFArrayRef;, like shown below, everything compiles fine and actually works - obviously this is completely wrong (the missing parameter) and as expected it will throw on a very few occasions an Access Violation error.



Code: Pascal  [Select]
  1. DADiskDescriptionChangedCallback = procedure( disk: DADiskRef; context: univptr); cdecl;
  2.  
  3.  
  4.  
  5. procedure DARegisterDiskDescriptionChangedCallback( session: DASessionRef;
  6.                                                     match: CFDictionaryRef;
  7.                                                     watch: CFArrayRef;
  8.                                                     callback: DADiskDescriptionChangedCallback;
  9.                                                     context: UnivPtr ); external name '_DARegisterDiskDescriptionChangedCallback';
  10.  
  11.  
  12. procedure DiskDescriptionChanged( disk: DADiskRef; context: univptr); cdecl;
  13. begin
  14.   // Do something when a disk description changed (eg. Mount, Unmount, etc)
  15. end;

Cyrax

  • Hero Member
  • *****
  • Posts: 758
You need to define calling convention for your registration procedures, too.

Code: Pascal  [Select]
  1. procedure DARegisterDiskDescriptionChangedCallback( session: DASessionRef;
  2.                                                     match: CFDictionaryRef;
  3.                                                     watch: CFArrayRef;
  4.                                                     callback: DADiskDescriptionChangedCallback;
  5.                                                     context: UnivPtr );cdecl; external name '_DARegisterDiskDescriptionChangedCallback';

Hansaplast

  • Hero Member
  • *****
  • Posts: 539
  • Tweaking4All.com
    • Tweaking4All

Thanks Cyrax for chiming in as well ...  :)


1) When I add "cdecl" as described I get a similar error message (just the "hint" changed).
Code: [Select]
Error: Incompatible type for arg no. 4: Got "<address of procedure(DADiskRef;CFArrayRef;Pointer);CDecl>", expected "<procedure variable type of procedure(DADiskRef;CFArrayRef;Pointer);CDecl>"
Hint: Found declaration: DARegisterDiskDescriptionChangedCallback(DASessionRef;CFDictionaryRef;CFArrayRef;DADiskDescriptionChangedCallback;Pointer); CDecl;


2) When adding "cdecl ;" to one of the 2 working functions, I get;
Code: [Select]
Error: linker: Undefined symbols for architecture x86_64:
Error: linker:   "__DARegisterDiskAppearedCallback", referenced from:
Error: ld: symbol(s) not found for architecture x86_64


(but NOT for DARegisterDiskDescriptionChangedCallback)


It still strikes me as odd that 2 functions work just fine, but the 3rd, declared in the same fashion, totally fails ...

Thaddy

  • Hero Member
  • *****
  • Posts: 9176
What is the exact C prototype for those functions? Maybe there is a const somewhere? That'll save me climbing stairs  :D
I suspect a const * which you should translate as var or as a pointer to...
« Last Edit: February 14, 2019, 04:56:52 pm by Thaddy »
also related to equus asinus.

Cyrax

  • Hero Member
  • *****
  • Posts: 758

Thanks Cyrax for chiming in as well ...  :)


1) When I add "cdecl" as described I get a similar error message (just the "hint" changed).
Code: [Select]
Error: Incompatible type for arg no. 4: Got "<address of procedure(DADiskRef;CFArrayRef;Pointer);CDecl>", expected "<procedure variable type of procedure(DADiskRef;CFArrayRef;Pointer);CDecl>"
Hint: Found declaration: DARegisterDiskDescriptionChangedCallback(DASessionRef;CFDictionaryRef;CFArrayRef;DADiskDescriptionChangedCallback;Pointer); CDecl;


2) When adding "cdecl ;" to one of the 2 working functions, I get;
Code: [Select]
Error: linker: Undefined symbols for architecture x86_64:
Error: linker:   "__DARegisterDiskAppearedCallback", referenced from:
Error: ld: symbol(s) not found for architecture x86_64


(but NOT for DARegisterDiskDescriptionChangedCallback)


It still strikes me as odd that 2 functions work just fine, but the 3rd, declared in the same fashion, totally fails ...

Remove the underscore from the name definitions.  When using cdecl calling convention, FPC adds the underscore automatically.

Hansaplast

  • Hero Member
  • *****
  • Posts: 539
  • Tweaking4All.com
    • Tweaking4All
I'm not seeing any difference between the 3 types and procedures ... (but I'm not an expert)
The C prototypes (source);


Code: C  [Select]
  1. typedef void (*DADiskAppearedCallback)(DADiskRef disk, void *context);
  2. typedef void (*DADiskDisappearedCallback)(DADiskRef disk, void *context);
  3. typedef void (*DADiskDescriptionChangedCallback)(DADiskRef disk, CFArrayRef keys, void *context);
  4.  
  5.  
  6. extern void DARegisterDiskAppearedCallback( DASessionRef           session,
  7.                                             CFDictionaryRef        match,
  8.                                             DADiskAppearedCallback callback,
  9.                                             void *                 context );
  10.                                            
  11. extern void DARegisterDiskDisappearedCallback( DASessionRef              session,
  12.                                                CFDictionaryRef           match,
  13.                                                DADiskDisappearedCallback callback,
  14.                                                void *                    context );
  15.                                                
  16. extern void DARegisterDiskDescriptionChangedCallback( DASessionRef                     session,
  17.                                                       CFDictionaryRef                  match,
  18.                                                       CFArrayRef                       watch,
  19.                                                       DADiskDescriptionChangedCallback callback,
  20.                                                       void *                           context );

Hansaplast

  • Hero Member
  • *****
  • Posts: 539
  • Tweaking4All.com
    • Tweaking4All

I'm still confused why 2 of these work fine and one keeps failing ...

@Thaddy:
No need to climb the stairs ...  :)  (I do very much appreciate the help!)


@Cyrax:
I removed the underscore from "DARegisterDiskDescriptionChangedCallback", and it still produces the same error. The only way I get it compiled seems to be in Delphi mode (with or without cdecl and/or underscore).

Thaddy

  • Hero Member
  • *****
  • Posts: 9176
void * needs to be a var? Otherwise I have to walk the stairs O:-)
also related to equus asinus.

Hansaplast

  • Hero Member
  • *****
  • Posts: 539
  • Tweaking4All.com
    • Tweaking4All
Not sure, with the other functions using "contect:univptr" for "void * context" worked fine both ways (set and receive).
I recall seeing something about it being nullable;


Code: C  [Select]
  1. typedef void ( *DADiskDescriptionChangedCallback )( DADiskRef disk, CFArrayRef keys, void * __nullable context );



Just tested it; adding "var" in front of the context parameter actually introduces more access violation messages  :o :D

Cyrax

  • Hero Member
  • *****
  • Posts: 758

I'm still confused why 2 of these work fine and one keeps failing ...

@Thaddy:
No need to climb the stairs ...  :)  (I do very much appreciate the help!)


@Cyrax:
I removed the underscore from "DARegisterDiskDescriptionChangedCallback", and it still produces the same error. The only way I get it compiled seems to be in Delphi mode (with or without cdecl and/or underscore).

This sounds like a bug at FPC compiler. Have you tried the FPC trunk or FPC 3.2.x (the next release version, the fixes_3_2 branch)? You can use fpcupdeluxe to download and install them.