A. #undef FOO
or
B. #define FOO
or
C. #define FOO 0
or
D. #define FOO 1
I need to consider A and C to be equivalent, and B and D to be
equivalent. How do I test for that?
Some example tests:
#if FOO is true for D. #ifdef FOO is true for B, C, and D. #if FOO+0
== 0 is true for B and C.
The problem is that I can't find a test that distinguishes cases B and C.
Any suggestions?
Mark
#ifdef FOO
#if FOO != 0
#undef FOO
#define FOO 1
#endif
#else
#define FOO 0
#endif
This results in an error in case B: "operator '!=' has no left operand".
Mark
If there really are only those four options:
#if !defined(FOO) || FOO == 0
#error undef or zero
#else
#error defined empty or 1
#endif
The message is incorrect, of course, if the definition of FOO is
something other than empty or 1.
--
Ben.
Token-gluing with the ## operator should solve it.
Just hope the pool of crazy definitions doesn't expand to include
-DFOO=yes -DFOO=no
#include <stdio.h>
/*Insert one of A,B,C,D here*/
#define CAT(x,y) CAT2(x,y)
#define CAT2(x,y) x##y
#ifdef FOO
# if CAT(1,FOO) == 1
# define FOO_is_undef_or_0 0
# elif FOO
# define FOO_is_undef_or_0 0
# else
# define FOO_is_undef_or_0 1
# endif
#else
# define FOO_is_undef_or_0 1
#endif
int main(void)
{
#if FOO_is_undef_or_0
puts("FOO is undef or 0");
#else
puts("FOO is empty or 1");
#endif
}
--
Alan Curry
I'm going to take this last question not totally literally and
confine myself to suggestions about distinguishing case B and C.
You already know how to narrow down to the 'it's either B or C'
case so I'll ignore that part of the problem.
Disclaimer: this idea doesn't work if the definition for FOO is
other than a simple numeric value 0 (or blank), and can fail
miserably if it's something more complicated. And, stylistically,
it's a total crock. However, you did say "any suggestions?".
#define CROCKIFY(x) x ## 15
#define C_TEST(x) CROCKIFY(x) == 13
After these macro definitions, the preprocessor test
#if C_TEST(FOO)
will include the following lines if FOO is defined as 0, and not
include the following lines if FOO is defined just as an empty
definition.
It also has the additional benefit of being very likely to generate
compilation errors if FOO is defined as anything other than blank or
a simple number (or identifier). Of course some people might not
think of that as a benefit.
This fails with an error on that first line for case B: "operator '||'
has no right operand".
Mark
If you've got a C99 compiler,
#define BAR(x) BARQ(x)
#define BARQ(x) BAR_ ## x
#define BAR_ 10
#define BAR_0 11
#define BAR_1 12
#define BAR_FOO 13
...
#if BAR(FOO) == 10
puts ("blank");
#elif BAR(FOO) == 11
puts ("zero");
#elif BAR(FOO) == 12
puts ("one");
#else
puts ("undefined or garbage");
#endif
(You could disambiguate the final case with #ifdef, if desired.)
--
Eric Sosman
eso...@ieee-dot-org.invalid
Alan,
Thanks -- this successfully discriminates the four cases. Now I'll see
how portable ## is.
By the way, why is CAT2 needed?
Mark
|Alan,
|
|Thanks -- this successfully discriminates the four cases. Now I'll see
|how portable ## is.
It was in the 1989 standard, so there shouldn't be many surviving compilers
that don't support it.
|
|By the way, why is CAT2 needed?
Because the preprocessor won't expand a macro parameter and do the ##
operation in a single step, so you have to give it 2 chances. As for why it
was designed that way, I have no idea. There would be no harm in allowing
#define cat(x,y) x##y to do the job by itself, since what it does now without
the second expansion pass is never useful.
--
Alan Curry
Ha! Poor testing. Maybe this will make amends:
#if -FOO+1 == 1
#error A or C (undefined or 0)
#else
#error B or D (empty or 1)
#endif
--
Ben.
Completely.
> By the way, why is CAT2 needed?
Because of the way macro expansion works; the arguments to a macro
are reexpanded in a possibly-surprising way; check the comp.lang.c FAQ
for the gory details.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet...@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
> Mark Adler <mad...@alumni.caltech.edu> writes:
>>Outside of my source file, someone will do one of these four things:
>>A. #undef FOO
>>B. #define FOO
>>C. #define FOO 0
>>D. #define FOO 1
>>I need to consider A and C to be equivalent, and B and D to be
>>equivalent. How do I test for that?
>
> This makes no sense to me. How can one »do« a preprocessor
> directive? Did you mean that someone will /write/ them into
> a text file? With C you can read certain text files and
> inspect their contents, like - simplified -
>
> if( strstr( text, "#define ALPHA 1" ))puts( "case D." );
I think the meaning is that a source file will be translated "as if" one
of these four were in effect. The most obvious method being though a
command-line argument (often -UFOO -DFOO= -DFOO=0 and -DFOO=1) to the
compiler.
BTW, my "poor testing" was to forget that, with gcc, -DFOO defines FOO
as 1 not as an empty token list -- you need -DFOO= to get case B.
Doing this without token pasting would make an amusing puzzle, I think.
--
Ben.
Exactly.
Mark
Ok, this looks like an interesting approach.
However I'd wonder if there won't be a compiler that would complain
about "-+1" as an expression.
Mark
Why would it? - - - - - - - - - - - 5 is a perfectly valid expression.
> On 2010-04-10 15:14:50 -0700, Ben Bacarisse said:
>> #if -FOO+1 == 1
>
> Ok, this looks like an interesting approach.
Simpler than the token pasting option but also less direct. A comment
will fix that, though.
> However I'd wonder if there won't be a compiler that would complain
> about "-+1" as an expression.
There are more reasonable things to worry about! -+1 is perfectly valid
(meaning -(+(1)) of course -- C's integer constants have no optional
sign). If -+1 fails you are likely to have other problems with the
implementation.
--
Ben.
I wasn't worried about the minus, I was worried about the plus. Unary
plus was not always valid in C. K&R and the old Sun C didn't allow it.
Mark
Oh, wait. Then there's an easy fix:
#if -FOO - -1 == 1
Should do the same thing, where - - -1 is clearly valid. Testing ...
and ... it works!
Thank you all for your help. This looks like the most portable solution.
Mark
Unary + was introduced by ANSI in the C89 standard. If you're using a
compiler so old that it doesn't support it, then it doesn't conform to
*any* standard, and you're likely to have worse problems than that.
--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
If you are really trying to get something that works for pre-ANSI C then
you need to remember that K&R C does not say what happens in case A
where FOO is not defined. At least +1 will generate an error message on
such systems. In some K&R C implementations, undefined FOO may be
treated as if FOO were defined to be empty (though, to be honest, I
can't remember any particular behaviour).
--
Ben.
Making the preprocessor standards-conformant is an area the compiler vendors tend to neglect. Two examples that hit me in the last 6 months:
1) An admittedly obscure embedded C compiler, claiming C90 conformance with traces of C99, under active maintenance, recognized unary + in the compiler, but not the preprocessor, due to a defect; the vendor fixed it within hours of my report.
2) A non-obscure compiler (if you visualize what I mean) won't compile:
// return the smallest of x y z
#define MIN3(x,y,z) ( (x)>(y) ? (y)>(z) ? (z) : (y) : (x) )
#define ALPHA 5
#define BETA 4
#define GAMMA 6
#if MIN3(ALPHA,BETA,GAMMA) < 2
#error "check ALPHA BETA GAMMA"
#endif
The (representative of the) vendor's comment on that bug: "We were able to reproduce this issue. But Unfortunately we will not fix this issue as it is not a common code and is not a high priority issue.Thanks for playing with our product."
https://connect.microsoft.com/VisualStudio/feedback/details/546053
Francois Grieu
Age doesn't necessarily have anything to do with it. I would very much
like to live in a world where compilers (and preprocessors) that
claimed to conform to a standard actually did. A compiler can be new
and still not be compilant with C89. The standard is unfortunately not
reality -- the standard is a wish, often unfulfilled.
Mark
I prefer the other way...
#ifndef CAT
#define CAT(a,b) a ## b
#define CAT2(a,b) CAT(a, b)
#define CAT3(a,b,c) CAT2(CAT2(a, b), c)
#define CAT4(a,b,c,d) CAT2(CAT3(a, b, c), d)
...
#endif
Then I use CAT<N> explicitly.
> #ifdef FOO
> # if CAT(1,FOO) == 1
> # define FOO_is_undef_or_0 0
> # elif FOO
> # define FOO_is_undef_or_0 0
> # else
> # define FOO_is_undef_or_0 1
> # endif
> #else
> # define FOO_is_undef_or_0 1
> #endif
% type defined.h
#define PREFIX_FOO 1 /* undefined */
#define PREFIX_0 10 /* defined 0 */
#define PREFIX_1 11 /* defined 1 */
#define CAT(a,b) a ## b
#define CAT2(a,b) CAT(a, b)
#define STR(x) #x
#define STR2(x) STR(x)
puts(STR2(CAT2(PREFIX_,FOO)));
#if !defined(FOO) || CAT2(PREFIX_,FOO) == PREFIX_0
puts("A or C");
#else
puts("B or D");
#endif
#if CAT2(PREFIX_,FOO) == PREFIX_FOO
puts("A undefined");
#elif CAT2(PREFIX_,FOO) == 0
puts("B defined blank");
#elif CAT2(PREFIX_,FOO) == PREFIX_0
puts("C defined 0");
#elif CAT2(PREFIX_,FOO) == PREFIX_1
puts("D defined 1");
#else
#error huh
#endif
% type defined.c
#include <stdio.h>
int main(void)
{
puts("#undef FOO");
#undef FOO
#include "defined.h"
puts("\n#define FOO");
#define FOO
#include "defined.h"
puts("\n#define FOO 0");
#undef FOO
#define FOO 0
#include "defined.h"
puts("\n#define FOO 1");
#undef FOO
#define FOO 1
#include "defined.h"
return 0;
}
% acc defined.c -o defined.exe
% defined.exe
#undef FOO
1
A or C
A undefined
#define FOO
PREFIX_
B or D
B defined blank
#define FOO 0
10
A or C
C defined 0
#define FOO 1
11
B or D
D defined 1
%
--
Peter
Why disable an option unnecessarily? Sometimes you want the
macros expanded, sometimes you don't. Maybe you don't very
often, but maybe you don't know you don't until it happens,
and then you _really_ don't want it! ;)
I've certainly had cases where I didn't want # to expand
it's argument. Why make ## different?
> There would be no harm in allowing #define cat(x,y) x##y to
> do the job by itself, since what it does now without the
> second expansion pass is never useful.
A related problem is stopping the expansion...
#define CAT(x,y) x ## y
#define CAT2(x,y) CAT(x,y)
#define PLUS '+'
#define MINUS '-'
#define OP_PLUS 0
#define OP_MINUS 1
#ifndef TEST /* default PLUS, but allow -DTEST=MINUS
#define TEST PLUS
#endif
/* neither of these work */
#define OP_TEST CAT(OP_, TEST)
#define OP_TEST CAT2(OP_, TEST)
I want OP_TEST to expand to OP_PLUS, not OP_TEST, and
certainly not error on OP_ ## '+'.
[Of course, the solution is to do something like...
#define CHAR_PLUS '+'
#define CHAR_MINUS '-'
#define OP_PLUS 0
#define OP_MINUS 1
#ifndef TEST /* default PLUS, but allow -DTEST=MINUS
#define TEST PLUS
#endif
#define CHAR_TEST CAT2(CHAR_, TEST)
#define OP_TEST CAT2(OP_, TEST)
]
--
Peter
> Disclaimer: this idea doesn't work if the definition for FOO is
> other than a simple numeric value 0 (or blank), and can fail
> miserably if it's something more complicated. And, stylistically,
> it's a total crock. However, you did say "any suggestions?".
>
> #define CROCKIFY(x) x ## 15
> #define C_TEST(x) CROCKIFY(x) == 13
>
> After these macro definitions, the preprocessor test
>
> #if C_TEST(FOO)
>
> will include the following lines if FOO is defined as 0, and not
> include the following lines if FOO is defined just as an empty
> definition.
>
> It also has the additional benefit of being very likely to generate
> compilation errors if FOO is defined as anything other than blank or
> a simple number (or identifier). Of course some people might not
> think of that as a benefit.
That's positively, err, something.
--
Online waterways route planner | http://canalplan.eu
Plan trips, see photos, check facilities | http://canalplan.org.uk
Thank you for contributing, Nick. I have given your post a 5-star
rating in Google Groups. It's posts like yours that make this
newsgroup such a valuable resource, and I think it's important that we
all take a moment to acknowledge that. Bless you, sir!
I can't quite work out whether you are sarcastically getting at me for a
silly comment, or agreeing with me!
I felt that Tim's wonderful abuse of octal/decimal was well worth
pulling out of a thread and drawing more attention to. It's not like we
have too much C code posted here.
I'm glad you did, I'd missed it, and it is BRILLIANT.
> On 2010-04-13, Nick <3-no...@temporary-address.org.uk> wrote:
>> I felt that Tim's wonderful abuse of octal/decimal was well worth
>> pulling out of a thread and drawing more attention to. It's not like we
>> have too much C code posted here.
>
> I'm glad you did, I'd missed it, and it is BRILLIANT.
Me too, me too, and it is, according to me too. The Underhanded C Contest
comes to mind.
lacos
I'm glad you liked it. How often do we get a code example
that so obviously satisfies the 80/20 rule as the use here
of octal/decimal does?
A non-obscure compiler (if you visualize what I mean) won't compile:
// return the smallest of x y z
#define MIN3(x,y,z) ( (x)>(y) ? (y)>(z) ? (z) : (y) : (x) )
#define ALPHA 5
#define BETA 4
#define GAMMA 6
#if MIN3(ALPHA,BETA,GAMMA) < 2
#error "check ALPHA BETA GAMMA"
#endif
The (representative of the) vendor's comment on that bug: "We were
able to reproduce this issue. But Unfortunately we will not fix this
issue as it is not a common code and is not a high priority issue.
Thanks for playing with our product."
A "Quality Assurance" representative for that compiler added: "Though
the case you reference is more common, since it is wrapped in a macro,
it is fairly trivial to apply the workaround you provided [adding
parenthesis]. Especially given the workaround, we do not view this
issue as one that affects a significant number of customers, and have
chosen to prioritize it below other, more serious, issues.
https://connect.microsoft.com/VisualStudio/feedback/details/546053
The aspiration to make a bug-free program seems gone; even though,
in the case of at least the preprocessor, it seems something a single
person could fulfill in at most a few weeks of work.
Comments and other experiences welcome. Also, feel free to vote at
the above URL on what you think should be done.
Francois Grieu
The final sentence is telling: It reveals that the vendor
considers the product to be a toy.
--
Eric Sosman
eso...@ieee-dot-org.invalid