A certain company has contracted with a consultant (both of which
I will leave anonymous) to upgrade a bunch of not-quite-ANSI C code
to the ANSI standard. This is primarily for staffing reasons, as
all of Company's C programmers are busy with other projects.
Some of this code contains multi-line macro definitions that are
wrapped in a do ... while(0), as recommended in the C FAQ, question 10.4:
#define MACRO(arg1, arg2) do { \
blah1; \
blah2; \
/* ... */ \
} while(0)
...
MACRO(x, y);
...
MACRO(z, y);
...
etc.
Consultant has changed these macros so they read like this:
#define MACRO(arg1, arg2) \
int macro_do_once = 0;\
do { \
blah1; \
blah2; \
/* ... */ \
} while(macro_do_once)
...
{MACRO(x, y);}
...
{MACRO(z, y);}
...
which sort of defeats the purpose of wrapping the code
in the do ... while to begin with. When queryed about this
practice, Consultant has claimed that the "latest version" of
the standard prohibits the do ... while(0) construct. Company's
staff considers this claim to be dubious, but none of them has a
copy of the "latest version" of the standard (with all the latest
corrections, interpretations, etc.) to quote back at Consultant.
My question, therefore, is: is there anything in either the current standards
or working drafts of future versions of either the C or C++ standards
that would depreciate, much less prohibit, this widespread practice?
If you have special expertise to answer this question (member of one
of the committees, etc.), I would appreciate it if you could include
a brief summary of such expertise in your reply.
P.S.: If it matters, some of the macros are actually single-statement
conditionals, such as:
#define MACRO1(w, x, y, z)\
do\
if (condition(x, y, z))\
w=func(y);\
else\
w=func(z);\
while (0)
which was converted to:
#define MACRO1(w, x, y, z)\
int do_macro_once = 0;\
do\
if (condition(x, y, z))\
w=func(y);\
else\
w=func(z);\
while (do_macro_once)
--
Dave Wallace (wal...@netcom.com)
It is quite humbling to realize that the storage occupied by the longest
line from a typical Usenet posting is sufficient to provide a state space
so vast that all the computation power in the world can not conquer it.
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std...@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++...@ncar.ucar.edu
]
As you have described the problem,the company is clueless, and the
consultant is bullshitting. It is true that there were once a few
compilers that objected to code like:
#define MACRO(arg1, arg2) { \
blah1; \
blah2; \
/* ... */ \
}
but all reasonable ANSI C compilers will now accept this. Not
merely is 'do { ... } while (0);' merely a verbose way of saying
'{ ... }', but creating an unnecessary variable is just plain
stupid.
HOWEVER, this does assume that you have described the situation
precisely. There are circumstances under which contortions like
the one specified by the consultant are necessary. But not just
in this simple case.
Nick Maclaren,
University of Cambridge Computer Laboratory,
New Museums Site, Pembroke Street, Cambridge CB2 3QG, England.
Email: nm...@cam.ac.uk
Tel.: +44 1223 334761 Fax: +44 1223 334679
I agree he is bullshitting, but:
>It is true that there were once a few
>compilers that objected to code like:
>
>#define MACRO(arg1, arg2) { \
> blah1; \
> blah2; \
> /* ... */ \
> }
>
>but all reasonable ANSI C compilers will now accept this. Not
>merely is 'do { ... } while (0);' merely a verbose way of saying
>'{ ... }', ...
You did, of course, consider the construct:
---
if (foo)
MACRO(x, y);
else
bar;
---
before stating categorically that 'do { ... } while (0)' is merely a
verbose way of saying '{ ... }' ?
IMO, the use of it suggests the original writer of the code was more
familiar with standard C practices than either the company or the
consultant.
--
---------------------------------------------------------------------------
Tim Hollebeek | Disclaimer :=> Everything above is a true statement,
Electron Psychologist | for sufficiently false values of true.
Princeton University | email: t...@wfn-shop.princeton.edu
Well, I think the purpose of the 'do { ... } while(0)' (note no
semicolon in macro expansion) is so that you can use MACRO wherever
you could use a function-returning-void. If you just have the braces,
then in 'if (expr) MACRO(a, b); else voidfunc(d, e);' the first
semicolon is a syntax error, while the second is required.
>Consultant has changed these macros so they read like this:
[...]
>Consultant has claimed that the "latest version" of
>the standard prohibits the do ... while(0) construct.
If this is an accurate description of the situation, then Consultant
should be terminated with extreme prejudice, and explicitly named on
comp.*.c to ensure no-one ever employs them again.
As Nick McLaren points out, we need to be sure that the actual situation
isn't more complex.
> Company's
>staff considers this claim to be dubious, but none of them has a
>copy of the "latest version" of the standard (with all the latest
>corrections, interpretations, etc.) to quote back at Consultant.
I have.
Try asking Consultant what the "latest version" of the Standard is. I'll
bet they get it wrong.
>My question, therefore, is: is there anything in either the current standards
>or working drafts of future versions of either the C or C++ standards
>that would depreciate, much less prohibit, this widespread practice?
Speaking only for the C Standard (C++ standardisation, IMO, abandoned
reality long ago), the answer is a definite:
N N OOO
NN N O O
N N N O O
N NN O O
N N OOO
The idea hasn't even been *suggested*.
The principle of orthogonality also makes me believe it would never
happen. We wouldn't depreciate "do...while", and we wouldn't introduce a
special case (while clause is zero) where none is needed.
>If you have special expertise to answer this question (member of one
>of the committees, etc.), I would appreciate it if you could include
>a brief summary of such expertise in your reply.
Certainly:
member of BSI IST/5/-/14;
member of the UK delegation to ISO/IEC JTC1/SC22/WG14, including in one
case being Principle UK Expert;
author of approximately one-third of all Defect Reports filed against
the current Standard;
author of various changes to the next revision, some of which have now
been accepted by WG14.
[Does Consultant even know who the two named bodies are ?]
--
Clive D.W. Feather | You should reply to <cl...@demon.net> (the
Associate Director | Reply-To: header has been set to this). This
Demon Internet Limited | account is on my laptop and is only used when I
| travel; messages to it may not be seen quickly.
The consultant definitely ought to be shot for stupidity though.
--
Duncan Booth dun...@rcp.co.uk
int month(char *p){return(124864/((p[0]+p[1]-p[2]&0x1f)+1)%12)["\5\x8\3"
"\6\7\xb\1\x9\xa\2\0\4"];} // Who said my code was obscure?
>"David E. Wallace" <wal...@netcom.com> writes
>
>>My question, therefore, is: is there anything in either the current standards
>>or working drafts of future versions of either the C or C++ standards
>>that would depreciate, much less prohibit, this widespread practice?
>
>Speaking only for the C Standard (C++ standardisation, IMO, abandoned
>reality long ago), the answer is a definite:
>
> N N OOO
> NN N O O
> N N N O O
> N NN O O
> N N OOO
>
>The idea hasn't even been *suggested*.
The same is true for the C++ standard.
--
Fergus Henderson <f...@cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger f...@128.250.37.3 | -- the last words of T. S. Garp.
> which sort of defeats the purpose of wrapping the code
> in the do ... while to begin with. When queryed about this
> practice, Consultant has claimed that the "latest version" of
> the standard prohibits the do ... while(0) construct. Company's
> staff considers this claim to be dubious, but none of them has a
> copy of the "latest version" of the standard (with all the latest
> corrections, interpretations, etc.) to quote back at Consultant.
I can think of only a single thing which this might accomplish, that
being elimination of a compiler warning to the effect "constant
expression in while() is always false". I have heard of some
companies adopting coding standards that say programs must compile
without any warnings with compiler set to highest diagnostic level.
--
Richard Krehbiel, Kastle Systems, Arlington, VA, USA
ri...@kastle.com (work) or ri...@mnsinc.com (personal)
Actually, I said that 'do { ... } while (0);' is merely a verbose
way of saying '{ ... }'! Why can't you write the following code?
if (foo)
MACRO(x, y)
else
bar;
The point is that the user of the macro has to know that it is a
macro generating a partial statement and not one generating a
void function call. For example, the original definition will not
work as the first operand in a comma operator or if prefixed by a
'(void)' cast. Hence there is no extra difficulty in defining the
macro's specification to be use without a trailing semicolon.
While most compilers will diagnose errors, a few will simply
generate meaningless code because the resulting construction is so
bizarre that the compiler writer may not have thought of checking
for it. Yes, seriously. I got very badly burnt by a similar
problem when porting a largish (20K line) program with a severe
case of macroitis.
|> IMO, the use of it suggests the original writer of the code was more
|> familiar with standard C practices than either the company or the
|> consultant.
That is probably true, but he assuredly wasn't familiar with GOOD
commercial C practices! One of the very first rules is to avoid
introducing traps for the programmer into standard headers, and I
am afraid that this counts as a trap. But I agree that it is a
fairly common convention.
>Not merely is 'do { ... } while (0);' merely a verbose way of saying
>'{ ... }',
Note that the original didn't have a semicolon at the end. Surely the
purpose of the "while" version is so that you can say
if(...) MACRO(a,b); else ...
Otherwise you would have to know that the macro expanded to {...} in
order to avoid putting a semicolon after it when used in an "if".
>but creating an unnecessary variable is just plain stupid.
Presumably the variable was to prevent a compiler warning about a
constant condition.
-- Richard
--
Margaret Thatcher, Bill Gates, Canter & Siegel, Ian Paisley, Rupert Murdoch
|> In article <wallaceD...@netcom.com>,
|> David E. Wallace <wal...@netcom.com> wrote:
|> >A certain company has contracted with a consultant (both of which
|> >I will leave anonymous) to upgrade a bunch of not-quite-ANSI C code
|> >to the ANSI standard. This is primarily for staffing reasons, as
|> >all of Company's C programmers are busy with other projects.
|> >Some of this code contains multi-line macro definitions that are
|> >wrapped in a do ... while(0), as recommended in the C FAQ, question 10.4:
|> >
|> >#define MACRO(arg1, arg2) do { \
|> > blah1; \
|> > blah2; \
|> > /* ... */ \
|> > } while(0)
|> >
|> >Consultant has changed these macros so they read like this:
|> >
|> >#define MACRO(arg1, arg2) \
|> >int macro_do_once = 0;\
|> >do { \
|> > blah1; \
|> > blah2; \
|> > /* ... */ \
|> > } while(macro_do_once)
Get rid of the consultant, and quick. This change will probably break
code. Even the code that still compiles may have subtle errors. Note
that the original version expands to a single C/C++ statement *minus*
the semi-colon. So the code:
if ( x )
MACRO( 1 , 2 ) ;
else
blah() ;
is legal, and will work as expected. Now try it with the revised
version. Unless you have a very strange C/C++ compiler, it should
complain about an else with no if. If there is no else, then the
consultant's solution is even worse, since there is no compiler error
(in C++, at least), but only the declaration of the int is in the if,
not what was previously the body of the for loop. (In C, the macro
simply cannot be used except at the beginning of a block. And of
course in neither language can it be used multiple times in the same
block.)
With regards to the request in the original posting to justify the
legality of the original form from the standard, my point of view would
be that the burden of proof is on the other foot. Why is this illegal,
according to the consultant? The resulting loop is certainly conform to
the *grammar* of the do statement, so there must be some constraint
which forbids it. Ask the consultant to show you that constraint in the
standard.
|> As you have described the problem,the company is clueless, and the
|> consultant is bullshitting. It is true that there were once a few
|> compilers that objected to code like:
|> #define MACRO(arg1, arg2) { \
|> blah1; \
|> blah2; \
|> /* ... */ \
|> }
|> but all reasonable ANSI C compilers will now accept this.
In what context. All compilers I've every used would accept anything
that they could tokenize in a macro definition. When it comes to using
the definition, however, the above has the same problem as the
consultants solution: follow it with a semi-colon, and then an else, and
what happens. It is particularly worth considering the following case:
if ( x1 )
if ( x2 )
MACRO( 1 , 2 ) ;
else
something() ;
The use of the above macro will not result in a compilation error here.
It will result in the compiler associating the else with the first if,
however, which is probably not what was wanted.
|> Not
|> merely is 'do { ... } while (0);' merely a verbose way of saying
|> '{ ... }', but creating an unnecessary variable is just plain
|> stupid.
Saying 'do { ... } while ( 0 ) ;' is perhaps just a verbose way of
saying '{ ... }'. Not putting the trailing semicolon in the macro,
however, makes a significant difference in the usage; the macro can now
be used with a trailing semicolon as a *single* statement (not two).
--
James Kanze Tel.: (+33) 88 14 49 00 email: ka...@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils, études et réalisations en logiciel orienté objet --
-- A la recherche d'une activité dans une region francophone
Note do { ... } while( 0 ) and { ... } are only identical if they body
of code does not contain break or continue. [Now theres a trick for the
obfuscated C competition.]
--
Stephen Baynes bay...@ukpsshp1.serigate.philips.nl
Philips Semiconductors Ltd
Southampton My views are my own.
United Kingdom
Are you using ISO8859-1? Do you see © as copyright, ÷ as division and ½ as 1/2?
If compilers (adhering to the silly prohibition) were to reject
do ; while (0);
this would require a special addition to the described semantics
of the do/while construct, such as "The condition is not allowed
to be a compile-time constant expression evaluating to false."
Otherwise, it would allow devious programmers to simply write
"0|0" instead of "0".
David E. Wallace <wal...@netcom.com> wrote in article
<wallaceD...@netcom.com>...
[ snip material regarding #define ... do { ... } while (0) ]
> Consultant has changed these macros so they read like this:
>
> #define MACRO(arg1, arg2) \
> int macro_do_once = 0;\
> do { \
> blah1; \
> blah2; \
> /* ... */ \
> } while(macro_do_once)
> ...
> {MACRO(x, y);}
> ...
> {MACRO(z, y);}
> ...
>
> which sort of defeats the purpose of wrapping the code
> in the do ... while to begin with. When queryed about this
> practice, Consultant has claimed that the "latest version" of
> the standard prohibits the do ... while(0) construct.
I would say it _completely_ defeats the purpose of wrapping
the code. That purpose was to make the macro useful in
contexts such as
if (...)
MACRO(arg1,arg2);
else
SomethingElse;
where an unwrapped version would cause the else clause to
associate differently than if the macro were a single statement.
There is another, equally serious problem with the rewritten
macro: It cannot be used twice in the same scope because
it would redefine macro_do_once. (And many folks would
consider it bad form to introduce secret variables or names
into the scope as a side-effect of using the macro.)
I think your smoke-blowing consultant should be supervised
very carefully, probably full time by somebody that knows the
language. You might want to reevaluate the economics here.
> Actually, I said that 'do { ... } while (0);' is merely a verbose
> way of saying '{ ... }'! Why can't you write the following code?
>
> if (foo)
> MACRO(x, y)
> else
> bar;
>
> The point is that the user of the macro has to know that it is a
> macro generating a partial statement and not one generating a
> void function call. For example, the original definition will not
> work as the first operand in a comma operator or if prefixed by a
> '(void)' cast. Hence there is no extra difficulty in defining the
> macro's specification to be use without a trailing semicolon.
There are several reasons that I can come up with that having to add
the trailing semicolon is a Very Good Thing(tm).
1) Macro's look, and usually act, like function calls. It looks very
odd to see a line with a function (or macro) call without a
trailing semicolon. It's a case of "if it looks like a duck,
smells like a duck, and walks like a duck, it must be a duck..."
2) Maintainability. Sometime in the future, it may become desirable
to change the MACRO(x, y) call into a real function. Checking to
make sure that every place it is used is syntactically correct is
just extra work that can easily be avoided with a little
fore-thought. You may also find it desirable to replace a function
with a macro. In this case, you have to either write the macro
to require a semicolon, or check every place the function to make
the appropriate modifications. If you have to conceivably write
one macro to require a semicolon, you might as well write them
all that way to avoid confusion.
3) Consistancy. Everybody knows that all function calls end with a
semicolon. If you have several different macros, it may be
difficult to remember which ones require a trailing semicolon and
which don't, as not all macros are compound statements. There is
bound to be a few extra semicolons if you write some macros so they
don't require semicolons. That, in conjunction with the several
situations where an extra semicolon wouldn't affect anything, can
add up to a lot of confusion (but all I did was add an "else"
clause, and now the compiler complains about a syntax error... It
looks exactly right, it must be a compiler bug). You might argue
that statements like if() and for() also look like function calls,
but these exceptions are so widely known that they don't cause
confusion with function calls :-)
4) Editors that handle indenting for you are confused by the lack of a
semicolon at the end of each function (or function look-alike).
The nifty program 'indent' falls into this category. People who
pipe programs through 'indent' before looking at them (so the style
looks somewhat familiar) would get a very confused looking source
file.
So your "no extra difficulty" can add up to a lot of avoidable grief.
--
Zach Heilig (za...@blizzard.gaffaneys.com) | ALL unsolicited commercial email
Support bacteria -- it's the | is unwelcome. I avoid dealing
only culture some people have! | with companies that email ads.
But that is PRECISELY what you are proposing! The original macro
is an unterminated statement, with the syntax of a function call.
They are not the same (as we agreed in a snipped section).
>What big trap? Personally, I think the original programmer did it The
>Right Way. ...
This is an actual story from real life, or at least academia.
A certain 20K program started life in one language, and was converted
to K&R C for UNIX by a macro freak, and included exactly the above
construction. Its macro calls were typically nested 10-20 deep, and
were virtually impossible to follow. I then converted it to ANSI C
for MVS, trying to make as few manual source changes as possible (for
obvious software engineering reasons).
One of the changes that I needed to make involved using what I THOUGHT
was a function call with a comma operator. About three levels down in
the expansions, it separated a partial statement from an expression
with a comma. The compiler I was using has extremely thorough error
messages but, unfortunately, that particular case was so bizarre that
it had not been checked for and fell through a switch statement.
The result was that the program compiled and linked correctly, and
went bizarrely wrong. As the relevant code was part of its data
management, you will agree that I was lucky to find the problem after
only a couple of days :-)
Since then, I have discovered many other C compilers with the property
that they fail to detect at least some cases of sufficiently bizarre
not-even-remotely-C code. The root cause of this is, of course, the
fact that the C syntax and constraints cannot be described in a nice,
clean, rigorous terminology (like BNF or even Van Wijngarden grammar).
So that is why I regard almost-but-not-quite void function macros as
utterly and irredemiably loathesome, and a trap waiting to be sprung.
|> JKU6:
|> JKU6: if ( x1 )
|> JKU6: if ( x2 )
|> JKU6: MACRO( 1 , 2 ) ;
|> JKU6: else
|> JKU6: something() ;
|> JKU6:
|> JKU6: The use of the above macro will not result in a compilation error here.
|> JKU6: It will result in the compiler associating the else with the first if,
|> JKU6: however, which is probably not what was wanted.
|> How is this legal?
By getting a compiler that is as tired and bleery eyed as the poster:-).
You're right, of course. The empty statement generated by the extra
semicolon terminates both if's, and not just the inner if.
Because Thou Shalt Not Use The Preprocessor To Change The Syntax Of The
Language. If MACRO(x,y) were a function call, the semicolon would be
required.
> The point is that the user of the macro has to know that it is a
> macro generating a partial statement and not one generating a
> void function call.
And the point of the do-while(0) construct is that the user shouldn't
have to know that.
> For example, the original definition will not
> work as the first operand in a comma operator or if prefixed by a
> '(void)' cast.
True.
> Hence there is no extra difficulty in defining the
> macro's specification to be use without a trailing semicolon.
Personally, I'd prefer being able to treat the construct as if it were a
simple expression *statement*, even if I couldn't use it as a general
expression.
> |> IMO, the use of it suggests the original writer of the code was more
> |> familiar with standard C practices than either the company or the
> |> consultant.
>
> That is probably true, but he assuredly wasn't familiar with GOOD
> commercial C practices! One of the very first rules is to avoid
> introducing traps for the programmer into standard headers, and I
> am afraid that this counts as a trap. But I agree that it is a
> fairly common convention.
What big trap? Personally, I think the original programmer did it The
Right Way. The braindead consultant's change did not solve any problem
with the code as far as I can tell. It still uses the do-while
construct, but replaces the constant with a variable. Not only didn't it
solve any problem (what exactly was the consultant claiming to be
illegal now in the first place? A constant controlling experssion in a
do loop?), but this prevents the Macro from being used in C unless it's
in the first statement in the block, since you can't mix declarations
among statements in C.
--
Chris Volpe Phone: (518) 387-7766
GE Corporate R&D Fax: (518) 387-6560
PO Box 8 Email: vol...@crd.ge.com
Schenectady, NY 12301 Web: http://www.crd.ge.com/~volpecr
No, the above is a syntax error, pure and simple. There is no way for the
else to be associated with the outer if, unless the inner if is placed in
a separate statement block from the else. This is clearly not the case.
What you have, essentially is:
if (x1)
if (x2) {
/* blah */
}; else
something();
The grammar of C cannot generate a string in which a closing brace
is followed by a semicolon and an else keyword. The grammar production
for an if statement with else is:
selection-statement:
if ( expression ) statement else statement
No string that is generated by the ``statement'' syntactic unit
has a }; suffix. In particular, a compount-statement ends with
a } token. No semicolon in sight!
>|> merely is 'do { ... } while (0);' merely a verbose way of saying
>|> '{ ... }', but creating an unnecessary variable is just plain
>|> stupid.
>
>Saying 'do { ... } while ( 0 ) ;' is perhaps just a verbose way of
>saying '{ ... }'. Not putting the trailing semicolon in the macro,
>however, makes a significant difference in the usage; the macro can now
>be used with a trailing semicolon as a *single* statement (not two).
The macro now _must_ be used with a trailing semicolon. If you had
looked at the grammar production for a do/while construct, you would have
noticed that the semicolon is mandatory for do/while. All strings
generated by the do/while grammar production have a ; suffix. Hence
by writing the semicolon, the user unknowingly makes the do/while whole!
--
They are fools, because many warnings are generated for purely stylisic
reasons. One compiler set to the higest diagnostic level may report something
different from another compiler set to its highest level.
I try to follow a relaxed variant of the rule, but sometimes GCC complains
about some completely ridiculous thing, because its analysis does not see
deeply enough.
For example, in one function I initialize a variable X for the first and only
time inside both cases of a switch(). The switch is made on the contents of an
enum variable Y that is defined to hold only two values. Yet the compiler
complains that I may be usign variable X uninitialized inside the function.
That is bollocks, because in all my assignments to Y elsewhere in the project,
the enum values are carefully used. True, the compiler is right, strictly
speaking. But what should I do about it? Introduce a default: case which
forces some sort of panic? But I only assign the enum values to Y, so why
should I?
The same compiler will not complain if I assign an arbitrary integer to an
enum (yes, I know this is allowed, but it _IS_ a candidate for a stylistic
warning). Such a warning would be much more useful in catching an error
with respect to the contents of Y.
The point is that the heuristics used by a compiler in generating warnings
are useful most of the time, but they can be both annoying and unhelpful.
--
So it comes down to quality of implementation again. But in that case the
consultant and the compiler both fail. A good compiler will have the ability
to suppress the warning about the constant condition, and in this case it
should also be able to tell quite easily that the condition is constant even
though a variable it used to (slightly) mask it.
A good consultant (IMHO) would (if the compiler wan't up to it) have fixed the
warning by rewriting the code in a function rather a macro---speed is
presumably not an issue here or why is the consultant trying to get the
compiler to generate unnecessary loops testing a variable that is always 0?
Besides which the compiler may well be able to inline the function call if it
decides it is appropriate.
This has always been a bugbear of mine, and one that I now get around
by the simple process of *always* using braces in if/else/for/while
constructs, even when only a single statement follows the construct.
The number of bugs I have had to fix when maintaining code which would
have been eliminated at source by two braces are too many to count.
The extra braces take but a fraction of a second to type, don't impede
readability to any competent programmer and completely eliminate any
chance of this sort of problem. The classic case is, of course:
if (x1)
if (x2)
do_something();
else
do_something_else();
which doesn't do what the indentation implies. Writing it as:
if (x1)
{
if (x2)
{
do_something();
}
}
else
{
do_something_else();
}
makes everything absolutely clear and unequivocal (and right). The other classic is where an extra line of code has been added to an already complete if clause without due thought: how many times have I seen this...
if (x1)
do_something();
do_something_else();
Please, put the extra braces in. The number of simple bugs that two
keystrokes can save is worth it - OK, so your source code grows by a
few lines (although you can snuggle the braces if that is your
preferred style) and make the source code a few bytes longer, but they
help prevent bugs, and that is what all professional programmers
should be trying to acheive.
Cheers,
Mike.
--
Mike Ellis, Research and Development Engineer, BBC R & D
mike....@rd.bbc.co.uk DDI:(+44) 1737 836663 Fax:(+44) 1737 836667
BBC R&D, Kingswood Warren, Woodland Way, Tadworth, Surrey KT20 6NP, UK
Any opinions expressed are my own and may not reflect those of the BBC
Because it's more in keeping with C syntax to use a semicolon. Either
one regards the macro as a function call, which it looks like, so the
semicolon would be required, or one regards the macro as new syntax, in
which case the semicolon is still preferred.
>In article <4utn9g$n...@cnn.Princeton.EDU>, t...@franck.Princeton.EDU (Tim Hollebeek) writes:
>The point is that the user of the macro has to know that it is a
>macro generating a partial statement and not one generating a
>void function call. For example, the original definition will not
>work as the first operand in a comma operator or if prefixed by a
>'(void)' cast.
If it is used in such a way, a diagnostic will be required, making it
obvious to the programmer. Most of the time the user *can* treat it as
a function.
> Hence there is no extra difficulty in defining the
>macro's specification to be use without a trailing semicolon.
Yes there is. It results in a greater number of errors than the
widely-preferred construct does. Neither form is fatally flawed, but
one needs to know which form is being used, and invocations of the
do...while(0) form are more pleasing to the eye.
>While most compilers will diagnose errors, a few will simply
>generate meaningless code because the resulting construction is so
>bizarre that the compiler writer may not have thought of checking
>for it.
Then they're crap compilers. Good C compilers are widely available;
more than one of them is free.
>That is probably true, but he assuredly wasn't familiar with GOOD
>commercial C practices! One of the very first rules is to avoid
>introducing traps for the programmer into standard headers, and I
>am afraid that this counts as a trap. But I agree that it is a
>fairly common convention.
Neither form is a serious trap. Incorrect code can't be generated by
simple misuse of either. However, in the {...} form, if a usage looks
like "MACRO();" then it is legal and won't generate a diagnostic, but
will suddenly fail if it is moved to be the true branch of an if-else
statement. The do...while(0) form has no such pitfall -- "MACRO()"
usage will generate a diagnostic wherever it's used.
-zefram
In this case, you shouldn't put in a default: just to satisfy the
compiler. Instead, you should put one in because it can save you
down the road. I wish I had a penny for every time I've heard
someone say "I don't need to check for that, because it can't happen"
and it came back to bite them in the ass. I'd probably have close to
a dollar! (All right, make it a dollar every time :-)
-Jon
| Nick Maclaren wrote:
| >
| > In article <4utn9g$n...@cnn.Princeton.EDU>, t...@franck.Princeton.EDU (Tim Hollebeek) writes:
| > |>
| > |> You did, of course, consider the construct:
| > |>
| > |> ---
| > |> if (foo)
| > |> MACRO(x, y);
| > |> else
| > |> bar;
| > |> ---
| > |>
| > |> before stating categorically that 'do { ... } while (0)' is merely a
| > |> verbose way of saying '{ ... }' ?
| >
| > Actually, I said that 'do { ... } while (0);' is merely a verbose
| > way of saying '{ ... }'! Why can't you write the following code?
| >
| > if (foo)
| > MACRO(x, y)
| > else
| > bar;
| >
|
| Because Thou Shalt Not Use The Preprocessor To Change The Syntax Of The
| Language. If MACRO(x,y) were a function call, the semicolon would be
| required.
This is bad advice, IMHO, even though written in capital letters. (I
find that people who start speaking in capitals are usually in the first
stages of psychosis. Subsequent symptoms are talking about themseleves
by name, and using upper case only. NOBODY CAN STOP DR DOOM!)
:-)
However, the point here is that although a macro might *look* like a
function invocation, it is not. There are perfectly sensible uses of
macros that expand to things other than expressions; these cannot be
wrapped up to look like functions even if you want to.
The rule I follow, and which seems the only one that can work in
all cases, is that a macro should expand to *some* valid syntactic
construct of the language. Whichever one it is should be documented.
From then on, it's caveat emptor.
For example,
#define CALL(f) {if ((e = (f)) != NULL) goto finish;}
is a statement;
#define HACK(n) struct {int a; double b; char c [n];}
is a type-specifier; and
#define HEADER_MEMBERS \
draw_type type; \
size_t size; \
os_box bbox;
is a field-list (according to H+S, anyway).
I have always been surprised that the advice to use
do ... while (0)
is in the FAQ for this newsgroup. It is a purely syntactic trick that
allows a misguided person to get away with a spurious semicolon in one
particular situation, without actually addressing the issue.
If it was the right thing to do, it would work in comma expressions,
as function arguments, etc. It isn't, it doesn't.
/|
(_|/
/|
(_/
I disagree. If you put in a default: case, the compiler can't sanely detect
that you aren't handling a new value, if you increase the set of values in the
enum. When I switch on an enum, gcc -Wall warns me if I don't handle all the
values. I'd rather have
case FOO_MAX: break;
(where FOO_MAX exists only for purposes of iterating to (i < FOO_MAX)) than
introduce a default case which will catch any new value, preventing the
compiler from detecting my mistake.
I use default: only when the intent is to disregard or handle identically any
case I haven't thought of.
I just initialize variables which the compiler warns for like this; if it's
too stupid to statically discover that I have a state machine that is
incapable of using before initializing, I just lose one possible warning.
-s
--
Peter Seebach - se...@solon.com - Copyright 1996 - http://www.solon.com/~seebs
Unix/C Wizard - send mail for help, or send money for consulting!
The *other* C FAQ, the hacker FAQ, et al. See web page above.
Unsolicited email (junk mail and ads) is unwelcome, and will be billed for.
No it's not. The definition I endorse results in usage that resembles
legal C. The macro definition you endorse results in usage that
resembles illegal non-C. It is the usage that is significant.
> The original macro
> is an unterminated statement, with the syntax of a function call.
> They are not the same (as we agreed in a snipped section).
True and irrelevant.
[...snip...]
> One of the changes that I needed to make involved using what I THOUGHT
> was a function call with a comma operator.
Yeah, I hear ya, but that's what we have capitalization style
conventions for. Having the macro defined as a block statement would not
have made this situation easier.
Suddenly, I became plural.
> |
> | Because Thou Shalt Not Use The Preprocessor To Change The Syntax Of The
> | Language. If MACRO(x,y) were a function call, the semicolon would be
> | required.
>
> This is bad advice, IMHO, even though written in capital letters.
Then, I take it you endorse the practice of such things as
#define Begin {
#define End }
#define Record struct
etc, to ease the transition for Pascal users?
(I
> find that people who start speaking in capitals are usually in the first
> stages of psychosis.
Hmmm, first I'm diagnosed with multiple personality disorder, and now
psychosis. Thanks, doc.
Anyway...
>
> However, the point here is that although a macro might *look* like a
> function invocation, it is not.
We know that.
> There are perfectly sensible uses of
> macros that expand to things other than expressions; these cannot be
> wrapped up to look like functions even if you want to.
They can be wrapped up to look like functions in some contexts, which is
better than wrapping it up to look like a function followed by a syntax
error.
>
> The rule I follow, and which seems the only one that can work in
> all cases, is that a macro should expand to *some* valid syntactic
> construct of the language. Whichever one it is should be documented.
> From then on, it's caveat emptor.
>
> For example,
>
> #define CALL(f) {if ((e = (f)) != NULL) goto finish;}
>
> is a statement;
No it's not. A statement, with its terminating semicolon, is allowed to
appear before an "else" clause. That's why the above definition fails,
and the do-while construct works.
[snip]
>
> I have always been surprised that the advice to use
>
> do ... while (0)
>
> is in the FAQ for this newsgroup. It is a
> allows a misguided person
In your opinion.
> to get away with a spurious semicolon in one
It's not spurious, it's necessary. Statements must be terminated with a
semicolon, except when they are enclosed in braces. Since the macro
hides the braces, it forces a syntax change at the invocation of the
macro.
> particular situation, without actually addressing the issue.
Which is?
>
> If it was the right thing to do, it would work in comma expressions,
Non sequitur. That's like saying if declaring a variable with the
"register" qualifier were the right thing to do, it would work in
address-of expressions (i.e. ampersand operator, or array type
conversion).
> as function arguments, etc. It isn't, it doesn't.
>
--
WRONG!!! That is one of the MOST useful things to do with the macro
preprocessor. True, the changes should be well considered and well
documented, but changing the syntax can be very beneficial.
The downside is that programmers new to the altered syntax need an
hour or so to pick up the differences and perhaps a week or so to get
used to the changes, but the utility can far outweigh that cost.
To give an example, the standard that I use defines the following three
macros...
#define nil
#define when(x) if(!(x)) nil; else
#define unless(x) if(x) nil; else
these have the following advantages. First the "nil" macro is very
useful in places where only a null statement (;) is needed. It explicitly
makes it clear that there is an omitted statement. For example...
for (char * p = q; *p; ++p)
nil;
as opposed to...
for (char *p = q; *p; ++p);
or
for (char *p = q; *p; ++p)
;
either of which frequently leads to readability problems.
Now, consider when(x) and unless(x). Knuth did a study and found
that 80% of all if statements do not have an else clause (in FORTRAN
source code, but I see no evidence it is any different in C and C++).
Further, many programmers (if not most) have problems understanding
negation.
So the first advantage of when(x) and unless(x) is that they
are used if and only if there is to be no else clause present. This can
prevent many dangling else problems and clearly signals the intent of
the code. It can also reduce the indentation
level, depending on your indentation standard. The second advantage,
is that unless(x) has a built-in semantic negation which speakers of
the English language understand much better than explicit symbolic
negation. Both of these reduce coding errors.
My company has written many, many source programs using these
standards and have not found a programmer unable to pick up the
language "additions" in a few minutes.
So, the use of macros to change the language can be a VERY GOOD
THING!
The absurd idea that we want exactly one way to a accomplish
something in a programming language is due to Wirth and one of
the worst ideas to ever to pushed on the computer industry. We
don't speak Esperanto and for the same reason English offers many
different ways of saying the same thing, each with its own slight
semantic difference in meaning. This richness is to be coveted in
programming languages as well as natural languages.
That doesn't mean that we need ten different if statements which
are semantically the same except for name, but if there is a semantic
difference likely to be used, then it improves both the conciseness
and readability of the resulting source code. There is, of course, a
careful balancing act between too many variants and too little
semantic richness, but that is language design issue.
The trivial increase in learning time is well worth the cost. It would
be best, of course, for variants like when(x) and unless(x) to make
it into the standard, but until then, altering the language syntax using
macros is most assurdly a reasonable thing to do. Even if not to
everyone's taste.
: They are fools, because many warnings are generated for purely stylisic
: reasons. One compiler set to the higest diagnostic level may report something
: different from another compiler set to its highest level.
I think that's a bit harsh; I've yet to find a warning I can't
eliminate without 'unimproving' the source. E.g. removing warnings
has always made my source 'better'. Of course, I'm using gcc, which
luckily doesn't generate many warnings that are hard to avoid in practice.
: I try to follow a relaxed variant of the rule, but sometimes GCC complains
: about some completely ridiculous thing, because its analysis does not see
: deeply enough.
: For example, in one function I initialize a variable X for the first and only
: time inside both cases of a switch(). The switch is made on the contents of an
: enum variable Y that is defined to hold only two values. Yet the compiler
: complains that I may be usign variable X uninitialized inside the function.
: That is bollocks, because in all my assignments to Y elsewhere in the project,
: the enum values are carefully used. True, the compiler is right, strictly
: speaking. But what should I do about it? Introduce a default: case which
: forces some sort of panic? But I only assign the enum values to Y, so why
: should I?
Well, check it at runtime then (since you're sure it MUST be true).
#ifdef DEBUG
default:
fatal("Value of Y was not a valid enum value\n");
#endif
: The same compiler will not complain if I assign an arbitrary integer to an
: enum (yes, I know this is allowed, but it _IS_ a candidate for a stylistic
: warning). Such a warning would be much more useful in catching an error
: with respect to the contents of Y.
Yeah, I'd like to be forced to use a cast here if I _really_ meant to
do it.
: The point is that the heuristics used by a compiler in generating warnings
: are useful most of the time, but they can be both annoying and unhelpful.
IMO, the only problem is with warnings which cannot easily be avoided
or silenced; I haven't run into any with gcc, but I have with other
compilers.
Oh, while we're on gcc warning gripes, why no complaint about the
"dangerous" (double) -> (int) conversion? If I want the fractional
part of a number dropped, I'll use a cast, thank you ...
---------------------------------------------------------------------------
Tim Hollebeek | Disclaimer :=> Everything above is a true statement,
Electron Psychologist | for sufficiently false values of true.
Princeton University | email: t...@wfn-shop.princeton.edu
----------------------| http://wfn-shop.princeton.edu/~tim (NEW! IMPROVED!)
| > However, the point here is that although a macro might *look* like a
| > function invocation, it is not.
|
| We know that.
and then he wrote (ditto)
| > There are perfectly sensible uses of
| > macros that expand to things other than expressions; these cannot be
| > wrapped up to look like functions even if you want to.
|
| They can be wrapped up to look like functions in some contexts, which is
| better than wrapping it up to look like a function followed by a syntax
| error.
which, to me, clearly and completely contradicts the *first* thing he
wrote. Strange, that.
Now, I've read what Mr Volpe has written in many postings, and
(light-hearted badinage aside) have the utmost respect for it; I've been
following this newsgroup for many years, and this is my conclusion.
Maybe he doesn't like being diagnosed as prepsychotic; however, it is
important to distinguish the jokes from the substance. I expect I just
read too many comics as a child or something.
I wrote ...
| > For example,
| >
| > #define CALL(f) {if ((e = (f)) != NULL) goto finish;}
| >
| > is a statement;
and he wrote
| No it's not.
Now come ON!
| A statement, with its terminating semicolon, is allowed to
| appear before an "else" clause.
As is that.
| That's why the above definition fails,
| and the do-while construct works.
It doesn't fail.
| > I have always been surprised that the advice to use
| >
| > do ... while (0)
| >
| > is in the FAQ for this newsgroup. It is a
| > allows a misguided person
|
| In your opinion.
I *Said* that---that's IMHO.
| > to get away with a spurious semicolon in one
|
| It's not spurious, it's necessary. Statements must be terminated with a
| semicolon, except when they are enclosed in braces.
Nonsense. What about an if-statement? Or a for-statement? Or a
while-statement? Or a switch-statement? Or a compound statement (which
is not any (other) sort of statement enclosed in braces)? The
do-statement actually is the *only* statement to require a semicolon,
apart from various things that function only as parts of a greater
whole, such as breaks, continues, gotos, returns. That's why it's abused
in this way.
Imagine a language like C but with
do-statement ::= 'do' statement 'while' '(' expression ')'
instead of
do-statement ::= 'do' statement 'while' '(' expression ')' ';'
Now what do you do? That's why it's a *trick*.
| Since the macro
| hides the braces, it forces a syntax change at the invocation of the
| macro.
It's only a change if you think the macro has to be an expression;
but we've agreed it doesn't.
| > If it was the right thing to do, it would work in comma expressions,
|
| Non sequitur. That's like saying if declaring a variable with the
| "register" qualifier were the right thing to do, it would work in
| address-of expressions (i.e. ampersand operator, or array type
| conversion).
I don't agree. To reiterate, a macro should expand to a recognisable
syntactic construct of the language; which one, should be documented;
and do ... while (0) isn't one (except the trivial one of itself).
Ah. Here we have the root of the problem. That's a rather poor rule
to use. I generally use the rule that if a macro invocation isn't a
legal expression, then it is adding new syntax to the language, and
should therefore have the flavour of the base language, as far as is
possible.
> #define CALL(f) {if ((e = (f)) != NULL) goto finish;}
Here the CALL macro is being used to create a new type of statement.
If this were part of the C syntax, its production would be
statement -> CALL ( expression ) ;
as all statements end with either ; or }. So the macro should support
this use:
#define CALL(f) do if ((e = (f)) != NULL) goto finish; while(0)
(Braces may be insrted for additional clarity.) Another possible
definition would be
#define CALL(f) if ((e = (f)) == NULL) ; else goto finish
Note that in each of my versions of this macro, an incorrect usage,
without a semicolon, will result in a diagnostic, whereas the most
likely incorrect usage of your version will not *usually* generate a
diagnostic.
-zefram
Yes it is. After the above definition, "CALL(foo)" *is* a statement,
and "CALL(foo);" is two statements.
> A statement, with its terminating semicolon,
Not all statements end in a semicolon. A compound statement ends in
}. Some statement productions end with another statement, and can thus
end with either ; or }.
> is allowed to
>appear before an "else" clause. That's why the above definition fails,
>and the do-while construct works.
With the above definition, "CALL(foo)" can appear as the first
sub-statement of an if. For example "if(foo) CALL(bar) else baz=2;"
would be legal. I agree that the definition is a bad idea (see another
posting), but your argument is weakened here by misuse of terminology.
-zefram
That is because you are using ONE compiler; I use dozens, and write code
that I intend to compile under all of them without system-dependent #ifs.
For example:
The Solaris 2 compiler gives warnings about semantic changes from
the SunOS 4 compiler (which is not ANSI). What's more, it gets them
wrong!
Many supercomputer compilers give warnings about unvectorisable
loops. I have never seen a program that didn't generate some of these
warnings, for obvious reasons.
Some compilers warn about possibly uninitialised local variables.
Others warn about redundant assignments. Now, just get THOSE two in
step, remembering that the compilers don't use quite the same rules
for analysing the data flow :-)
You will find that the vast majority of the participants of this group
will disagree with you.
> True, the changes should be well considered and well
> documented, but changing the syntax can be very beneficial.
>
> The downside is that programmers new to the altered syntax need an
> hour or so to pick up the differences and perhaps a week or so to get
> used to the changes, but the utility can far outweigh that cost.
Matter of opinion. Someone maintaining your code will not be happy with
it.
>
> To give an example, the standard that I use defines the following three
> macros...
>
> #define nil
> #define when(x) if(!(x)) nil; else
> #define unless(x) if(x) nil; else
>
> these have the following advantages. First the "nil" macro is very
> useful in places where only a null statement (;) is needed. It explicitly
> makes it clear that there is an omitted statement. For example...
>
> for (char * p = q; *p; ++p)
> nil;
What's wrong with:
for (char *p = q; *p; ++p)
/* EMPTY BODY!! */ ;
>
> either of which frequently leads to readability problems.
>
> Now, consider when(x) and unless(x). Knuth did a study and found
> that 80% of all if statements do not have an else clause (in FORTRAN
> source code, but I see no evidence it is any different in C and C++).
So what?
> Further, many programmers (if not most) have problems understanding
> negation.
Proof by blatant assertion?
>
> So the first advantage of when(x) and unless(x) is that they
> are used if and only if there is to be no else clause present. This can
> prevent many dangling else problems and clearly signals the intent of
> the code.
It obfuscates the code. If I see
when (x<0)
x++;
the first thing I'm going to ask is "How is this different from an
'if'?". The second thing I'm going to ask, after hunting down this macro
definition, is "Why didn't he just use an 'if'?". The third question I'm
going to ask myself is "What good is a special keyword for an else-less
'if' when all it accomplishes is force me to replace keywords whenever I
want to add an else clause?".
> It can also reduce the indentation
> level, depending on your indentation standard.
Example please? Replacing "if" with "when" doesn't accomplish this.
Neither does replacing "if (!x)" with "unless (x)".
> The second advantage,
> is that unless(x) has a built-in semantic negation which speakers of
> the English language understand much better than explicit symbolic
> negation.
Citation please? I speak the English language, and I find myself doing a
quick mental translation from "unless x do y" to "if x is false, do y",
which I find clearer.
> Both of these reduce coding errors.
I don't believe that one bit.
>
> My company has written many, many source programs using these
> standards and have not found a programmer unable to pick up the
> language "additions" in a few minutes.
I'm glad I don't work there.
>
> So, the use of macros to change the language can be a VERY GOOD
> THING!
All in favor say "aye!" :-)
>
> The absurd idea that we want exactly one way to a accomplish
Who said we did? I'm just saying that making a program look like it's
written in an unrecognizable langage benefits no one, and makes
readability a real pain.
> something in a programming language is due to Wirth and one of
> the worst ideas to ever to pushed on the computer industry. We
> don't speak Esperanto and for the same reason English offers many
> different ways of saying the same thing, each with its own slight
> semantic difference in meaning. This richness is to be coveted in
> programming languages as well as natural languages.
Which is why we have if statements and switch statements, in addition to
goto statements and while statements.
>
> That doesn't mean that we need ten different if statements which
> are semantically the same except for name, but if there is a semantic
> difference likely to be used, then it improves both the conciseness
> and readability of the resulting source code. There is, of course, a
> careful balancing act between too many variants and too little
> semantic richness, but that is language design issue.
And if you're designing a language, fine. If you're writing programs and
calling them C programs, then you're not designing a language.
>
> The trivial increase in learning time is well worth the cost. It
Hardly. It makes updates more expensive. It distracts the reader and
prevents the quick interpretation of common idioms that would otherwise
be immediately recognized due to their form. For example, if I see
if (p != NULL)
free(p);
I don't even have to think about it. If I see
unless(p == NULL)
free(p);
I've got to stare at it for a few seconds.
would
> be best, of course, for variants like when(x) and unless(x) to make
> it into the standard, but until then, altering the language syntax using
> macros is most assurdly a reasonable thing to do. Even if not to
> everyone's taste.
I disagree.
I clarified my intent in another posting. I won't repeat it here.
>
> With the above definition, "CALL(foo)" can appear as the first
> sub-statement of an if. For example "if(foo) CALL(bar) else baz=2;"
> would be legal.
Which is precisely the problem.
> I agree that the definition is a bad idea (see another
> posting), but your argument is weakened here by misuse of terminology.
I acknowledge that I was unclear.
What about the null statement, and expression statements? do happens
to be the cleanest for this purpose.
> Imagine a language like C but with
>
> do-statement ::= 'do' statement 'while' '(' expression ')'
>
>instead of
>
> do-statement ::= 'do' statement 'while' '(' expression ')' ';'
It wouldn't be C.
>Now what do you do?
#define FOO() if(1) { do_something(); do_something_else(); } else 0
-zefram
Swings and roundabouts.
I came to C from BCPL. In BCPL the syntactic forms are:
IF condition
DO statement
UNLESS condition
DO statement
TEST condition
THEN statement
ELSE statement
(in each case, DO and THEN are equivalent).
So these macros seem perfectly simple *to me*. I agree, though, that the
average C programmer would probably be confused, and the confusion of
the next person to read my code is more significant than bringing back
memories of my youth (:-)
--
Clive D.W. Feather | You should reply to <cl...@demon.net> (the
Associate Director | Reply-To: header has been set to this). This
Demon Internet Limited | account is on my laptop and is only used when I
| travel; messages to it may not be seen quickly.
In article <1996Aug25.1...@dcs.warwick.ac.uk>,
A.M...@dcs.warwick.ac.uk (Zefram) writes:
> Jonathan Coxhead <jona...@doves.demon.co.uk> wrote:
>> The rule I follow, and which seems the only one that can work in
>> all cases, is that a macro should expand to *some* valid syntactic
>> construct of the language.
>
> Ah. Here we have the root of the problem. That's a rather poor rule
> to use.
[Note that it may also be construed as a tautology,
permitting anything!]
> I generally use the rule that if a macro invocation isn't a
> legal expression, then it is adding new syntax to the language, and
> should therefore have the flavour of the base language, as far as is
> possible.
Here's my rule, as expressed in C Programming FAQs (Addison-
Wesley, 1996, ISBN 0-201-84519-9):
As a general rule, it's a good idea if the use of
preprocessor macros follows the syntax of the C language.
Macros without arguments should look like variables or
other identifiers; macros with arguments should look like
function calls. Ask yourself: "If I somehow presented
this code to the compiler without running it through the
preprocessor, how many syntax errors would I get?"
(Of course, you'd get plenty of undefined symbols and
non-constant array dimensions, but those aren't *syntax*
errors.) This rule means that C code, plus macro
invocations, still looks like C code. So-called
nonsyntactic macros like begin and end or CTRL(D) (see
question 10.21) can make C look like gobbledygook
(see also question 20.36).
This is of course largely a style issue; see also chapter 17.
This isn't perfect, but it's what I use as a point of departure.
[Jonathan Coxhead again:]
>> I have always been surprised that the advice to use
>> do ... while (0)
>> is in the FAQ for this newsgroup.
That FAQ list (note that it's for comp.lang.c, not comp.std.c) is
an uneven amalgam of newsgroup consensus, personal opinion, and
Absolute Truth. The do/while trick was (at least at the time I
composed that entry) certainly the consensus in comp.lang.c on
what to use, and I happen to agree that it's a decent tradeoff.
>> It is a purely syntactic trick that
>> allows a misguided person to get away with a spurious semicolon in one
>> particular situation, without actually addressing the issue.
It's definitely a trick, and it's definitely ugly (the macro
definition, that is, though not the invocations). It's hardly
the only place you can get away with spurious extra semicolons,
though! (An old co-worker used to like to put them after
function bodies, "for consistency," until ANSI C disallowed
vacuous declarations.)
>> If it was the right thing to do, it would work in comma expressions,
>> as function arguments, etc.
Good points, I suppose. When a "multi-statement" macro can in
fact be expressed as multiple, comma-separated *expressions*,
that's indeed the way to go, and it will then work in the
additional contexts you mention. Otherwise, the thing you end
up with is definitely statement-like, and the fact that you can't
stick it in comma expressions or argument lists is no more a
disappointment (to me, anyway) than that you can't put break,
return, or if/then statements there.
If (as I concede) a certain macro is "statement-like," I suppose
one could argue that it should be allowed to subsume the
semicolon, and to be invoked without one. The question of
whether the semicolon is properly part of a statement or a
terminator (or, for recent arrivals, a separator) is an old and,
to me, not very interesting one, although it may be that I find
it uninteresting only because I've never thought about it hard
enough, but rather brushed semicolons off as some kind of
programmer's equivalent to a lower spinal cord reflex.
However: requiring that, for some x, the construction
x(a, b)
, which looks for all the world like a function call, be invoked
as an expression statement *without* a semicolon, strikes me as
an accident waiting to happen.
It occurs to me that the right way to think about a multi-
statement macro, which is implemented as a function-like macro,
perhaps using the do/while trick, and which also has this
"statement-like" character in its invocation, is that it is a
void-valued function. You can't call void-valued functions in
argument lists, either, and I can't think of too many reasons
to put them in comma expressions. About the only places they
ever appear (other than as controlling expressions of for loops,
where this argument breaks down) are as standalone expression
statements, followed by semicolons.
The fundamental, underlying issue is of course that C has
separate statements and expressions at all. If it were the kind
of language (I forget the buzzword) where all statements have
values and where you can use any statement-like thing within any
expression-like thing, then we wouldn't be having this discussion
(and, incidentally, C wouldn't have ?:).
Steve Summit
s...@eskimo.com
I used BCPL for a long time; BCPL has "if" and "unless" and also "while"
and "until" ("until (x)" == "while (!x)"). I puzzled for a long time over
when to use one and when the other. My first idea was to use whichever simplified
the controlling expression the most. However I found that this didn't always improve
readability. In the end I settled on the rule of thumb that the expression should
represent the special case, i.e. it is better to express things not as "if (every-
thing's fine)" but "unless (something's wrong)".
However it's pretty marginal and most programmers are so used to "if-not" that the
unfamiliarity of "unless" outweighs any benefits it might have in terms of semantic
immediacy.
As for "while" and "until", the criterion I came on was "while (everything's
fine and dandy)" as opposed to "until (we've found what we are looking for)".
Less interestingly, ...
-->> First the "nil" macro is very
-->> useful in places where only a null statement (;) is needed. It explicitly
-->> makes it clear that there is an omitted statement. For example...
-->>
-->> for (char * p = q; *p; ++p)
-->> nil;
-->
-->What's wrong with:
-->
--> for (char *p = q; *p; ++p)
--> /* EMPTY BODY!! */ ;
or the more `natural'
for (char *p = q; *p; ++p)
; /* Do nothing! */
I really don't see that (intended) empty statements are a problem. Only a fool
(he says boldly/provocatively) would write:
for (char *p = q; *p; ++p);
-- jP --
Sorry, I appear to have missed the above post. I have no citation,
merely the observation that even small children can easily understand
statements such as "unless its raining we can go to the beach" and
the observation that almost all programmers I have seen have problems
with negation. On of my standard interview requests is to ask the
candidate the propagate the "not" through the parenthesis in the
expression...
not (A and B)
and found that only about 2% get it right and most of those have
to "think" about the problem. Also the observation that problems
with negations seem to abound in programs that I have been
called upon to fix. For me and apparently you, there may be no
significant advantage. And for many if not most programmers that
actively discuss programming it is not a problem. But for the
run of the mill programmers it is a serious problem.
>However it's pretty marginal and most programmers are so used to "if-not" that the
>unfamiliarity of "unless" outweighs any benefits it might have in terms of semantic
>immediacy.
For expressions containing a single term, you are right. When there
are two or more terms "most" programmers seem to get it wrong
more often than not.
>-->> First the "nil" macro is very
>-->> useful in places where only a null statement (;) is needed. It explicitly
>-->> makes it clear that there is an omitted statement. For example...
>-->>
>-->> for (char * p = q; *p; ++p)
>-->> nil;
>-->
>-->What's wrong with:
>-->
>--> for (char *p = q; *p; ++p)
>--> /* EMPTY BODY!! */ ;
>
>or the more `natural'
>
> for (char *p = q; *p; ++p)
> ; /* Do nothing! */
Most programmers won't write that. I have yet to see that (or any
variant) in code where an empty statement is used. Also, you have
to mentally parse a phrase rather than a single token with a
specific usage.
>I really don't see that (intended) empty statements are a problem. Only a fool
>(he says boldly/provocatively) would write:
>
> for (char *p = q; *p; ++p);
Then there a lot of fools out there. Unfortunately, all too many of
them work at places I have been. You simply cannot underestimate
the stupidity of the average programmer. I wish that weren't true,
and I had these nice illusions until I started interviewing "top notch"
programmers. I miss the days that I could blithely assert that MOST
programmers have no problems with negations, or that most "good"
programmers indent their code (and surely, most programmers are
"good" programmers, right?), but I have been sadly disappointed
over and over and over and...
So everything I can do to help them avoid mistakes improves the
quality of code. If using "when" means that a programmer does
not have a dangling else problem just once or twice then it is
worth it. If using "unless (x)" or "until (x)" (as in BCPL, also as
in my coding standards with the same semantics as while (!(x)))
means that a problem with negation is eliminated before it hatches,
then it is worth it.
And generally, semantic richness is far more important than
minimalism. I know that minimalism in programming languages
has gotten to be popular because of languages like PL/1 and
the relative simplicity of languages like PASCAL, but if minimalism
were really that good we would have MUCH simpler programming
languages. After all, all you really need is a "block" statement
with named "break" and "continue" and a single branched conditional
such as "when" to write any tree-structured program. Everything
else can be built up from those statements.
But limiting ourselves to that would be a very large pain. And likewise,
limiting the semantic variants JUST for the principle of minimalism
is just as much a pain. Needless redundancy in language design is
bad, of course, but semantic richness means that there are similar
ways to accomplish things, but each of those ways is slightly
different and better suited to different applications.
| > Jonathan Coxhead <jona...@doves.demon.co.uk> wrote:
| >> The rule I follow, and which seems the only one that can work in
| >> all cases, is that a macro should expand to *some* valid syntactic
| >> construct of the language.
| >
| > Ah. Here we have the root of the problem. That's a rather poor rule
| > to use.
We disagree here.
| >> If it was the right thing to do, it would work in comma expressions,
| >> as function arguments, etc.
|
| Good points, I suppose.
Thank you! And thank you for all your efforts on the FAQ.
| The fundamental, underlying issue is of course that C has
| separate statements and expressions at all. If it were the kind
| of language (I forget the buzzword) where all statements have
| values and where you can use any statement-like thing within any
| expression-like thing, then we wouldn't be having this discussion
| (and, incidentally, C wouldn't have ?:).
Orthogonal?
Cheers, anyway,
| Jonathan Coxhead <jona...@doves.demon.co.uk> wrote:
| > Imagine a language like C but with
| >
| > do-statement ::= 'do' statement 'while' '(' expression ')'
| >
| >instead of
| >
| > do-statement ::= 'do' statement 'while' '(' expression ')' ';'
|
| It wouldn't be C.
True, but it's damn close, and makes a point.
| >Now what do you do?
|
| #define FOO() if(1) { do_something(); do_something_else(); } else 0
Ouch! I give up!
I still think it's an abuse of the syntax for no clear gain, but it
*is* clever.
Julian Pardoe LADS LDN X1428 <par...@lonnds.ml.com> wrote:
>I really don't see that (intended) empty statements are a problem. Only a fool
>(he says boldly/provocatively) would write:
>
> for (char *p = q; *p; ++p);
Only a C++ programmer would write that. A C programmer would write
char *p;
for (p = q; *p; ++p);
or, more practically,
char *p = strchr(q, 0);
But ignoring that issue, this form (with the null statement on the same
line as the for) is the form I prefer. When one sees "for" at the
beginning of a line, one expects to find ")" or ") {" at the end of the
line, so ");" or ") ;" does stand out. The lack of indentation on the
next line is an additional clue (assuming the source is correctly
indented, one way or another).
-zefram
You don't have to define a macro to handle this -- there is a reserved word.
for (char *p = q; *p != '\0'; ++)
continue;
You'll note that I made one other change to the code that makes the intent
slightly plainer.
Similarly:
if ((c = getchar()) == EOF)
have_eof();
Should change to:
c = getchar();
if (c == EOF)
have_eof();
(Less cluttered, easier to understand at a glance)
and:
while ((c = getchar()) != EOF)
{
/* body */
}
Can change to:
while (c = getchar(), c != EOF)
{
/* body */
}
Without breaking any 'continue' statements in the loop.
while() and for() are about the only places I ever use a comma operator.
--
Darron Shaffer
lmmo...@dallas.net (wife's account)
[major snip]
>However: requiring that, for some x, the construction
>
> x(a, b)
>
>, which looks for all the world like a function call, be invoked
>as an expression statement *without* a semicolon, strikes me as
>an accident waiting to happen.
Steve,
I agree with all you wrote. I'd just like to point out the
"exception that proves the rule" which is the old-style (varargs.h)
macro:
va_dcl
which must be invoked *without* a semicolon. I have heavily commented
this fact in the (approximately three) functions in which I've used it.
(I wonder who thought that one up... oh, never mind).
I believe the ANSI (stdarg.h) macros don't suffer from that defect,
but I only need to write a varargs-type function every couple of years
or so, and end up figuring it out from scratch each time. :-)
Cheers,
Stan.
--
Stan Ryckman (st...@tiac.net)