5.1.2 Value Check Protocol

Now that we have our new option class, we need to implement the so-called value check protocol. This protocol is used to make sure that values provided for options of your new type actually comply with the type in question. Values going through this protocol are fallback values, default values, and values provided from a debugger restart (see Error Management in The Clon End-User Manual). In the case of fallback and default values (which, by the way, are provided by you, the Clon user), the check is performed only once, when the option object is created. Values provided from a debugger restart come from the application end-user, and hence are checked every time.

The value check protocol is implemented through a check generic function for which you must provide a method.

Generic Function: check OPTION VALUE

Check that VALUE is valid for OPTION. If VALUE is valid, return it. Otherwise, raise an invalid-value error.

As you can see, you need to provide a method with the first argument specialized to your new option type. This method must return VALUE if it is okay, and raise an invalid-value error otherwise.

Clon maintains a hierarchy of error conditions. The invalid-value error condition is defined like this:

(define-condition invalid-value (option-error)
  ((value :documentation "The invalid value."
          :initarg :value
          :reader value)
   (comment :documentation "An additional comment about the error."
            :type string
            :initarg :comment
            :reader comment))
  (:report (lambda (error stream)
             (format stream "Option ~A: invalid value ~S.~@[~%~A~]"
               (option error) (value error) (comment error))))
  (:documentation "An invalid value error."))

When the error is raised, you must fill in the value and comment slots appropriately. The super-condition option-error provides an additional option slot that you must also fill in when the error is raised.

Let’s look at the enumeration example now.

(defmethod check ((enum enum) value)
  "Check that VALUE is a valid ENUM."
  (unless (member value (enum enum))
    (error 'invalid-value
           :option enum
           :value value
           :comment (format nil "Valid values are: ~A."
                      (list-to-string (enum enum)
                                      :key #'prin1-to-string))))
  value)

This code should be self-explanatory. We check that the value we got belongs to the enumeration. list-to-string is a utility function that will separate every element with comas in the resulting string.