structures.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
 (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
       structures.lisp (6764B)
       ---
            1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
            2 ;;;
            3 ;;; structures.lisp --- Methods for translating foreign structures.
            4 ;;;
            5 ;;; Copyright (C) 2011, Liam M. Healy  <lhealy@common-lisp.net>
            6 ;;;
            7 ;;; Permission is hereby granted, free of charge, to any person
            8 ;;; obtaining a copy of this software and associated documentation
            9 ;;; files (the "Software"), to deal in the Software without
           10 ;;; restriction, including without limitation the rights to use, copy,
           11 ;;; modify, merge, publish, distribute, sublicense, and/or sell copies
           12 ;;; of the Software, and to permit persons to whom the Software is
           13 ;;; furnished to do so, subject to the following conditions:
           14 ;;;
           15 ;;; The above copyright notice and this permission notice shall be
           16 ;;; included in all copies or substantial portions of the Software.
           17 ;;;
           18 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
           19 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
           20 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
           21 ;;; NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
           22 ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
           23 ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
           24 ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
           25 ;;; DEALINGS IN THE SOFTWARE.
           26 ;;;
           27 
           28 (in-package #:cffi)
           29 
           30 ;;; Definitions for conversion of foreign structures.
           31 
           32 (defmethod translate-into-foreign-memory ((object list)
           33                                           (type foreign-struct-type)
           34                                           p)
           35   (unless (bare-struct-type-p type)
           36     (loop for (name value) on object by #'cddr
           37           do (setf (foreign-slot-value p (unparse-type type) name)
           38                    (let ((slot (gethash name (structure-slots type))))
           39                      (convert-to-foreign value (slot-type slot)))))))
           40 
           41 (defmethod translate-to-foreign (value (type foreign-struct-type))
           42   (let ((ptr (foreign-alloc type)))
           43     (translate-into-foreign-memory value type ptr)
           44     ptr))
           45 
           46 (defmethod translate-from-foreign (p (type foreign-struct-type))
           47   ;; Iterate over slots, make plist
           48   (if (bare-struct-type-p type)
           49       p
           50       (let ((plist (list)))
           51         (loop for slot being the hash-value of (structure-slots type)
           52               for name = (slot-name slot)
           53               do (setf (getf plist name)
           54                        (foreign-struct-slot-value p slot)))
           55         plist)))
           56 
           57 (defmethod free-translated-object (ptr (type foreign-struct-type) freep)
           58   (unless (bare-struct-type-p type)
           59     ;; Look for any pointer slots and free them first
           60     (loop for slot being the hash-value of (structure-slots type)
           61           when (and (listp (slot-type slot)) (eq (first (slot-type slot)) :pointer))
           62             do
           63                ;; Free if the pointer is to a specific type, not generic :pointer
           64                (free-translated-object
           65                 (foreign-slot-value ptr type (slot-name slot))
           66                 (rest (slot-type slot))
           67                 freep))
           68     (foreign-free ptr)))
           69 
           70 (defmacro define-translation-method ((object type method) &body body)
           71   "Define a translation method for the foreign structure type; 'method is one of :into, :from, or :to, meaning relation to foreign memory.  If :into, the variable 'pointer is the foreign pointer.  Note: type must be defined and loaded before this macro is expanded, and just the bare name (without :struct) should be specified."
           72   (let ((tclass (class-name (class-of (cffi::parse-type `(:struct ,type))))))
           73     (when (eq tclass 'foreign-struct-type)
           74       (error "Won't replace existing translation method for foreign-struct-type"))
           75     `(defmethod
           76          ,(case method
           77             (:into 'translate-into-foreign-memory)
           78             (:from 'translate-from-foreign)
           79             (:to 'translate-to-foreign))
           80        ;; Arguments to the method
           81        (,object
           82         (type ,tclass)
           83         ,@(when (eq method :into) '(pointer))) ; is intentional variable capture a good idea?
           84        ;; The body
           85        (declare (ignorable type)) ; I can't think of a reason why you'd want to use this
           86        ,@body)))
           87 
           88 (defmacro translation-forms-for-class (class type-class)
           89   "Make forms for translation of foreign structures to and from a standard class.  The class slots are assumed to have the same name as the foreign structure."
           90   ;; Possible improvement: optional argument to map structure slot names to/from class slot names.
           91   `(progn
           92      (defmethod translate-from-foreign (pointer (type ,type-class))
           93        ;; Make the instance from the plist
           94        (apply 'make-instance ',class (call-next-method)))
           95      (defmethod translate-into-foreign-memory ((object ,class) (type ,type-class) pointer)
           96        (call-next-method
           97         ;; Translate into a plist and call the general method
           98         (loop for slot being the hash-value of (structure-slots type)
           99               for name = (slot-name slot)
          100               append (list slot-name (slot-value object slot-name)))
          101         type
          102         pointer))))
          103 
          104 ;;; For a class already defined and loaded, and a defcstruct already defined, use
          105 ;;; (translation-forms-for-class class type-class)
          106 ;;; to connnect the two.  It would be nice to have a macro to do all three simultaneously.
          107 ;;; (defmacro define-foreign-structure (class ))
          108 
          109 #|
          110 (defmacro define-structure-conversion
          111     (value-symbol type lisp-class slot-names to-form from-form &optional (struct-name type))
          112   "Define the functions necessary to convert to and from a foreign structure.  The to-form sets each of the foreign slots in succession, assume the foreign object exists.  The from-form creates the Lisp object, making it with the correct value by reference to foreign slots."
          113   `(flet ((map-slots (fn val)
          114             (maphash
          115              (lambda (name slot-struct)
          116                (funcall fn (foreign-slot-value val ',type name) (slot-type slot-struct)))
          117              (slots (follow-typedefs (parse-type ',type))))))
          118      ;; Convert this to a separate function so it doesn't have to be recomputed on the fly each time.
          119      (defmethod translate-to-foreign ((,value-symbol ,lisp-class) (type ,type))
          120        (let ((p (foreign-alloc ',struct-name)))
          121          ;;(map-slots #'translate-to-foreign ,value-symbol) ; recursive translation of slots
          122          (with-foreign-slots (,slot-names p ,struct-name)
          123            ,to-form)
          124          (values p t))) ; second value is passed to FREE-TRANSLATED-OBJECT
          125      (defmethod free-translated-object (,value-symbol (p ,type) freep)
          126        (when freep
          127          ;; Is this redundant?
          128          (map-slots #'free-translated-object value) ; recursively free slots
          129          (foreign-free ,value-symbol)))
          130      (defmethod translate-from-foreign (,value-symbol (type ,type))
          131        (with-foreign-slots (,slot-names ,value-symbol ,struct-name)
          132          ,from-form))))
          133 |#