Changeset 2523

Show
Ignore:
Timestamp:
02/17/08 22:27:25 (9 months ago)
Author:
hhubner
Message:

Improve DEFTRANSACTION:

Define wrapper function with docstring, if supplied. Use lambda list
specified in DEFTRANSACTION for the wrapper function. Handle
docstrings correctly. Insert IN-TRANSACTION-P check after
declarations and docstring in generated function.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/trunk-reorg/bknr/datastore/src/data/txn.lisp

    r2283 r2523  
    241241  (execute-unlogged transaction)) 
    242242 
     243(defun find-doc (body) 
     244  "Given a function definition BODY, extract the docstring, if any. 
     245Skips over any declarations that precede the docstring.  See also CLHS 
     2463.4.11" 
     247  (do ((body body (cdr body))) 
     248      ((or (not (listp (car body))) 
     249           (not (eq 'declare (caar body)))) 
     250       (when (and (stringp (car body)) 
     251                  (cdr body)) 
     252         (car body))))) 
     253 
     254(defun insert-after-declarations (body forms-to-insert) 
     255  "Given a function definition body, insert FORMS-TO-INSERT after all 
     256declarations and documentation in BODY." 
     257  (loop for rest on body 
     258        for form = (car rest) 
     259        with decls 
     260        with doc 
     261        while (or (and (listp form) (eq 'declare (car form))) 
     262                  (and (not doc) (cdr rest) (stringp form))) 
     263        when (stringp form) 
     264        do (setf doc form) 
     265        do (push form decls) 
     266        finally (return-from insert-after-declarations (append (nreverse decls) forms-to-insert rest)))) 
     267 
     268(defun make-args (args) 
     269  "Parse the lambda list ARGS, returning a list that contains the 
     270arguments in the lambda list prepared so that the list can be applied 
     271to a function accepting that lambda list. 
     272 
     273For example: 
     274 
     275 (MAKE-ARGS '(A B &OPTIONAL C &REST D &KEY E F)) => (A B C :E E :F F) 
     276 
     277It is used to forward arguments to a transaction wrapper generated by 
     278DEFTRANSACTION to the actual transaction so that the wrapper function 
     279can be declared with the lambda list of the transaction function 
     280itself," 
     281  (do ((args args (cdr args)) 
     282       result 
     283       in-keywords-p) 
     284      ((not args) 
     285       (nreverse result)) 
     286    (let ((arg (funcall (if (listp (car args)) #'caar #'car) args))) 
     287      (cond 
     288        ((eql #\& (aref (symbol-name arg) 0)) 
     289         (case arg 
     290           (&optional) 
     291           (&rest (setf args (cdr args))) ; skip argument, too 
     292           (&key (setf in-keywords-p t)) 
     293           (otherwise (error "unsupported lambda list option ~A in DEFTRANSACTION" arg)))) 
     294        (t 
     295         (when in-keywords-p 
     296           (push (intern (symbol-name arg) :keyword) result)) 
     297         (push arg result)))))) 
     298 
    243299(defmacro deftransaction (name (&rest args) &rest body) 
    244   "Define a transaction function tx-NAME and a function NAME executing tx-NAME in the context 
    245 of the current store. The arguments to NAME will be serialized to the transaction-log, and 
    246 should must be supported by the binary encoder. tx-NAME will be called during a roll-forward." 
    247   (dolist (arg args) 
    248     (when (listp arg) 
    249       (error "can't have argument defaults in transaction declaration for transaction ~A, please implement a wrapper" name))) 
    250   (let ((args-name (gensym)) 
    251         (tx-name (intern (string-upcase (concatenate 'string "tx-" (symbol-name name))) 
    252                          (symbol-package name)))) 
    253     `(progn 
    254       (defun ,tx-name ,args 
    255         (unless (in-transaction-p) 
    256           (error 'not-in-transaction)) 
    257         ,@body) 
    258       (defun ,name (&rest ,args-name) 
    259         (execute (make-instance 'transaction 
    260                                 :function-symbol ',tx-name 
    261                                 :timestamp (get-universal-time) 
    262                                 :args ,args-name)))))) 
     300  "Define a transaction function tx-NAME and a function NAME executing 
     301tx-NAME in the context of the current store. The arguments to NAME 
     302will be serialized to the transaction-log, and should must be 
     303supported by the binary encoder. tx-NAME will be called during a 
     304roll-forward." 
     305  (let ((name name) 
     306        (args args) 
     307        (body body)) 
     308    (dolist (arg args) 
     309      (when (listp arg) 
     310        (error "can't have argument defaults in transaction declaration for transaction ~A, please implement a wrapper" name))) 
     311    (let ((tx-name (intern (format nil "TX-~A" name) 
     312                           (symbol-package name)))) 
     313      `(progn 
     314        (defun ,tx-name ,args 
     315          ,@(insert-after-declarations body 
     316                                       '((unless (in-transaction-p) 
     317                                           (error 'not-in-transaction))))) 
     318        (defun ,name ,args 
     319          ,@(let ((doc (find-doc body))) 
     320                 (when doc (list (format nil "[Transaction function wrapper ~A invokes a store transaction]~%~A" name doc)))) 
     321          ,@(let ((rest (member '&rest args))) 
     322                 (when rest `((declare (ignore ,(second rest)))))) 
     323          (execute (make-instance 'transaction 
     324                                  :function-symbol ',tx-name 
     325                                  :timestamp (get-universal-time) 
     326                                  :args (list ,@(make-args args))))))))) 
    263327 
    264328(defmethod encode-object ((object transaction) stream)