Same thing for Gauche's object and condition system Lassi Kortela 14 Aug 2020 12:50 UTC

(define-condition-type <foreign> <error> foreign-status?
   (set foreign-status-set)
   (code foreign-status-code)
   (table %foreign-status-table))

(define (%make-foreign-status condition-type plist)
   (define (ensure-symbol x)
     (cond ((keyword? x) (string->symbol (keyword->string x)))
           ((symbol? x) x)
           (else (error "Bad plist keyword:" x))))
   (let ((table (make-hash-table)))
     (let loop ((tail plist) (set #f) (code #f))
       (cond ((null? tail)
              (make-condition condition-type
                              'set set
                              'code code
                              'table table))
             ((not (and (pair? tail) (pair? (cdr tail))))
              (error "Bad plist:" plist))
             (else
              (let ((property (ensure-symbol (car tail)))
                    (value (cadr tail)))
                (case property
                  ((set)
                   (loop (cddr tail) value code))
                  ((code)
                   (loop (cddr tail) set value))
                  (else
                   (hash-table-set! table property value)
                   (loop (cddr tail) set code)))))))))

(define (make-foreign-status :key :allow-other-keys plist)
   (%make-foreign-status <foreign> plist))

(define-method foreign-status-ref ((st <foreign>) property)
   (case property
     ((set code)
      (condition-ref st property))
     (else
      (hash-table-ref/default (%foreign-status-table st) property #f))))

;;

(define-condition-type <errno> <foreign> errno-status?)

(define-method foreign-status-ref ((st <errno>) property)
   (case property
     ((message)  (sys-strerror (foreign-status-code st)))
     (else       (next-method))))

(define (make-errno-status code :key :allow-other-keys plist)
   (%make-foreign-status <errno> (list* :set 'errno :code code plist)))

;;

(define-syntax pp
   (syntax-rules ()
     ((_ x) (begin (write 'x) (display " => ") (write x) (newline)))))

(let ((st (make-foreign-status :set 'errno :code 4)))
   (pp (foreign-status? st))
   (pp (errno-status? st))
   (pp (foreign-status-set st))
   (pp (foreign-status-ref st 'set))
   (pp (foreign-status-code st))
   (pp (foreign-status-ref st 'code)))
(newline)
(let ((st (make-errno-status 4 :foreign-procedure 'read)))
   (pp (foreign-status? st))
   (pp (errno-status? st))
   (pp (foreign-status-set st))
   (pp (foreign-status-ref st 'set))
   (pp (foreign-status-code st))
   (pp (foreign-status-ref st 'code))
   (pp (foreign-status-ref st 'message))
   (pp (foreign-status-ref st 'foreign-procedure)))

;;;; OUTPUT:

(foreign-status? st) => #t
(errno-status? st) => #f
(foreign-status-set st) => errno
(foreign-status-ref st 'set) => errno
(foreign-status-code st) => 4
(foreign-status-ref st 'code) => 4

(foreign-status? st) => #t
(errno-status? st) => #t
(foreign-status-set st) => errno
(foreign-status-ref st 'set) => errno
(foreign-status-code st) => 4
(foreign-status-ref st 'code) => 4
(foreign-status-ref st 'message) => "Interrupted system call"
(foreign-status-ref st 'foreign-procedure) => read