4

I'm trying to implement json serialization API for common lisp. To achieve this I've defined a metaclass called json-class. This metaclass defines the slot options :ignore which are used to ignore specific slots of the object. Since I'm using yason for the serialization process, I wanted to specialize the method yason:encode-slots to every object of classes that use the metaclass json-class. The only way I can think of to achieve this, is to make all the objects instantiated from a json-class of type json-object which can be used to specialize the methods. The behaviour that I'm trying to replicate is the same as the one already implemented by the MOP consisting in every class using the standard-class producing an object of type standard-object.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
MPaga
  • 319
  • 2
  • 6

2 Answers2

2

Use Closer to Mop:

(defpackage :so (:use :closer-common-lisp))
(in-package :so)

;; Define a metaclass named json-class.
;; It inherits from a standard-class
(defclass json-class (standard-class) ())

Specialize VALIDATE-SUPERCLASS (see also VALIDATE-SUPERCLASS explained).

(defmethod validate-superclass ((j json-class) (s standard-class)) t)

Then you define the base json-object.

(defclass json-object () () (:metaclass json-class))

For example:

(class-of (make-instance 'json-object))
#<JSON-CLASS SO::JSON-OBJECT>

The returned object is a json-object, an instance of json-class, a subclass of standard-class.

coredump
  • 37,664
  • 5
  • 43
  • 77
  • 2
    So, if I understand correctly, if I then want to create a class with the `json-class` properties and I want it to inherit all the methods specialized for `json-object` I should define for example the class as `(defclass person (json-object) () (:metaclass json-class))` right? I also had an idea of using the `ensure-class-using-class` method to automatically add `json-object` to the `direct-superclasses` list but it does not look explicit to me. Would it be considered good design to just keep it explicit in the class definition? Or is there a better way to declare such a class? – MPaga Jul 26 '19 at 16:25
  • @MPaga: I think there ought to be an easy way to default the superclasses (after all, there is for instances of `standard-class`. But I can't work out what it should be. –  Jul 27 '19 at 19:38
1

If you look through the source code of SBCL, you'll find that it adds standard-object to the list of direct superclasses inside of shared-initialize:

         (setq direct-superclasses
               (or direct-superclasses
                   (list (if (funcallable-standard-class-p class)
                             *the-class-funcallable-standard-object*
                             *the-class-standard-object*))))

If you wish to implicitly add a class to the direct superclasses list, it may be best to do so in an :around method on shared-initialize or initialize-instance.

Another option is to specialize compute-class-precedence-list to, for example:

(cons my-superclass (call-next-method))

This is what An existing JSON MOP library does.

EDIT:

I think messing with compute-class-precedence-list to add superclasses is undefined behavior, though it usually works. I believe the best solution is to write an :around method for either shared-initialize or initialize-instance that first checks (using subclassp) whether your class is already in the inheritance chain, then adds it if not.

markasoftware
  • 12,292
  • 8
  • 41
  • 69