Re: Macro context (continued)
Erik,
and you probably want a gensym insteam of *current-debug-context*:
(defparameter *debug-context-var* (gensym))
(defmacro with-debug-context (var &body body)
`(symbol-macrolet ((,*debug-context-var* ,var))
,@body))
(defmacro debug-log (message &rest args)
`(when ,*debug-context-var*
(format *error-output* ,message ,@args)))
CL-USER> (sb-cltl2:macroexpand-all
`(with-debug-context test2
(debug-log "true")))
(SYMBOL-MACROLET ((#:G1154 TEST2)) (IF TEST2 (PROGN (FORMAT
*ERROR-OUTPUT* "true")) NIL))
On Mon, Jul 11, 2011 at 5:13 PM, Paul A. Anokhin <paul7@paul7.net> wrote:
> Hi!
>
> I believe, you need a local macro or symbol-macro.
>
>> (defmacro with-debug-context (var &body body)
> `(symbol-macrolet ((*current-debug-context* ,var))
> ,@body))
>
>> (defmacro debug-log (message &rest args)
> `(when *current-debug-context*
> (format *error-output* ,message ,@args)))
>
>> (let ((test t))
> (with-debug-context test
> (debug-log "true")))
>
> true
>
> Hope this helps!
>
> On Mon, Jul 11, 2011 at 5:00 PM, Erik Ronström
> <erik.ronstrom@doremir.com> wrote:
>> Thinking about it a little bit more...
>> While your solution solves my problem very well, it would still be
>> interesting to know if my first approach is at all possible.
>>
>> As you pointed out, the evaluation of *test1* needs to happen at run
>>
>> time, and you'll need some way to keep track of that.
>>
>> But that doesn't mean that the "meta-variable" *current-debug-context* has
>> to be evaluated at run time!
>> Now we've been talking about debug logging, but imagining another context
>> where performance was important, it would be very nice to have the expansion
>> to be evaluated at compile time, so that
>> (debug-context *test1*
>> (debug-log "Log message")
>> )
>> would expand to
>> (when *test1* (do-some-logging "Log message"))
>> Of course, *test1* is still evauated at run time, but the expansion is made
>> at compile time. Is this possible in CL?
>> Regards
>> Erik
>>
>>
>>
>> This seems like
>>
>> a good application for closures. Would the following work for you?
>>
>> (defparameter *debug-context-thunks* '()
>>
>> "A list of functions that take zero arguments. When every function
>>
>> returns true, debug messages will be printed \(by debug-log\).")
>>
>> (defmacro debug-context (context-form &body body)
>>
>> "Debug-context arranges for the body forms to be evaluated in a
>>
>> dynamic environment where *debug-context-thunks* includes a function
>>
>> whose body is context-form."
>>
>> `(let ((*debug-context-thunks*
>>
>> (list* #'(lambda () ,context-form)
>>
>> *debug-context-thunks*)))
>>
>> ,@body))
>>
>> (defun debug-log (control-string &rest format-arguments)
>>
>> "Debug-log applies format to the control-string and format-arguments
>>
>> when every function in *debug-context-thunks* returns true."
>>
>> (when (every 'funcall *debug-context-thunks*)
>>
>> (apply 'format t control-string format-arguments)))
>>
>> It would be used as follows:
>>
>> CL-USER 2 >
>>
>> (defparameter *test1* nil)
>>
>> *TEST1*
>>
>> CL-USER 3 >
>>
>> (debug-context *test1*
>>
>> (let ((*test1* t))
>>
>> (debug-log "~&Log Message ~A" 1))
>>
>> (let ((*test1* nil))
>>
>> (debug-log "~&Log Message ~A" 2)))
>>
>> Log Message 1
>>
>> NIL
>>
>> This isn't necessarily a final solution. For instance, since
>>
>> *debug-context-thunks* is a special variable, and debug-context adds
>>
>> thunks onto it at each step, you'll end up with a whole bunch of
>>
>> combined context tests when you're in deeply nested function calls.
>>
>> This may or may not be what you want. You could also have something
>>
>> that checks just the most recent context, e.g.,
>>
>> (defmacro single-debug-context (form &body body)
>>
>> `(let ((*debug-context-thunks* (list #'(lambda () ,form))))
>>
>> ,@body))
>>
>> At any rate, I hope this is a useful starting point.
>>
>> //JT
>>
>>
>> --
>>
>> Joshua Taylor, http://www.cs.rpi.edu/~tayloj/
>>
>>
>>
>>
>>
>> --
>> Joshua Taylor, http://www.cs.rpi.edu/~tayloj/
>>
>>
>
>
>
> --
> Best regards,
> Paul A. Anokhin
>
--
Павел Анохин