6

I want to use macro parameter like this:

  #define D(cond,...) do{         \
    #if cond                      \
    #define YYY 1                 \
    #else                         \
    #define YYY 0                 \
  } while(0)

Is it possible?

UPD
Maybe when sources will be preprocessed twice: gcc -E source.c | gcc -xc - next will work:

#define D(cond,...) #define YYY cond&DEBUG
#if YYY
#define D(...) printf( __VA_ARGS__ )
#else
#define D(...)
#endif
Eugen Konkov
  • 22,193
  • 17
  • 108
  • 158

6 Answers6

6

This is not possible. Read about the GNU cpp preprocessor and the C11 standard (i.e. n1570), and check here. The C preprocessor is (conceptually at least) run before the rest of the compiler (which gets the preprocessed form of your translation unit). BTW, for a file foo.c you could use gcc -C -E foo.c > foo.i (using GCC) to get inside foo.i its preprocessed form, and you can inspect that foo.i -since it is a textual file- with a pager or an editor.

However, a .c file can be generated (generating C code is a common practice, at least since the 1980s; for example with yacc, bison, rpcgen, swig, ....; many large software projects use specialized generators of C or C++ code...). You might consider using some other tool, perhaps the GPP preprocessor (or GNU m4) or some other program or script, to generate your C file (from something else). Look also into autoconf (it might have goals similar to yours).

You may want to configure your build automation tool for such purpose, e.g. edit your Makefile for GNU make.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
6

No, because C 2011 [N1570] 6.10.3.4 3 says, about macro replacement, “The resulting completely macro-replaced preprocessing token sequence is not processed as a preprocessing directive even if it resembles one,…”

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
4

No, this is not possible.

During translation, all preprocessing directives (#define, #include, etc.) are executed before any macro expansion occurs, so if a macro expands into a preprocessing directive, it won't be interpreted as such - it will be interpreted as (invalid) source code.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • If all preprocessing directives were executed before any macro expansion, `#if MyPreprocessorSymbol == 3` would not work. You have to expand the preprocessor symbol before evaluating the `#if`. You can even use preprocessor macros in a `#include` statement, per C 2011 [N1570] 6.10.2 4. – Eric Postpischil Jan 24 '18 at 22:57
3

As pointed out by others this is not possible but there is a work around:

int YYY;
/* global scope variables are sometimes considered bad practice... */
#define D(cond,...) do{         \
  if (cond) {                   \
  YYY = 1;                      \ 
  }                             \
  else {                        \
  YYY = 0;                      \
  }                             \
} while(0)

Use an optimizing flag (ex: gcc/clang -O3) and it will replace the dead code as if it was a macro. Obviously you may want to change the type of YYY but you seem to use it like a boolean.

ft_error
  • 132
  • 1
  • 6
  • Hi. I try your example, but it always generate `if(0){ ...` despite on first parameter to `D`: `D(1, "some string" )`. Also I find interesting trick: we can preprocess sources twice: `gcc -E source.c | gcc -xc -` – Eugen Konkov Jan 25 '18 at 09:03
  • This is a really nice trick! I edited my answer, i am sorry i did't understand you wanted to use cond as a literal, i thought it was a define condition... Now this should work. – ft_error Jan 25 '18 at 22:38
  • yes, it should be define condition. and when `cond` is 0 no code should be generated. – Eugen Konkov Jan 26 '18 at 11:08
2

No, you cannot. The C preprocessor cannot know what is going to occur during runtime.

The preprocessor goes through the program before it is even compiled and replaces every macro defined with its assigned value.

ack
  • 1,181
  • 1
  • 17
  • 36
  • This does not involve preprocessing after translation. The idea in the question is that the D macro would be expanded somewhere later in the source, and, after that expansion, the replacement text would then be rescanned for preprocessor directives. This would all occur during preprocessing. – Eric Postpischil Jan 24 '18 at 23:01
  • @EricPostpischil: The interesting trick is to preprocess sources twice: `gcc -E source.c | gcc -xc -` – Eugen Konkov Jan 25 '18 at 09:06
  • @EugenKonkov: That does not work on the first code sequence in the question because `\` followed by new-line is deleted during translation phase 2, before preprocessing, so the resulting `D` macro does not have new-line characters in its replacement text, so the expanded preprocessing directives are not on separate lines. It does not work on the first or the second code sequence because `#` is an operator in macro replacement (it stringifies) and must be followed by a macro parameter, so `#if` is illegal. – Eric Postpischil Jan 25 '18 at 15:41
1

This is some poor man's code generation, for when integrating another tool to the project is overkill.

Define a macro like this, expanding for your needs:

#define NESTED              /* Comment out instead of backslash new lines.
*/                          /*
*/  UNDEF   REPLACED        /*
*/                          /*
*/  IFDEF   CONDITION       /*
*/  DEFINE  REPLACED  1     /*
*/  ELSE                    /*
*/  DEFINE  REPLACED  0     /*
*/  ENDIF

Your version of NESTED can be a function-like macro, and REPLACED can have a more elaborated body.

Leave CONDITION and the directive named macros without a definition.

DEFINE CONDITION to control which value NESTED gets on compilation, similarly to normal #ifdef usage:

DEFINE  CONDITION
NESTED
int i = REPLACED;        //i == 1

UNDEF  CONDITION
NESTED
int z = REPLACED;        //z == 0

Source code that uses NESTED and the other macros will not compile. To generate a .c or .cpp file that you can compile with your chosen options, do this:

gcc   -E -CC   source.c  -o temporary.c
gcc   -E                                                  \
   -DDEFINE=\#define  -DUNDEF=\#undef                     \
   -DIFDEF=\#ifdef    -DELSE=\#else    -DENDIF=\#endif    \
   temporary.c  -o usableFile.c

rm temporary.c     #remove the temporary file

-E means preprocess only, not compile. The first gcc command expands NESTED and all normally defined macros from the source. As DEFINE, IFDEF, etc. are not defined, they and their future arguments remain as literal text in the temporary.c file.

-CC makes the comments be preserved in the output file. After the preprocessor replaces NESTED by its body, temporary.c contains the directive macros in separate lines, with the comments. When the comments are removed on the next gcc command, the line breaks remain by the standard.

# is accepted in the body of a macro that takes no arguments. However, unlike macros, directives are not rescaned and executed on expansion, so you need another preprocessor pass to make nested defines work. All preprocessing related to the delayed defines needs to be delayed too, and made available to the preprocessor at once. Otherwise, directives and arguments needed in a later pass are consumed and removed from the code in a previous one.

The second gcc command replaces the -D macros by the delayed directives, making all of them available to the preprocessor starting on the next pass. The directives and their arguments are not rescaned in the same gcc command, and remain as literal text in usableFile.c.

When you compile usableFile.c, the preprocessor executes the delayed directives.

really
  • 93
  • 7