embedded_guy
embedded_guy

Reputation: 1977

Are #include directives processed prior to macro expansion regardless of their location within a file?

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

Answers (1)

Jonathan Leffler
Jonathan Leffler

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

Related Questions