Reputation: 1977
I came across some code the other day that was similar to the following (the following has been over-simplified for the sake of brevity):
config.h
#ifndef __CONFIG__
#define __CONFIG__
#define DEVELOPMENT_BLD _TRUE_
#if (DEVELOPMENT_BLD == _TRUE_)
#define FILE_EXT ".dev"
#else
#define FILE_EXT ".bin"
#endif
#define PROJECT_STRING "my_project"
#define FILE_NAME PROJECT_STRING FILE_EXT
/* Common include files */
#include "my_defs.h"
#endif /* __CONFIG__ */
my_defs.h
#ifndef __MY_DEFS__
#define __MY_DEFS__
#define _TRUE_ 1
#endif /* __MY_DEFS__ */
The project had always compiled without any issues, but since I made some minor changes (and the actual project was rather large) I decided to run Lint on it. When I did, I received the following error:
Warning 553: Undefined preprocessor variable '_TRUE_', assumed 0
I then wondered why the compiler didn't catch that _TRUE_
is defined in my_defs.h which is included after the macro's first usage. So I compiled it on a different compiler with the same results - succesful compilation, no warnings and FILE_NAME
was correctly evaluated regardless of how I set DEVELOPMENT_BLD
(using _TRUE_
or !_TRUE_
). Here are my two compiler settings:
ArmCC -c -cpu Cortex-M3 -g -O0 --apcs=interwork -I "..\ARM\CMSIS\Include" -I "..\ARM\INC\NXP\LPC17xx" -o "file.o" --omf_browse "file.crf" --depend "file.d" "file.c"
mingw32-gcc.exe -pedantic -Wall -g -c D:\dev\practice\header_question\main.c -o obj\Debug\main.o
I decided to run a simple test to see if the value of FILE_NAME
was being properly evaluated by the preprocessor. I also wanted to see what the value of DEVELOPMENT_BLD
actually was. I ran the following code two times:
main.c
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("FILE_NAME:%s, WHAT_IS_TRUE:%d", FILE_NAME,DEVELOPMENT_BLD);
return 0;
}
The first time I used the value #define DEVELOPMENT_BLD _TRUE_
with this result:
FILE_NAME:my_project.dev, WHAT_IS_TRUE:1
The second time I used the value #define DEVELOPMENT_BLD !_TRUE_
with this result:
FILE_NAME:my_project.bin, WHAT_IS_TRUE:0
My first thought was that perhaps _TRUE_
was being defined elsewhere - so just to be sure I commented out #include "my_defs.h"
. I then began to receive a compiler error:
error: '_TRUE_' undeclared (first use in this function)
All of that leads to my question. Are #include statements required to be evaluated by the preprocessor before macro expansion or did I just get lucky?
Upvotes: 3
Views: 1140
Reputation: 754570
The C pre-processor acts on directives as it encounters them. In this context, the warning is correct; at the time you use #if DEVELOPMENT_BUILD == _TRUE_
, the effective value of _TRUE_
is zero. However, because of the #define DEVELOPMENT_BUILD _TRUE_
definition, the preprocessor is evaluating #if 0 == 0
, which is true. However, you'd have had the same result if you'd specified #define DEVELOPMENT_BUILD _FALSE_
because _FALSE_
would also be implicitly 0 and hence the test would be #if 0 == 0
again (which also evaluates to true). If, when the preprocessor has finished evaluating expressions in the #if
condition, there are identifiers left over, they are implicitly assumed to be 0.
Note that names starting with an underscore and a capital letter or another underscore are reserved for any use by the implementation. You are treading on very thin ice with your choice of names such as _TRUE_
and __CONFIG__
. (Just because system headers use names like that is not a good reason for you to do so — in fact, quite the opposite. The system headers are carefully keeping out of the namespace reserved for you to use; you should keep out of the namespace reserved for the system.)
Upvotes: 4