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\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.