The point was that "debug" and "message" parts in the example were arbitrarily complex expressions which were computationally expensive to evaluate. Your wrapping of log debug would still require evaluating them to get the message string unconditionally, even when debug logging is not enabled.
Of course, with support for first-class functions, you could do something like:
func debug(produceMessage ()->string) {
if log.isDebugEnabled() {
log.debug(produceMessage());
}
}
The downside there is you can still end up allocating a closure. Whereas an expanded macro shouldn't cost anything. (A sufficiently smart compiler might be able to optimize the closure allocation.)
A sufficiently modern language (like, saaaaay, D2) could also give you a type like "closure you don't intend to escape" (let's call this a "scoped closure", or if you will, "scope string delegate()"), and eschew allocation entirely without requiring optimization.
I don't understand why this is special?
Why can't you rewrite log.debug to include the check?
What makes macros special in this case?