Hello,
I'm reworking the type translator interface. The idea is to simplify and allow for more flexibility. I'll start with some examples:
This is how the boolean type would be defined with the current interface:
(defctype boolean :int)
(define-type-translator boolean :in (arg result-var) "Type translator to convert t/nil to a C boolean." (values `(if ,arg 1 0) nil))
(define-type-translator boolean :result (arg result-var) "Type translator to convert C booleans to t/nil." (values `(if (zerop ,arg) nil t)))
First thing I'm changing is :in/:return, the names don't make sense when we think about callbacks, the :return translation is applied for the arguments and :in for the callback's return value. So I picked :to-c and :from-c. Can someone suggest better names?
And this is how my interface would look like:
(define-type-translation (boolean :int) "Converting between C ints and Lisp booleans." :to-c (if &value 1 0) :from-c (if (zerop &value) nil t))
It takes care of the defctype unless the first arg isn't a list. If it were just 'boolean' defctype woudn't be used (eg. when defining a type-translating for a struct type). Hmm, is this extra syntax re defctype overkill?
Also it sets up &value with symbol-macrolet.
Another example, the string type. Before:
(defctype string :pointer)
(define-type-translator string :to-c (arg result-var) "Type translator for converting Lisp strings to C string." (values `(foreign-string-alloc ,arg) `((foreign-string-free ,result-var))))
(define-type-translator string :from-c (arg result-var) "Type translator for C string values." `(foreign-string-to-lisp ,arg))
After:
(define-type-translation (string :pointer) "Converting between C and Lisp strings." :in (let ((&var (foreign-string-alloc &value))) (unwind-protect (&body) (foreign-string-free &var))) :to-c (foreign-string-alloc &value) :from-c (foreign-string-to-lisp &value))
Here we have an extra translation :in, used when we are actually passing something to a foreign function (the :to-c translation is used, in case :in is undefined). Here we have to explicitly write the unwind-protect, I think the translation becomes more readable this way. :in also sets up a few more (symbol-)macros, &var and &body whose meaning should be obvious.
Getting to decide where &body goes (ie. not having to be restricted to a unwind-protect form as before) gives us the ability to do this:
(define-type-translation (string :pointer) "Converting between C and Lisp strings." :in (with-foreign-string (&var &value) (&body)) :to-c (foreign-string-alloc &value) :from-c (foreign-string-to-lisp &value))
This way the string is stack allocated (if the lisp supports this).
In this last example, I indented the code in a different way, I'm not yet happy with the syntax, any comments or suggestions regarding syntax, naming, etc.. will be most appreciated.
Also, the :to-c/:from-c translations, unlike :in, don't have access to &body as I couldn't think of a situation where this would be necessary. Am I missing something?