On 10/25/19 12:45 AM, Sage Gerard wrote:
I am porting some C++ code to Racket that uses a function pointer.
C++ origin: See 294 through 306:
https://github.com/Erkaman/vulkan_minimal_compute/blob/master/src/main.cpp#L294
Racket destination:
https://github.com/zyrolasting/racket-vulkan/blob/master/examples/mandelbrot.rkt#L240
How do I use function-ptr on debug-report-callback to produce a valid
function pointer matching this signature
<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
--
You received this message because you are subscribed to the Google Groups "Racket
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit
https://groups.google.com/d/msgid/racket-users/6c26abc4-09bc-3a2e-369f-b14671e84958%40ccs.neu.edu.