Kyle
Kyle

Reputation: 178

In Allegro CL and ABCL, can I construct a struct given only its type name?

In most implementations, you can use (make-instance 'struct-type) to create a struct regardless of whether you defined a constructor function for that type. This doesn't work on Allegro or ABCL, but those implementations can still create structs at read-time with #S(struct-type), which makes me think there must be some other way to construct them at runtime given the type name as a symbol.

Upvotes: 1

Views: 242

Answers (3)

Kyle
Kyle

Reputation: 178

I've thought of one potential solution, but it's a bit of a hack. Since the #S reader macro works but I can't find out how ABCL and Allegro implement it (it may be totally internal to the implementation and not defined in Lisp), I can at least generate a string and programmatically invoke the reader macro function on it:

(defun make-struct-from-type (type-name)
  (with-input-from-string
      (s (format nil "(~A::~A)"
                 (package-name (symbol-package type-name))
                 (symbol-name type-name)))
    (funcall (get-dispatch-macro-character #\# #\S)
             s #\S nil)))

This might not work on other implementations though. SBCL complains about sb-impl::*read-buffer* being unbound, so it may require a little more finessing to trick the implementation into thinking the reader macro is being invoked in a typical context.

This should invoke the default constructor even if it was given a name without the default make- prefix.

Upvotes: 0

Frank Zalkow
Frank Zalkow

Reputation: 3930

Maybe not very elegant, but whats about:

(defun make-struct-by-name (name &rest args)
  (apply (symbol-function
          (intern (concatenate 'string "MAKE-"
                               (symbol-name name))))
         args))

Usage:

CL-USER> (defstruct foo
           a b)
FOO

CL-USER> (make-struct-by-name 'foo :a 1 :b 2)
#S(FOO :A 1 :B 2)

Upvotes: 0

acelent
acelent

Reputation: 8135

You shouldn't mix structure and object creation. The standard defined make-instance for standard-class, and for symbol such that the provided symbol is passed to find-class, then make-instance recurses.

As such, a stock implementation that extends make-instance for named structure types is not conforming, and neither is your code if you rely on it.

Then, #S is specified to work correctly only if the structure has a standard constructor, so there's not much magic left there.

Given this restriction, you could implement #S yourself by interning a symbol named make- concatenated with the structure name, followed by the keyword argument list.

But again, the implementation dependent details hit. You ask about when there's no constructor, which implies :constructor nil in defstruct. Note that not specifying the constructor argument means it'll have a default constructor. The implementation can have internal bookkeeping, including a hidden standard constructor that it creates regardless of your options (or a parameterless constructor and slot accessors) to be used in make-load-form-using-slots, an extended #S, and possibly to optimize literal structure loading (as opposed to forms that create one) in file compilation through make-load-form's specialization for structures.

Upvotes: 1

Related Questions