I'm working on FFI wrapper for SDL2 library in Racket. The library includes several variadic functions (e.g. SDL_SetError, SDL_LogMessage etc); Racket FFI does not have straight way for importing those, but there's a workaround showcased here. Despite having little experience with Racket macros, I've decided to create one from the linked code to avoid excess code duplication. Here's my code:
#lang racket (require (for-syntax racket/syntax) (for-syntax racket/format) ffi/unsafe) (define sdl-lib (ffi-lib "libSDL2-2.0" '("0" #f))) (define-syntax (define-vararg-func stx) (syntax-case stx () [(_ name (arg-types ...) ret-type) (with-syntax* ([name-str (~a (syntax->datum #'name))] [arglist (map (lambda (arg) (gensym (syntax->datum arg))) (syntax->list #'(arg-types ...)))] [full-arglist (append (syntax->list #'arglist) 'args)] [final-call (append '(apply fun) (syntax->list #'arglist) '(args))]) #'(define name (let ([interfaces (make-hash)]) (lambda full-arglist (define itypes (append (list arg-types ...) (map (lambda (x) (cond [(and (integer? x) (exact? x)) _int] [(and (number? x) (real? x)) _double*] [(string? x) _string] [(bytes? x) _bytes] [(symbol? x) _symbol] [else (error "don't know how to deal with ~e" x)])) args))) (let ([fun (hash-ref interfaces itypes (lambda () (let ([i (get-ffi-obj name-str sdl-lib (_cprocedure itypes ret-type))]) (hash-set! interfaces itypes i) i)))]) final-call)))))])) ;; macro usage (define-vararg-func SDL_SetError (_string) _int) (SDL_SetError "test %s %d" "test" 42) (define SDL_GetError (get-ffi-obj "SDL_GetError" sdl-lib (_cprocedure '() _string))) (displayln (SDL_GetError))
I'd love any feedback on the macro and suggestions on how to improve its readability.