I am trying to create a binding for gobject library with CFFI.

CFFI works extremely well, however I have a use case that is not covered by 
CFFI.

Basically, I want to define foreign types that should "clean up" after 
themselves when they are used as types for callback arguments.

I have following use cases for this:
1) I want to have a structure created when callback is entered and have its 
contents copied to foreign structure when callback returns.

2) I want to have wrappers that contain a pointer to foreign structure. 
However, I want to ensure that operations on wrapper signal errors when 
callback returns (because pointer points to a stack-allocated structure). This 
is needed to ensure that no operations are done on invalid pointer.

The attached patch provides suggested change: foreign types may specify that 
additional actions should be performed when the callback returns. This is 
achieved by adding new generic function CLEANUP-TRANSLATED-OBJECT-FOR-
CALLBACK. Additional generic function HAS-CALLBACK-CLEANUP is called at 
macroexpansion time as an optimization to remove unnecessary calls to CLEANUP-
TRANSLATED-OBJECT-FOR-CALLBACK.

Attached is a test case that demonstrates the usage and checks for correctness 
of behavior.

If there are obstacles for adding this patch, I would be happy to work on 
them.
diff -rN -u old-cffi/src/early-types.lisp new-cffi/src/early-types.lisp
--- old-cffi/src/early-types.lisp	2009-07-27 01:00:33.358581046 +0400
+++ new-cffi/src/early-types.lisp	2009-07-27 01:00:33.393581553 +0400
@@ -357,6 +357,14 @@
   (:method (value type param)
     (declare (ignore value type param))))
 
+;;; Execute actions when callback returns. VALUE is a value that was
+;;; translated by TRANSLATE-TO-FOREIGN and ORIGINAL-ARG is the canonical
+;;; foreign representation of value.
+(defgeneric cleanup-translated-object-for-callback (type value original-arg)
+  (:method (type value original-arg)
+    (declare (ignore type value original-arg))
+    nil))
+
 ;;;## Macroexpansion Time Translation
 ;;;
 ;;; The following EXPAND-* generic functions are similar to their
@@ -385,6 +393,14 @@
   (declare (ignore value))
   *runtime-translator-form*)
 
+;;; CLEANUP-TRANSLATED-OBJECT-FOR-CALLBACK
+
+;;; Checks whether the type has actions that should be performed at callback return.
+(defgeneric has-callback-cleanup (type)
+  (:method (type)
+    (declare (ignore type))
+    nil))
+
 ;;; EXPAND-TO-FOREIGN
 
 ;; The second return value is used to tell EXPAND-TO-FOREIGN-DYN that
diff -rN -u old-cffi/src/functions.lisp new-cffi/src/functions.lisp
--- old-cffi/src/functions.lisp	2009-07-27 01:00:33.358581046 +0400
+++ new-cffi/src/functions.lisp	2009-07-27 01:00:33.392580957 +0400
@@ -270,12 +270,24 @@
 
 ;;;# Defining Callbacks
 
+(defun expand-to-callback-cleanup (translated-arg original-arg type)
+  (when (has-callback-cleanup type)
+    `(cleanup-translated-object-for-callback ,type ,translated-arg ,original-arg))))
+
 (defun inverse-translate-objects (args types declarations rettype call)
-  `(let (,@(loop for arg in args and type in types
-                 collect (list arg (expand-from-foreign
-                                    arg (parse-type type)))))
-     ,@declarations
-     ,(expand-to-foreign call (parse-type rettype))))
+  (let* ((sym-list (loop repeat (length args) collect (gensym)))
+         (parsed-types (loop for type in types collect (parse-type type)))
+         (cleanup-forms (loop for arg in args and type in parsed-types and sym in sym-list
+                          collect (expand-to-callback-cleanup arg sym type))))
+    `(let (,@(loop for arg in args and type in types
+                collect (list arg (expand-from-foreign
+                                   arg (parse-type type))))
+           ,@(loop for arg in args and sym in sym-list and cleanup-form in cleanup-forms
+                when cleanup-form
+                collect (list sym arg)))
+       ,@declarations
+       (unwind-protect (progn ,(expand-to-foreign call (parse-type rettype)))
+         ,@(remove nil cleanup-forms)))))
 
 (defun parse-defcallback-options (options)
   (destructuring-bind (&key (calling-convention :cdecl)
diff -rN -u old-cffi/src/package.lisp new-cffi/src/package.lisp
--- old-cffi/src/package.lisp	2009-07-27 01:00:33.360581486 +0400
+++ new-cffi/src/package.lisp	2009-07-27 01:00:33.395580862 +0400
@@ -117,6 +117,8 @@
    #:translate-to-foreign
    #:translate-from-foreign
    #:free-translated-object
+   #:cleanup-translated-object-for-callback
+   #:has-callback-cleanup
    #:expand-to-foreign-dyn
    #:expand-to-foreign
    #:expand-from-foreign

(defpackage :cffi-test-case
  (:use :cl :cffi)
  (:export :test))

(in-package :cffi-test-case)

(defcstruct gdk-rectangle-cstruct
  (x :int)
  (y :int)
  (width :int)
  (height :int))

(defstruct gdk-rectangle
  (x 0)
  (y 0)
  (width 0)
  (height 0))

(define-foreign-type gdk-rectangle-type ()
  ()
  (:actual-type :pointer)
  (:simple-parser gdk-rectangle))

(defmethod translate-to-foreign (rectangle (type gdk-rectangle-type))
  (let ((native-structure (foreign-alloc 'gdk-rectangle-cstruct)))
    (loop
       for slot in '(x y width height)
       do (setf (foreign-slot-value native-structure 'gdk-rectangle-cstruct 
slot)
                (slot-value rectangle slot)))
    (values native-structure rectangle)))

(defmethod free-translated-object (native-structure (type gdk-rectangle-type) 
rectangle)
  (loop
     for slot in '(x y width height)
     do (setf (slot-value rectangle slot)
              (foreign-slot-value native-structure 'gdk-rectangle-cstruct 
slot)))
  (foreign-free native-structure))

(defmethod has-callback-cleanup ((type gdk-rectangle-type))
  t)

(defmethod translate-from-foreign (native-structure (type gdk-rectangle-type))
  (let ((rectangle (make-gdk-rectangle)))
    (loop
       for slot in '(x y width height)
       do (setf (slot-value rectangle slot)
                (foreign-slot-value native-structure 'gdk-rectangle-cstruct 
slot)))
    rectangle))

(defmethod cleanup-translated-object-for-callback ((type gdk-rectangle-type) 
rectangle native-structure)
  (loop
     for slot in '(x y width height)
     do (setf (foreign-slot-value native-structure 'gdk-rectangle-cstruct slot)
              (slot-value rectangle slot))))

(defcallback incf-rectangle-callback :void
    ((rectangle gdk-rectangle) (delta :int))
  (loop
     for slot in '(x y width height)
     do (incf (slot-value rectangle slot) delta)))

(defun incf-rectangle (r &optional (delta 1))
  (foreign-funcall-pointer (callback incf-rectangle-callback) ()
                           gdk-rectangle r
                           :int delta
                           :void))

(defun test ()
  (let ((r (make-gdk-rectangle :x 1 :y 2 :width 3 :height 4)))
    (print r)
    (incf-rectangle r 3)
    (print r)
    (and (= 4 (gdk-rectangle-x r))
         (= 5 (gdk-rectangle-y r))
         (= 6 (gdk-rectangle-width r))
         (= 7 (gdk-rectangle-height r)))))

Attachment: signature.asc
Description: This is a digitally signed message part.

_______________________________________________
cffi-devel mailing list
cffi-devel@common-lisp.net
http://common-lisp.net/cgi-bin/mailman/listinfo/cffi-devel

Reply via email to