foreign.lisp 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. "Higher-level wrappers around the LuaJIT ffi module."
  2. (import luajit/ffi (cdef C) :export)
  3. (import luajit/bit bit :export)
  4. (import luajit/ffi ffi :export)
  5. (defmacro define-foreign-function (name lambda-list do-errno-check)
  6. "Define a foreign function wrapper for the C symbol NAME, taking
  7. arguments LAMBDA-LIST.
  8. Additionally, if DO-ERRNO-CHECK is true or a number, assume that
  9. negative return values (or the number, if given) signal an error
  10. condition, and raise an exception with the message determined by
  11. strerror(3).
  12. The symbol NAME will be mangled by replacing `-`s with `_`s. If this
  13. is undesirable, you may give an argument of the form `(quote foo)`,
  14. in which foo will not be mangled.
  15. ### Example:
  16. ```cl :no-test
  17. > (ffi/cdef \"char *get_current_dir_name(void);\")
  18. out = nil
  19. > (define-foreign-function get-current-dir-name () 0)
  20. out = function: 0x42e22188
  21. > (get-current-dir-name)
  22. out = cdata<char *>: 0x00d26610
  23. > (ffi/string (get-current-dir-name))
  24. out = \"/home/hydraz/Projects/urn/compiler\"
  25. ```"
  26. (let* [(exit (gensym))
  27. (fun (gensym))
  28. (status (gensym))
  29. (mangle (lambda (nam)
  30. (case nam
  31. [(quote ?x) (symbol->string x)]
  32. [symbol? => (first (string/gsub (symbol->string it) "-" "_"))])))]
  33. (ffi/cdef "char *strerror(int);")
  34. `(defun ,name ,lambda-list
  35. (ffi/cdef "char *strerror(int);")
  36. (let* [(,fun (.> ffi/C ,(mangle name)))
  37. (,exit (,fun ,@lambda-list))
  38. (,status (ffi/cast "int" ,exit))]
  39. ,(cond
  40. [(number? do-errno-check)
  41. `(if (= ,status ,do-errno-check)
  42. (format 1 "({} {@( )}) failed: {}" ,(symbol->string name) (list ,@lambda-list)
  43. (ffi/string ((.> ffi/C :strerror) (ffi/errno))))
  44. ,exit)]
  45. [do-errno-check
  46. `(if (< ,status 0)
  47. (format 1 "({} {@( )}) failed: {}" ,(symbol->string name) (list ,@lambda-list)
  48. (ffi/string ((.> ffi/C :strerror) (ffi/errno))))
  49. ,exit)]
  50. [else exit])))))
  51. (defmacro define-foreign-functions (c-definitions &functions)
  52. "Declare all the foreign functions specified in C-DEFINITIONS, and
  53. additionally build the wrappers as described in FUNCTIONS, using
  54. [[define-foreign-function]]"
  55. (let* [(c-definitions (.> c-definitions :value))]
  56. (ffi/cdef c-definitions)
  57. (splice
  58. `((ffi/cdef ,c-definitions)
  59. ,@(map (cut cons `define-foreign-function <>) functions)))))