Behaviour of #if

D

David White

What is the correct compiler response to the code below? Mine compiles this
happily and silently (with the #if evaluating to true, naturally). An error
would be helpful.

// MONITOR_CORE.CPP
// oops, forgot to #include header that #defines
// constants REACTOR_TYPE & REACTOR_OLD_MODEL

void MonitorReactorCore()
{
#if REACTOR_TYPE == REACTOR_NEW_MODEL

// nothing to do
// monitored by hardware, with auto-shutdown

#else // REACTOR_OLD_MODEL

if(CoreOverTemperature())
ShutDownReactor();

#endif

// more monitoring code
}

David

P.S. I could not locate the answer in "The C++ Programming Language" (3rd
ed.)
 
N

Nils Petter Vaskinn

What is the correct compiler response to the code below? Mine compiles this
happily and silently (with the #if evaluating to true, naturally). An error
would be helpful.

// MONITOR_CORE.CPP
// oops, forgot to #include header that #defines
// constants REACTOR_TYPE & REACTOR_OLD_MODEL

#ifndef REACTOR_TYPE
#error REACTOR_TYPE not defined
#endif
void MonitorReactorCore() [snip]

P.S. I could not locate the answer in "The C++ Programming Language" (3rd
ed.)

page 813


hth
NPV
 
P

Paul Mensonides

David said:
What is the correct compiler response to the code below? Mine
compiles this happily and silently (with the #if evaluating to true,
naturally). An error would be helpful.
#if REACTOR_TYPE == REACTOR_NEW_MODEL

The preprocessor will convert any identifiers to 0 that remain after macro
expansion but before the expression is evaluated. So, if the names aren't
macros, you get an expression like this:

#if 0 == 0

Regards,
Paul Mensonides
 
C

Cy Edmunds

David White said:
What is the correct compiler response to the code below? Mine compiles this
happily and silently (with the #if evaluating to true, naturally). An error
would be helpful.

// MONITOR_CORE.CPP
// oops, forgot to #include header that #defines
// constants REACTOR_TYPE & REACTOR_OLD_MODEL

void MonitorReactorCore()
{
#if REACTOR_TYPE == REACTOR_NEW_MODEL

// nothing to do
// monitored by hardware, with auto-shutdown

#else // REACTOR_OLD_MODEL

if(CoreOverTemperature())
ShutDownReactor();

#endif

// more monitoring code
}

David

P.S. I could not locate the answer in "The C++ Programming Language" (3rd
ed.)


This illustrates yet another reason why macro programming should be kept to
a minimum. If the logic had been implemented using typed data this couldn't
have happened. For instance:

void MonitorReactorCore(reactor_enum reactor_type)
{
if (reactor_type == REACTOR_OLD_MODEL)
if (CoreOverTemperature())
ShutDownReactor();
// more monitoring code
}

If either reactor_enum or REACTOR_OLD_MODEL are not defined because of a
missing header you will get a compile time error.

And BTW, why is everybody so afraid of arguments in a function? Or return
results? Don't get me started...
 
D

David White

Paul Mensonides said:
The preprocessor will convert any identifiers to 0 that remain after macro
expansion but before the expression is evaluated. So, if the names aren't
macros, you get an expression like this:

#if 0 == 0

Well, that's horrifying. Why 0, and not void (producing "#if == ", which
surely is an error)? It's hard to believe that anyone could argue that this
is desirable, but that's something to put in comp.std.c++.

Thanks.

David
 
D

David White

Nils Petter Vaskinn said:
#ifndef REACTOR_TYPE
#error REACTOR_TYPE not defined
#endif

The trouble is, if I remembered to put this in every source file, I would
remember to #include the right header file, which would make the above
redundant.

Thanks.

David
 
R

Rolf Magnus

David said:
Well, that's horrifying. Why 0, and not void (producing "#if == ",
which surely is an error)?

The preprocessor doesn't know anything about types.
 
D

David White

Rolf Magnus said:
The preprocessor doesn't know anything about types.

I didn't mean 'void' in the C++ language-proper sense. I just meant nothing,
instead of assuming that an unseen symbol is an integer with value 0.

David
 
P

Pete Becker

David said:
Since I can't trust the preprocessor, I'll probably do the equivalent of
this:

You can trust the preprocessor. It will do what it's specification says
it does. If you need to determine whether a symbol has been defined use
the defined operator:

#if defined(X) && X == 0
 
P

Paul Mensonides

David said:
Well, that's horrifying. Why 0, and not void (producing "#if == ",
which surely is an error)? It's hard to believe that anyone could
argue that this is desirable, but that's something to put in
comp.std.c++.

It is desirable for situations like "#if SYMBOL" where SYMBOL might not be
defined, e.g.

#if __cplusplus

#endif

Granted, this can be rewritten as #ifdef __cplusplus, #if defined __cplusplus,
or #if defined(__cplusplus). However, the point being that a symbol, if it has
no _other_ meaning (i.e. is not defined), is the logical equivalent of 0 in an
expression.

Regarding your "REACTOR_TYPE == REACTOR_NEW_MODEL" issue where you want it to
verify the definition for you: change the object-like macros to function-like
macros, which will yield an error like you want:

#define REACTOR_TYPE() // ...
#define REACTOR_NEW_MODEL() // ...

// ...

#if REACTOR_TYPE() == REACTOR_NEW_MODEL()

That way, if the symbols are not defined, you'll get an ill-formed expression:

#if 0() == 0()

Regards,
Paul Mensonides
 
P

Paul Mensonides

Paul said:
It is desirable for situations like "#if SYMBOL" where SYMBOL might
not be defined, e.g.

#if __cplusplus

#endif

Sorry, slight clarification. It is useful for situations like "#if SYMBOL"
where SYMBOL might be one of three things: zero, non-zero, or not defined.
This is less verbose then doing this:

#if defined(SYMBOL) && SYMBOL

Regards,
Paul Mensonides
 
D

David White

Pete Becker said:
You can trust the preprocessor. It will do what it's specification says
it does. If you need to determine whether a symbol has been defined use
the defined operator:

#if defined(X) && X == 0

And do what if it's not defined? It has to be one model or the other. If I
can be relied upon to check whether the symbol has been defined, then I can
be relied upon to include the header in which the symbols _are_ defined,
obviating the need to check whether they are defined. The basic problem is:
how to prevent a disaster if the programmer forgets to include the symbol
definitions (or forgets to check whether they have been defined)?

It is a little surprising that, since C++ is built upon rigorous
compile-time checking, it has a preprocessor that assumes that an undefined
symbol has a value of 0, and that this is what the programmer wants.
(Whether you like or not - and many don't - the preprocessor IS part of the
language).

David

P.S. Switching to a const enum presents its own difficulties. Where before I
had:

#if REACTOR_TYPE == REACTOR_NEW_MODEL

const int Const1 = 1;
const int Const2 = 2;

#else

const int Const1 = 3;
const int Const2 = 4;

#endif

now I have:

const int Const1 = (ReactorType == ReactorNewModel) ? 1 : 3;
const int Const2 = (ReactorType == ReactorNewModel) ? 2 : 4;

I only hope I don't have to add three more model types.

David
 
D

David White

Paul Mensonides said:
It is desirable for situations like "#if SYMBOL" where SYMBOL might not be
defined, e.g.

#if __cplusplus

#endif

Granted, this can be rewritten as #ifdef __cplusplus, #if defined __cplusplus,
or #if defined(__cplusplus). However, the point being that a symbol, if it has
no _other_ meaning (i.e. is not defined), is the logical equivalent of 0 in an
expression.

Regarding your "REACTOR_TYPE == REACTOR_NEW_MODEL" issue where you want it to
verify the definition for you: change the object-like macros to function-like
macros, which will yield an error like you want:

#define REACTOR_TYPE() // ...
#define REACTOR_NEW_MODEL() // ...

// ...

#if REACTOR_TYPE() == REACTOR_NEW_MODEL()

That way, if the symbols are not defined, you'll get an ill-formed expression:

#if 0() == 0()

Thanks. That might be the answer I'm looking for.

David
 
D

David White

David White said:
Thanks. That might be the answer I'm looking for.

.... as long as no one forgets the (), I just realized. Still, it's much
better.

David
 
P

Pete Becker

David said:
It has to be one model or the other. If I
can be relied upon to check whether the symbol has been defined, then I can
be relied upon to include the header in which the symbols _are_ defined,
obviating the need to check whether they are defined. The basic problem is:
how to prevent a disaster if the programmer forgets to include the symbol
definitions (or forgets to check whether they have been defined)?

Oh, I see: you don't trust other programmers, so you want to control
what they do. Sorry, won't work. Document what your code is designed to
do and how to use it. That's where your job ends.
It is a little surprising that, since C++ is built upon rigorous
compile-time checking, it has a preprocessor that assumes that an undefined
symbol has a value of 0, and that this is what the programmer wants.

Whatever the language does, someone will want to do something
differently. Work with what's there.
 
D

David White

Pete Becker said:
Oh, I see: you don't trust other programmers, so you want to control
what they do.

No, I had myself in mind actually. But now that you mention it, I don't
trust other programmers either. If programmers could be trusted, compilers
would not need error messages.

David
 
N

Nils Petter Vaskinn

The trouble is, if I remembered to put this in every source file, I
would remember to #include the right header file, which would make the
above redundant.

I don't know which compiler you use but gcc-s preprosessor has an option
(-Wundef) to warn you when an undefined identifier is used (and expanded
to 0) in an #if.

Read you preprosesors documentation, maybe your has a similar option and
can provide a warning for you.

hth
NPV
 
D

David White

Nils Petter Vaskinn said:
I don't know which compiler you use but gcc-s preprosessor has an option
(-Wundef) to warn you when an undefined identifier is used (and expanded
to 0) in an #if.

Read you preprosesors documentation, maybe your has a similar option and
can provide a warning for you.

Good idea. I think it should default to that, but I'll happily take it if
it's there at all.

DW
 

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,614
Members
45,292
Latest member
EttaCasill

Latest Threads

Top