> <
https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/PFN_vkDebugReportCallbackEXT.html>?
>
>
> My current error comes from line 250, on the expression (function-ptr
> debug-report-callback _PFN_vkDebugReportCallbackEXT):
>
> ; function-ptr: contract violation
> ; expected: (and ctype? (lambda (ct) (eq? 'fpointer (ctype->layout ct))))
> ; given: #<ctype>
>
> It seems that my error was that _PFN_vkDebugReportCallbackEXT was
> (_cpointer/null (_fun ...)), but I am not sure what should take its place.
>
> * If I substitute _fpointer for _PFN_vkDebugReportCallbackEXT, I get
> the "application; not a procedure" error.
> * If I substitute the _fun form with the signature I want, then I get
> "#<ctype>->C: argument is not `#<ctype>' pointer"
>
> I suspect this has something to do with the /callout/ concept from the
> manual, but I don't understand how it applies here.
The use of _cpointer/null seems wrong: its first argument is interpreted
as a tag, so that's the source of the last error you mentioned. The
`_cpointer/null` is unnecessary anyway, so just delete it:
(define _PFN_vkDebugReportCallbackEXT (_fun ....))
On line 250: I don't think you need to use function-ptr. The field
setter will automatically convert debug-report-callback (I assume that's
a Racket procedure) into a *callback* function pointer.
A *callout* is a foreign function gets converted into a Racket
procedure; a *callback* is when a Racket procedure gets turned into a
function pointer so it can be called from foreign code. Both are
described by _fun ctypes.
Here's a simpler example. Suppose you have the following C code:
/* demo.c */
/* gcc -fpic -shared -o demo.so demo.c */
int an_int_fun(int x) {
return x + 1;
}
typedef int (*int2int)(int);
int apply_to_twelve(int2int f) {
return (*f)(12);
}
struct two_funs {
int2int f;
int2int g;
};
int apply_two_funs_and_sum(struct two_funs *tf, int arg) {
return (tf->f)(arg) + (tf->g)(arg);
}
Here Racket bindings for the foreign code:
(require ffi/unsafe ffi/unsafe/define)
(define-ffi-definer define-demo (ffi-lib "demo.so"))
(define _int2int (_fun _int -> _int))
(define-demo an_int_fun _int2int)
(define-demo apply_to_twelve (_fun _int2int -> _int))
(define-cstruct _two_funs
([f _int2int]
[g _int2int]))
(define-demo apply_two_funs_and_sum
(_fun _two_funs-pointer _int -> _int))
In that Racket program, `an_int_fun` is a *callout*: a Racket procedure
that calls foreign code when applied.
(an_int_fun 5) ;; => 6
If you call `apply_to_twelve` with a Racket procedure, like this:
(apply_to_twelve add1) ;; => 13
then the FFI converts `add1` (a Racket procedure) into a function
pointer using the _fun type `_int2int`. Foreign code can use that
function pointer to *call back* into Racket.
Storing a function in a struct with _int2int fields does the same
automatic conversion. For example:
(define (f x) (expt x 2))
(define (g x) (expt x 3))
(define tf (make-two_funs #f #f))
(set-two_funs-f! tf f)
(set-two_funs-g! tf g)
;; or equivalently, (define tf (make-two_funs f g))
(apply_two_funs_and_sum tf 3) ;; => 36
You can even fetch one of the function fields back and call it, like this:
((two_funs-f tf) 4) ;; => 16
IIUC, that creates a callout procedure that calls the callback function
pointer that calls the original Racket procedure.
I gave `f` and `g` names so they wouldn't be collected, but in general
you need to make sure a Racket procedure doesn't get collected when
foreign code still has a callback for it. For example, the following
code is likely to crash: because the callback function pointer stored in
tf2->g refers to a Racket procedure that has probably been GC'd:
(define tf2 (make-two_funs f (lambda (x) (expt x 5))))
(collect-garbage)
(apply_two_funs_and_sum tf 3)
See the #:keep argument of `_fun` for more information.
Ryan