Test of a preprocessor symbol defined as nothing vs. zero

M

Mark Adler

Outside of my source file, someone will do one of these four things:

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
 
M

Malcolm McLean

Outside of my source file, someone will do one of these four things:

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
 
M

Mark Adler

#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
 
B

Ben Bacarisse

Mark Adler said:
Outside of my source file, someone will do one of these four things:

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?

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.
 
A

Alan Curry

|
[...]
|B. #define FOO
|
|or
|
|C. #define FOO 0
[...]
|
|The problem is that I can't find a test that distinguishes cases B and C.
|

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
}
 
T

Tim Rentsch

Mark Adler said:
Outside of my source file, someone will do one of these four things:

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?

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.
 
E

Eric Sosman

Outside of my source file, someone will do one of these four things:

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?

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.)
 
M

Mark Adler

#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

Alan,

Thanks -- this successfully discriminates the four cases. Now I'll see
how portable ## is.

By the way, why is CAT2 needed?

Mark
 
A

Alan Curry

|> #define CAT(x,y) CAT2(x,y)
|> #define CAT2(x,y) x##y

|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.
 
S

Seebs

Thanks -- this successfully discriminates the four cases. Now I'll see
how portable ## is.
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
 
B

Ben Bacarisse

Mark Adler said:
This fails with an error on that first line for case B: "operator '||'
has no right operand".

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
 
S

Stefan Ram

Mark Adler said:
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." );

.
 
B

Ben Bacarisse

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.
 
M

Mark Adler

#if -FOO+1 == 1

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
 
S

Seebs

However I'd wonder if there won't be a compiler that would complain
about "-+1" as an expression.

Why would it? - - - - - - - - - - - 5 is a perfectly valid expression.

-s
 
B

Ben Bacarisse

Mark Adler said:
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.
 
M

Mark Adler

Why would it? - - - - - - - - - - - 5 is a perfectly valid expression.

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
 
M

Mark Adler

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.

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,260
Latest member
kentcasinohelpWilhemina

Latest Threads

Top