3
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.