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?
Luis Oliveira luismbo@gmail.com writes:
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?
I think this is a good improvement. I always knew the type translator syntax was too complicated but never got around to simplifying it.
As for the DEFCTYPE syntax, how about another keyword argument:
(define-type-translation boolean "doc" :c-type :int :to-c ... :from-c ...)
Also it sets up &value with symbol-macrolet.
I must admit I'm not wild about using &symbols as placeholders inside these expansions, as I think that's a naming convention best left unique to lambda list keywords.
Here are some alternative syntaxes (besides just renaming the symbol macros), I'm sure there are more:
;; Use special variables instead of symbol macros: :in (let ((*var* (foreign-string-alloc *value*))) (unwind-protect (progn *body*) (foreign-string-free *var*)))
;; Use MACROLET, sort of like CALL-NEXT-METHOD: :in (let (((var) (foreign-string-alloc (value)))) (unwind-protect (progn (body)) (foreign-string-free (var))))
;; IMO the cleanest, but also verbose, using a function ;; that returns the form using backquote: :in (lambda (var value body) `(let ((,var (foreign-string-alloc ,value))) (unwind-protect (progn ,@body) (foreign-string-free ,var))))
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).
Yes, this was one of my big complaints about my current design. :-)
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.
:IN might be better named something that makes it clear that it is fulfilling a similar purpose to :TO-C except it is used only in the special circumstance of passing arguments to a function. Maybe :TO-C-ARG or something...
James
On 4/ago/2005, at 04:57, James Bielman wrote:
I must admit I'm not wild about using &symbols as placeholders inside these expansions, as I think that's a naming convention best left unique to lambda list keywords.
What about $symbols? Looks like Perl, heh.
(define-type-translation string "String translator." :c-type :pointer :to-c-arg (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))
On 4/ago/2005, at 04:57, James Bielman wrote:
;; IMO the cleanest, but also verbose, using a function ;; that returns the form using backquote: :in (lambda (var value body) `(let ((,var (foreign-string-alloc ,value))) (unwind-protect (progn ,@body) (foreign-string-free ,var))))
After trying to implement this and realizing some of my assumptions were incorrect (eg. (symbol-)macrolet for &$var won't work, I'd need to search for it in the form) I got to these two syntaxes:
(define-type-translation string :pointer "Translation between C and Lisp strings." :to-c-arg (lambda (var value body) `(with-foreign-string (,var ,value) ,@body)) :to-c (lambda (value) `(foreign-string-alloc ,value)) :from-c (lambda (value) `(foreign-string-to-lisp ,value)))
(define-type-translation string :pointer "Translation between C and Lisp strings." (:to-c-arg (var value body) `(with-foreign-string (,var ,value) ,@body)) (:to-c (value) `(foreign-string-alloc ,value)) (:from-c (value) `(foreign-string-to-lisp ,value)))
Preferences and/or comments anyone?
Luis Oliveira luismbo@gmail.com writes:
After trying to implement this and realizing some of my assumptions were incorrect (eg. (symbol-)macrolet for &$var won't work, I'd need to search for it in the form) I got to these two syntaxes:
(define-type-translation string :pointer "Translation between C and Lisp strings." :to-c-arg (lambda (var value body) `(with-foreign-string (,var ,value) ,@body)) :to-c (lambda (value) `(foreign-string-alloc ,value)) :from-c (lambda (value) `(foreign-string-to-lisp ,value)))
Honestly, I like the explicit lambdas here. It's not that much more verbose and it makes things pretty clear IMO.
(define-type-translation string :pointer "Translation between C and Lisp strings." (:to-c-arg (var value body) `(with-foreign-string (,var ,value) ,@body)) (:to-c (value) `(foreign-string-alloc ,value)) (:from-c (value) `(foreign-string-to-lisp ,value)))
Preferences and/or comments anyone?
James
Sounds interesting. I like the idea of putting everything into one declaration instead of having two as before. I think "to-c" and "from-c" are fine names. A cuter name would just make it easier to get the two directions mixed up, IMHO.
I've got one question and one suggestion. Excuse me if they're dumb, because I'm not too up-to-date on the workings of C-FFI.
1) How does the translator interface express more complex C values? Say, I want the "to-c" clause to send a struct to the C code? Can I just return an object allocated by foreign-object-alloc?
2) Is there any way to make translations optional, and only invoke them on particular arguments in a given function declaration?
I'm also thinking about one other thing. Say I'm wrapping this function:
void cairo_line_to(cairo_t* cr, double x, double y);
I'd like the Lisp declaration to be as if the function was:
(defun cairo-line-to (cr pt) ...) where 't' is a 'point' structure or maybe just a (x . y) cons cell. Would it be reasonable to include that kind of translation in the FFI, or would it be more prudent to put that in a higher layer?
Sincerely, Rayiner Hashem
On 8/3/05, Luis Oliveira luismbo@gmail.com wrote:
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?
-- Luís Oliveira http://student.dei.uc.pt/~lmoliv/ Equipa Portuguesa do Translation Project http://www2.iro.umontreal.ca/~pinard/po/registry.cgi?team=pt _______________________________________________ cffi-devel mailing list cffi-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/cffi-devel
On 4/ago/2005, at 05:09, Rayiner Hashem wrote:
- How does the translator interface express more complex C values?
Say, I want the "to-c" clause to send a struct to the C code? Can I just return an object allocated by foreign-object-alloc?
Yes, why not. That's pretty much what the string translator does.
- Is there any way to make translations optional, and only invoke
them on particular arguments in a given function declaration?
Sounds easy to implement (within defcfun at least) but I guess it's not that useful. If you don't want the string to be translated, just use :pointer (or eventually something like (* :char), for a typed pointer, or another some other appropriate type), same for boolean vs. :int.
I'm also thinking about one other thing. Say I'm wrapping this function:
void cairo_line_to(cairo_t* cr, double x, double y);
I'd like the Lisp declaration to be as if the function was:
(defun cairo-line-to (cr pt) ...) where 't' is a 'point' structure or maybe just a (x . y) cons cell. Would it be reasonable to include that kind of translation in the FFI, or would it be more prudent to put that in a higher layer?
Hmm, I'm trying to figure out if this is worth the trouble. Probably not this summer. This kind of stuff is interesting to me though, it'd be nice to avoid the need for the user to defun wrappers around the ffi whenever possible and this is a nice example. Need to figure out a good way of doing this kind of things.