Reputation: 178
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
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
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
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