idiomatic approach conditional inclusion/exclusion of code during debugging

2,673 views
Skip to first unread message

kortschak

unread,
Aug 8, 2011, 2:47:16 AM8/8/11
to golang-nuts
Hi,

I'm wondering what people do in place of the C/C++ idiom of

#define DEBUG

#ifdef DEBUG
printf("foo %d", bar);
#endif

Particularly where there may be many wrapped debug printing
statements.


thanks
Dan

David Symonds

unread,
Aug 8, 2011, 2:56:23 AM8/8/11
to kortschak, golang-nuts
A neat trick is something like this:

var debugging debug = true // or flip to false

type debug bool

func (d debug) Printf(format string, args ...interface{}) {
if d {
log.Printf(format, args...)
}
}

and then your code can just write

debug.Printf("foo %d %.2f", 42, 12)


Dave.

bflm

unread,
Aug 8, 2011, 3:01:47 AM8/8/11
to golan...@googlegroups.com
const DEBUG = true

if DEBUG {
    fmt.Printf("foo %d", bar)
}

Dan Kortschak

unread,
Aug 8, 2011, 3:15:15 AM8/8/11
to David Symonds, golang-nuts
Yeah, I was thinking of something like that and maybe making it an
imported package so when it comes time to release it's easy to find all
the statements - how does the compiler go with optimising a false debug
out of the generated code (I'm assuming that this will happen)?

thanks
Dan

--
Omnes mundum facimus.

Dan Kortschak <dan.ko...@adelaide.edu.au>
F9B3 3810 C4DD E214 347C B8DA D879 B7A7 EECC 5A40

Rob 'Commander' Pike

unread,
Aug 8, 2011, 3:32:24 AM8/8/11
to golang-nuts List
David's example had a couple of bugs. Here's a version that works, and also promotes the debug flag to a const (it was a var in the original). Method on const: why not?

Note that in all these cases, the arguments will be evaluated even if debug is false. This is different in effect and cost from the #define idiom of C.

The compiler is unlikely to be clever any time soon in this sort of thing.

-rob


package main

import "log"

const debug debugging = true // or flip to false

type debugging bool

func (d debugging) Printf(format string, args ...interface{}) {
if d {
log.Printf(format, args...)
}
}

func main() {
log.Printf("foo %d %.2f", 42, 12.7)
}

Rob 'Commander' Pike

unread,
Aug 8, 2011, 3:47:15 AM8/8/11
to golang-nuts List

On 08/08/2011, at 5:32 PM, Rob 'Commander' Pike wrote:

> David's example had a couple of bugs. Here's a version that works, and also promotes the debug flag to a const (it was a var in the original). Method on const: why not?
>
> Note that in all these cases, the arguments will be evaluated even if debug is false. This is different in effect and cost from the #define idiom of C.
>
> The compiler is unlikely to be clever any time soon in this sort of thing.

Actually, the language requires that the arguments be evaluated, so s/unlikely/certain never/ and s/any time soon//.

-rob

Steven Blenkinsop

unread,
Aug 8, 2011, 12:22:56 PM8/8/11
to Rob 'Commander' Pike, golang-nuts List
In bflm's version, the compiler could discard discard the if statement and conditionally on the const include the print statement.

John Asmuth

unread,
Aug 8, 2011, 1:20:52 PM8/8/11
to golan...@googlegroups.com, Rob 'Commander' Pike
If the compiler starts inlining, then having a function which checks a const bool could also be compiled away.

Russ Cox

unread,
Aug 8, 2011, 1:33:58 PM8/8/11
to golan...@googlegroups.com, Rob 'Commander' Pike
On Mon, Aug 8, 2011 at 13:20, John Asmuth <jas...@gmail.com> wrote:
> If the compiler starts inlining, then having a function which checks a const
> bool could also be compiled away.

Not if it says

d.Print(expensiveFunctionCall())

You can't compile away the function call without violating the spec.

Russ

DisposaBoy

unread,
Aug 8, 2011, 2:17:34 PM8/8/11
to golan...@googlegroups.com
If we change the idiom from d.Print() to.Call() where Call takes a function then the entire expression can be optomized away if and when the compiler becomes smart enough as there are no side-effects in that case. I personally prefer this style it's more general

Andrew Gerrand

unread,
Aug 13, 2011, 6:29:56 PM8/13/11
to golan...@googlegroups.com
If you use David's suggestion but name your debugging package something unique, like DEBUG, you can just "grep -v DEBUG" your source files before compiling them for production use.

A bit of a hack, but only marginally moreso than C's #define IMO.

Andrew 

Morgaine

unread,
Aug 16, 2011, 4:25:25 AM8/16/11
to golang-nuts, Morgaine Dinova
kortschak, since the answers you received more or less amount to "We can't do that in Go", you might wish to contemplate that your idiomatic C/C++ example actually works just as well with any text-based language, including Go.  Arrange for make to pass your Go code through the C preprocessor. :-)

That workaround aside ...

Both conditional compilation and conditional argument evaluation at runtime are often needed in the real world, particularly in the case of languages whose goal it is to be efficient.  If such features are not in the Go spec, then I'm inclined to suggest that they'd better be added.  With a bit of thought, I'm sure that an elegant approach can be found.


Morgaine.



===================

Peter Bourgon

unread,
Aug 16, 2011, 5:05:24 AM8/16/11
to golang-nuts
> Both conditional compilation and conditional argument evaluation at runtime
> are often needed in the real world, particularly in the case of languages
> whose goal it is to be efficient.  If such features are not in the Go spec,
> then I'm inclined to suggest that they'd better be added.

-1.

Morgaine

unread,
Aug 16, 2011, 5:12:42 AM8/16/11
to golang-nuts, Morgaine Dinova
"-1" with no reason given is less than enlightening.

Engineering makes progress through evaluating merits and tradeoffs, not through obtaining majority votes.  Majority votes get you the kind of progress that we have in politics, namely none.

Do you have an engineering reason for your "-1"?  Go is too young a language to be in stasis yet.


Morgaine.



======================

Jan Mercl

unread,
Aug 16, 2011, 6:01:37 AM8/16/11
to golan...@googlegroups.com, Morgaine Dinova
On Tuesday, August 16, 2011 11:12:42 AM UTC+2, Morgaine wrote:
"-1" with no reason given is less than enlightening.

Engineering makes progress through evaluating merits and tradeoffs, not through obtaining majority votes.  Majority votes get you the kind of progress that we have in politics, namely none.

Do you have an engineering reason for your "-1"?  Go is too young a language to be in stasis yet.

Simply search the mailing list, this has been discussed more than once I believe.

Ad "With a bit of thought, I'm sure that an elegant approach can be found." - an elegant approach has never been presented for this. Just imagine e.g. the obvious consequences for separate/incremental compilation which now Go programmers enjoy (with at max linear growth wrt project size) and compare it to the amount of work some autoreconf/automake has to do with when a single #DEFINE is changed (and with possible exponential growth wrt project size).

Other nasty things come out for any source level tools - they would have to learn the whole metalanguage. And yes, for unknown reasons those metalanguages try to get Turing complete quickly :-)

Peter Bourgon

unread,
Aug 16, 2011, 6:01:36 AM8/16/11
to golang-nuts
> "-1" with no reason given is less than enlightening.

It is a simple response to a suggestion, and to some degree it acts as
a bellwether for the community, but, fair enough.

> conditional compilation [is] often needed in the real world

I reject this premise, even in C. It's weaker still in the context of
Go, which has no significant differences between architectures that
demand conditional compilation. (Architectural differences accounting
for the vast majority of the preprocessor's valid use-cases.)

I agree that a preprocessor is a quick and (at first glance) simple
answer to solving concrete problems of engineering, but (IMO) that
kind of naïvety quickly and invariably brings one to the Macro Hell of
unmaintainable codebases. Any problem which can be solved with a
#define is better, more properly solved with another tool -- full
stop.

For me, Go is a great language largely because it is light, airy, and
productive. Introducing a preprocessor compromises that philosophy to
solve problems that we don't actually have, based on the precedent of
historical mistakes.

In short, -1.

Peter Bourgon

unread,
Aug 16, 2011, 6:20:27 AM8/16/11
to golan...@googlegroups.com
> Ad "With a bit of thought, I'm sure that an elegant approach can be found."
> - an elegant approach has never been presented for this.

An elegant, if eccentric, approach is already available. It involves
gofmt -r, and an example is available at

$GOROOT/src/pkg/container/vector/Makefile

Jan Mercl

unread,
Aug 16, 2011, 6:32:27 AM8/16/11
to golan...@googlegroups.com
Yeah, I know that one really well :-)

But that's not about #DEFINE and friends ("conditional inclusion/exclusion of code during debugging"), is it?

Morgaine

unread,
Aug 16, 2011, 6:43:07 AM8/16/11
to golan...@googlegroups.com, Morgaine Dinova
Jan, I couldn't agree more that C's preprocessor-based conditional compilation and macros are quite a disaster, which is why my post was in two parts:  the first a workaround for kortschak in view of the fact that Go has no solution for him, and the second (which I very clearly split off with "That workaround aside ...") a suggestion that an elegant mechanism be found for Go to give it some form of conditional efficiency gains as well, albeit nothing like C's.

As your answer shows, you're aware that the key problem with C's primitive approach is that the preprocessor works at unstructured text level and so its transformations have no syntactic relationship whatsoever with the grammar of the language that it processes.  "Nasty" is an understatement.  It's poor engineering.

But a well designed mechanism for conditional compilation needn't have these problems.  The bulk of them disappear simply by making conditional compilation well structured and an integral part of the language, grammatically integrated with Go.  It's not particularly hard to do, but it does require willingness to accept that Go is not yet cast in stone.  The same applies to conditional evaluation of arguments.


Morgaine.




========================

Morgaine

unread,
Aug 16, 2011, 6:54:13 AM8/16/11
to golang-nuts, Morgaine Dinova
Peter, your reply tells me that you thought I was suggesting that Go acquire a C-like preprocessor and/or macro processor.  A more careful reread of my post should reassure you that you misunderstood completely.

More on what I was suggesting can be found in my reply to Jan.  It had nothing to do with preprocessing but implied tight and well-structured integration with Go's grammar.


Morgaine.


========================

Peter Bourgon

unread,
Aug 16, 2011, 7:41:29 AM8/16/11
to golang-nuts
> More on what I was suggesting can be found in my reply to Jan.  It had
> nothing to do with preprocessing but implied tight and well-structured
> integration with Go's grammar.

You're now describing syntactic preprocessors, which (as a rule) speak
to a different problem domain than lexical preprocessors. According to
Wikipedia[1],

"Syntactic preprocessors are typically used to customize the syntax
of a language, extend a language by adding new primitives, or embed a
Domain-Specific Programming Language inside a general purpose
language."

[1] http://en.wikipedia.org/wiki/Preprocessor#Syntactic_preprocessors

These goals are orthogonal to goals of efficiency, which you
identified as your primary motivation in the email I replied to. If I
misread that, my apologies.

In any case, I would be -1 on either type of preprocessor in Go. I
simply see no need.

Morgaine

unread,
Aug 16, 2011, 8:25:05 AM8/16/11
to golang-nuts, Morgaine Dinova
On Tue, Aug 16, 2011 at 12:41 PM, Peter Bourgon <peterb...@gmail.com> wrote:

These goals are orthogonal to goals of efficiency, which you
identified as your primary motivation in the email I replied to. If I
misread that, my apologies.


I'm not sure how you jumped from simple conditional compilation to full-blown syntactic transformation, which I certainly did not mention.  My interest is the same as that of the starter of this thread, namely to provide efficient support for running a program in two or more modes which differ in nothing more than which parts of the program's syntax tree have been compiled.

The two or more modes of conditional compilation typically arise from the different demands encountered during development, testing, debug release, and production release.  The efficiency gains of not compiling in code that is relevant only during development may be not only large but instrumental in making the end product viable.  The code may not even fit in the memory of the intended target (such as a smartphone) without conditional compilation.

Claiming that there is no need might be appropriate for a toy language, but shows little awareness of professional software development.  Go is certainly not intended as a toy, and deserves features that support the needs of commercial production and flexible deployment.  Conditional compilation is one such feature.


Morgaine.


========================

Peter Bourgon

unread,
Aug 16, 2011, 8:37:08 AM8/16/11
to golang-nuts
> Claiming that there is no need might be appropriate for a toy language, but
> shows little awareness of professional software development.  Go is
> certainly not intended as a toy, and deserves features that support the
> needs of commercial production and flexible deployment.  Conditional
> compilation is one such feature.

As a professional software developer, I simply do not accept the
assertion that conditional compilation is necessary for "commercial
production and flexible deployment." There is no problem for which it
is the best, or even a good, solution.

Even granting that, it's not evident to me that a preprocessor is the
best, or even a good, way to achieve it. IMO, it's rightly the domain
of the build tool, not the language.

Uriel

unread,
Aug 16, 2011, 8:38:02 AM8/16/11
to Morgaine, golang-nuts
Go has 'if', 'efficiency' should be accomplished by the compiler being
smart enough to identify dead code, it makes no sense to make the
language more convoluted to duplicate a feature that is semantically
already there. Also pretty much any form of conditional compilation is
asking for a debugging and testing nightmare. Just say no.

Morgaine

unread,
Aug 16, 2011, 9:13:29 AM8/16/11
to golang-nuts, Morgaine Dinova
On Tue, Aug 16, 2011 at 1:38 PM, Uriel <ur...@berlinblue.org> wrote:
Also pretty much any form of conditional compilation is
asking for a debugging and testing nightmare.


That is simply FUD.

Conditional compilation that is properly integrated into a language can guarantee that whatever constraints you require to hold will actually hold.  For example, it can guarantee that conditional inclusion or omission of code cannot in any way modify state variables anywhere else in the program.

What's more, the latter represents one of the most important uses of conditional compilation during development.  Conditionally compiled diagnostic code is almost universally required not to modify the value state of the program in any way, but only to generate additional data for performance instrumentation or diagnostic analysis.

Your objection seems to have forgotten the purpose of conditional compilation expressed in kortschak's original post, namely to support inclusion of debug code without making the final release inefficient.  How you've managed to call a direct aid for debugging a "debugging and testing nightmare" is pretty bizarre.  Not to mention lacking in any form of reasoned justification, and laced with emotive words like "nightmare".

Well reasoned objections are important.  It would be nice to hear some.


Morgaine.



=======================

Morgaine

unread,
Aug 16, 2011, 9:39:47 AM8/16/11
to golang-nuts, Morgaine Dinova
On Tue, Aug 16, 2011 at 1:38 PM, Uriel <ur...@berlinblue.org> wrote:
Go has 'if', 'efficiency' should be accomplished by the compiler being
smart enough to identify dead code,


That's exactly what is being requested, as long as "smart enough" includes the ability to omit from compilation everything that is unreachable.  This may include omitting extensive code that implements powerful development facilities which cannot run on the release target, not merely a few print statements.

As a simple example, program output generated at development time might be highly graphical, while the end target might be headless.  Huge amounts of code might need to be conditionally excluded when compiling the final release.


Morgaine.




======================

On Tue, Aug 16, 2011 at 1:38 PM, Uriel <ur...@berlinblue.org> wrote:

Ian Lance Taylor

unread,
Aug 16, 2011, 11:02:53 AM8/16/11
to Morgaine, golan...@googlegroups.com
Morgaine <morgain...@googlemail.com> writes:

> But a well designed mechanism for conditional compilation needn't have these
> problems. The bulk of them disappear simply by making conditional
> compilation well structured and an integral part of the language,
> grammatically integrated with Go. It's not particularly hard to do, but it
> does require willingness to accept that Go is not yet cast in stone. The
> same applies to conditional evaluation of arguments.

Write your test code as

if (debug) {
fmt.Println("Debugging")
}

Have two files in your package as follows:

release.go:

package p
const debug = false

debug.go:

package p
const debug = true

Write your Makefile like this:

GOFILES=... $(RELEASE_OR_DEBUG)

ifeq ($(DEBUG),)
RELEASE_OR_DEBUG = release.go
else
RELEASE_OR_DEBUG = debug.go
endif

Or something along those lines.

Ian

Marc-Antoine Ruel

unread,
Aug 16, 2011, 12:52:38 PM8/16/11
to Ian Lance Taylor, Morgaine, golan...@googlegroups.com
That was exactly what I was about to recommend. It's the best solution since it clearly marks what is "debug" and what is not, while keeping the conditional structure where it should be, in the build infrastructure.

M-A

Gerard

unread,
Aug 25, 2011, 1:04:35 AM8/25/11
to golang-nuts
On the other hand: If you make "debug" a boolean variabele, then you
can also use a command line parameter such al "-d", to use the
debugging.

I like the one line "debug printing" method in the code. It is very
clean.

Gerard

mar...@bottomline-group.com

unread,
Apr 16, 2014, 9:46:15 AM4/16/14
to golan...@googlegroups.com
I found a way to avoid the if statement:

Seems more efficient, but I could be wrong.

package main

import "log"

const debug nodebugging = true // debugging = true

type debugging bool
type nodebugging bool

func (d debugging) Printf(format string, args ...interface{}) {
                log.Printf(format, args...)
}

func (d nodebugging) Printf(format string, args ...interface{}) {
}

func main() {
        debug.Printf("foo %d %.2f", 42, 12.7)
}

Jim Robinson

unread,
Apr 17, 2014, 10:17:29 PM4/17/14
to golan...@googlegroups.com
Awhile ago Jochen Voss posted http://www.seehuhn.de/pages/trace/I liked the basic idea of the listener, and also added logic that I thought would defer expensive parameter evaluation: https://github.com/jimrobinson/trace/.

The idea is that you can write something the following:
traceFn, traceT := trace.M("github.com/jimrobinson/httpclient", trace.Trace);
...
if traceT {
    trace.T(traceFn, "http request: %v", req)
}
So you have a fairly light-weight call to pick up traceFn and traceT, which
capture active listeners for trace level debugging and a flag indicating that
listeners are in fact available, and then you guard your trace evaluation with
the traceT boolean.

Jim Robinson

unread,
Apr 18, 2014, 6:54:19 PM4/18/14
to golan...@googlegroups.com, mar...@bottomline-group.com
But the arguments are still evaluated in this situation.  So while you may
be dropping the arguments on the floor (when 'debug' is an instance of
nodebugging), but the compiler still needs to evaluate the arguments before
the function is called.

mar...@bottomline-group.com

unread,
Apr 23, 2014, 2:29:55 AM4/23/14
to golan...@googlegroups.com, mar...@bottomline-group.com
Would it help to wrap the arguments in an anonymous function, like

It seems like the trade-off is between evaluating an "if" statement and passing arguments that are dropped.
For readability, avoiding "if" is better, but what are the cost of calling an empty function?

Also, you can not switch on debug logging at runtime, but for my case, this is not a problem.

Op zaterdag 19 april 2014 00:54:19 UTC+2 schreef Jim Robinson:
Reply all
Reply to author
Forward
0 new messages