Today I spent about an hour trying to find out the source of a bug in the code for my PhD prototype. To make the context short, I need to have a class definition to be stored, and loaded afterwards. And I was using Gary King's time-saver metatilities:defclass* to reduce the required coding. The problem was when I wanted to load such a definition in a generic method, I was evaluating the definition expression - one of the acceptable uses of `eval', correct me if I'm wrong, is to interpret something that was just read (in this case, from a serialized string, from other servers). The code in question was something like this:
(defmacro load-data (name attributes) "Defines a domain concept, using metatilities:defclass* syntax." `(progn (metatilities:defclass* ,name (data-item) (,@attributes) (:metaclass profiled-metaclass) (:name-prefix ,(format nil "~(~A~)" name))) (info "Data type loaded: ~A" ',name))) (defmethod load-definition ((def data-item-definition)) (eval (macroexpand `(load-data ,(definition-name def) ,(data-item-definition-attributes def)))))
What I was getting as a result is that the auxiliary methods created as accessors by defclass* weren't being imported in my packaged, but in CL-USER. Hence, lots and lots of compiler warnings and subsequent errors when trying to use those accessors. I fiddled around, tried to see if I could remove the eval, but the solution, so obvious as I know can see, is quite simple. Just place your evaluated expression after setting the package. The fixed method is the following:
(defmethod load-definition ((def data-item-definition)) (eval `(progn (in-package :dshow) ,(macroexpand `(load-data ,(definition-name def) ,(data-item-definition-attributes def))))))
Hope you can see this before spending too much time on a similar bug!
2 comments:
Actually, it is better to just bind the *package* variable to the package that will receive the interned symbols. Otherwise, you might create mess somewhere else (in a code that expects *package* to be set to something else)
Ah, missed that! It is obviously what I should had thought about in the first place :). This way not only I can change the package name in the future but, as you mention, others may use the received code in other packages. Thanks for the tip!
Post a Comment