Changeset 548


Ignore:
Timestamp:
Feb 18, 2004, 10:51:29 AM (21 years ago)
Author:
beer
Message:

More (and different!) support for ObjC generic functions and methods

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/ccl/examples/objc-clos.lisp

    r543 r548  
    880880
    881881
    882 ;;; Return the generic function name and lambda list corresponding to
    883 ;;; a given ObjC MSG
    884 ;;; NOTE: Gary wants to handle "init..." messages specially
     882;;; Return the generic function name, lambda list and keywords corresponding
     883;;; to a given ObjC MSG
    885884
    886885(defun gfify (msg)
    887   (let ((mcomps (split-if-char #\: msg :elide)))
    888     (values
    889      (compute-lisp-name (first mcomps) (find-package "NS"))
    890      (case (count #\: msg)
    891        (0 '(self))
    892        (1 '(self arg))
    893        (t `(self arg &key ,@(mapcar #'compute-lisp-name (rest mcomps))))))))
     886  (let* ((mcomps (split-if-char #\: msg :elide))
     887         (ncolons (count #\: msg))
     888         (prefix (if (zerop ncolons) "@" "")))
     889    (values (compute-lisp-name
     890             (if (zerop ncolons)
     891                 (string-cat prefix (first mcomps))
     892               (first mcomps))
     893             (find-package "NS"))
     894            (if (zerop ncolons) '(self) '(self arg &key))
     895            (mapcar #'compute-lisp-name (rest mcomps)))))
     896
     897
     898;;; Special dcode for ObjC generic functions
     899;;; Currently, the list of keywords is used as the qualifier for an ObjC method
     900;;; This dcode just scans the list of methods looking for one whose qualifer
     901;;; matches the keywords in this call
     902
     903(defun %%objc-dcode (dt args)
     904  (flet ((invoke-method (largs)
     905           (multiple-value-bind (keys vals) (keys-and-vals (cddr largs))
     906             (declare (ignore vals))
     907             (dolist (m (%gf-dispatch-table-methods dt))
     908               (when (equal (method-qualifiers m) keys)
     909                 (return-from %%objc-dcode (apply (method-function m) largs))))
     910             (apply #'no-applicable-method (%gf-dispatch-table-gf dt) largs))))
     911    ;; If only on arg is present, args is apparently not encoded
     912    (if (numberp args)
     913        (with-list-from-lexpr (l args) (invoke-method l))
     914      (invoke-method (list args)))))
    894915
    895916
     
    898919(defun ensure-objc-generic-function (msg)
    899920  (multiple-value-bind (gf-name lambda-list) (gfify msg)           
    900     (when (and (fboundp gf-name) (generic-function-p (symbol-function gf-name)))
    901       (setq lambda-list
    902             (generalize-lambda-list
    903              (generic-function-lambda-list (symbol-function gf-name))
    904              lambda-list)))
    905     (ensure-generic-function
    906      gf-name
    907      :lambda-list lambda-list
    908      :generic-function-class (find-class 'objc-generic-function)
    909      :method-class (find-class 'objc-method))))
    910 
    911 
    912 ;;; Generalize a lambda list of an existing objc generic function to be
    913 ;;; consistent with a new lambda list
    914 
    915 (defun generalize-lambda-list (oldll newll)
    916   (if (equal oldll newll)
    917       oldll
    918     (let (req opt keys)
    919       (multiple-value-bind (reqold optold keysold)
    920                            (parse-objc-gf-lambda-list oldll)
    921         (multiple-value-bind (reqnew ignore keysnew) (parse-objc-gf-lambda-list newll)
    922           (declare (ignore ignore))
    923           (setq opt optold)
    924           (if (/= (length reqold) (length reqnew))
    925               (setq req '(self) opt '(arg))
    926             (setq req reqold))
    927           (setq keys (union keysold keysnew))
    928           `(,@req ,@(when opt (cons '&optional opt)) ,@(when keys (cons '&key keys))))))))
    929 
    930 (defun parse-objc-gf-lambda-list (ll)
    931   (let ((optpos (position '&optional ll :test #'eq))
    932         (keypos (position '&key ll :test #'eq)))
    933     (values
    934      (subseq ll 0 (or optpos keypos))
    935      (if optpos (subseq ll (1+ optpos) keypos))
    936      (if keypos (subseq ll (1+ keypos))))))
     921    (let ((gf (ensure-generic-function
     922               gf-name
     923               :lambda-list lambda-list
     924               :generic-function-class (find-class 'objc-generic-function)
     925               :method-class (find-class 'objc-method))))
     926      (setf (%gf-dcode gf) #'%%objc-dcode)
     927      gf)))
    937928
    938929
    939930;;; Ensure that the method corresponding to CLASS's method for MSG exists
    940931
    941 (defun ensure-objc-method (msg class)
    942   (multiple-value-bind (gf-name lambda-list) (gfify msg)
    943     (let* ((keypos (position '&key lambda-list :test #'eq))
    944            (required-args (subseq lambda-list 0 keypos))
    945            (keyword-args
    946             (if keypos (subseq lambda-list (1+ keypos)) nil)))
    947       (ensure-objc-generic-function msg)
    948       (ensure-method
    949        gf-name
    950        (cons (class-name class)
    951              (make-list (1- (length required-args)) :initial-element t))
    952        :function
    953        (compile nil
    954          `(lambda ,lambda-list
    955             ,(case (length lambda-list)
    956                (1 `(send self ,msg))
    957                (2 `(send self ,msg arg))
    958                (t `(send self ,msg arg ,@(append keyword-args '(&allow-other-keys)))))))
    959        :qualifiers nil
    960        :lambda-list lambda-list))))
     932(defun ensure-objc-method (msg &optional
     933                               (class (find-class 'objc:objc-object)))
     934  (flet ((keywordify (sym)
     935           (intern (string sym) (find-package 'keyword))))
     936    (multiple-value-bind (gf-name lambda-list keys) (gfify msg)
     937      (let* ((ncolons (count #\: msg))
     938             (class-name (class-name class))
     939             (gf (ensure-objc-generic-function msg))
     940             (lambda-list (append lambda-list keys))
     941             (m
     942              (ensure-method
     943               gf-name
     944               (if (zerop ncolons) (list class-name) (list class-name t))
     945               :function
     946               (compile nil
     947                        `(lambda ,lambda-list
     948                           ,(case ncolons
     949                                  (0 `(send self ,msg))
     950                                  (1 `(send self ,msg arg))
     951                                  (t `(send self ,msg arg ,@keys)))))
     952               :qualifiers (mapcar #'keywordify keys)
     953               :lambda-list lambda-list)))
     954        (setf (%gf-dcode gf) #'%%objc-dcode)
     955        m))))
    961956
    962957
    963958;;; Someday, this might even work...
    964959
    965 (defun define-all-objc-gfs ()
     960(defun define-all-objc-methods ()
    966961  (declare (special *type-signature-table*))
    967962  (maphash #'(lambda (msg ignore)
    968963               (declare (ignore ignore))
    969                (ensure-objc-generic-function msg))
     964               (ensure-objc-method msg))
    970965           *type-signature-table*))
    971966
    972 
    973 ;;; ISSUES
    974 ;;;  - Generic function conflicts
    975 ;;;  - Currently invokes compiler
    976 ;;;  - How to handle messages requiring STRETs?
    977 ;;;  - How to handle variable arity messages?
Note: See TracChangeset for help on using the changeset viewer.