74

If I want to expand a C macro, what are some good ways to do that (besides tracing it manually)?

For instance, GTK_WIDGET_SET_FLAGS, it uses a macro that uses a macro that uses a macro (or two) ...

I want to just see it somehow expanded automagically, instead of searching for every macro, every step of the way.

UPDATE

I tried cpp, but it seemed to only do the first pass

on:

GTK_WIDGET_SET_FLAGS(obj, 13)

I got the include file expanded, and then:

G_STMT_START{ ((GTK_OBJECT_FLAGS (obj)) |= (13)); }G_STMT_END

This is explained by these error message I get this on stderr (when using -o filename)

gtk/gtkwidget.h:34:21: gdk/gdk.h: No such file or directory
gtk/gtkwidget.h:35:31: gtk/gtkaccelgroup.h: No such file or directory
gtk/gtkwidget.h:36:27: gtk/gtkobject.h: No such file or directory
gtk/gtkwidget.h:37:31: gtk/gtkadjustment.h: No such file or directory
gtk/gtkwidget.h:38:26: gtk/gtkstyle.h: No such file or directory
gtk/gtkwidget.h:39:29: gtk/gtksettings.h: No such file or directory
gtk/gtkwidget.h:40:21: atk/atk.h: No such file or directory

the gtk, atk, and gdk directories are all in the current working directory, so how do I let cpp search in it?

btw, gcc -E gives the exact same output as cpp

Update2:

The include path problem is solved by using gcc -E and passing the include directory with the -I option

Community
  • 1
  • 1
hasen
  • 161,647
  • 65
  • 194
  • 231
  • Try running cop on your source file. – Alex Brown Jun 12 '09 at 07:20
  • Do not try to use cpp directly - it has a number of gotchas. As others have suggested use the -E flag of gcc. –  Jun 12 '09 at 08:14
  • 3
    As folks have said, use gcc. Also, like always when building with GTK+, you need to tell the compiler where to find the include files. For GTK+, this is done using pkg-config, like so: "gcc -E $(pkg-config --cflags gtk+-2.0) myfile.c". – unwind Jun 12 '09 at 09:30
  • Related: https://stackoverflow.com/questions/3742822/preprocessor-output – Ciro Santilli OurBigBook.com Apr 28 '23 at 07:02

14 Answers14

88

Depending on which compiler you use, there should be a way to see the code after the preprocessor (which does the macro expansion, macros are not known by the compiler at all) is done.

With gcc, the option is -E. Here's a simplified example, using toy code and not the actual GTK+ macro:

~/tmp> cat cpptest.c
#define SET_FLAGS(w, f) ((w)->flags |= (f))

int main(void)
{
        SET_FLAGS(0, 4711);

        return 0;
}
~/tmp> gcc -E cpptest.c
# 1 "cpptest.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "cpptest.c"


int main(void)
{
 ((0)->flags |= (4711));

 return 0;
}
unwind
  • 391,730
  • 64
  • 469
  • 606
  • 2
    Important note: you also need to pass gcc the same -I flags that you would during normal compilation so that it can find the proper header files. – Adam Rosenfield Jun 12 '09 at 15:38
  • 24
    You can use `-E -dD` to include macro definitions in the output. – Gerald Combs May 19 '11 at 23:55
  • 1
    For POSIX-systems without Gcc: POSIX specifies both `-E` and `-I` for [`c99`](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/c99.html). – mafso Sep 28 '14 at 12:57
  • In my case, the output still contains macros. Do you know whether we can expand it until all macros are removed? – yuefengz Oct 27 '14 at 00:18
15

You can dump the expansion of a macro at run time like this:

#include <stdio.h>

/*
 * generic helper macros
 */
#define CALL(macro, arguments) macro arguments
#define STR(...) STR_(__VA_ARGS__)
#define STR_(...) # __VA_ARGS__

/*
 * dumps a macro and its expansion to stdout
 * the second argument is optional and specifies the number of
 * arguments that macro takes: 0 means macro takes zero arguments
 * no second argument means macro is not function-like
 */
#define DUMP_MACRO(macro, ...) \
    do { \
        puts ( \
            "'" \
            # macro STR(DUMP_MACRO_ARGS_ ## __VA_ARGS__) \
            "' expands to '" \
            STR(CALL(macro, DUMP_MACRO_ARGS_ ## __VA_ARGS__)) \
            "'" \
        ); \
    } while (0)
/* helpers for DUMP_MACRO, add more if required */
#define DUMP_MACRO_ARGS_
#define DUMP_MACRO_ARGS_0 ()
#define DUMP_MACRO_ARGS_1 (<1>)
#define DUMP_MACRO_ARGS_2 (<1>, <2>)
#define DUMP_MACRO_ARGS_3 (<1>, <2>, <3>)

/*
 * macros to be used in examples for DUMP_MACRO
 */
#define EXAMPLE ( EXAMPLE0() << 9 )
#define EXAMPLE0() __GNUC__
#define EXAMPLE1(EXAMPLE1) EXAMPLE1
#define EXAMPLE3(EXAMPLE1, _, __) ( EXAMPLE1 ? _(__) : false )

int main() {
    /* examples */
    DUMP_MACRO(EXAMPLE);
    DUMP_MACRO(EXAMPLE0, 0);
    DUMP_MACRO(EXAMPLE1, 1);
    DUMP_MACRO(EXAMPLE3, 3);
    DUMP_MACRO(EXAMPLE3(EXAMPLE, EXAMPLE1, non_macro_symbol));
    /* does not work for DUMP_MACRO itself, because the
       preprocessor does not allow recursion */
    DUMP_MACRO(DUMP_MACRO, 1);
    DUMP_MACRO(DUMP_MACRO, 2);
    return 0;
}

The program prints:

'EXAMPLE' expands to '( 4 << 9 )'
'EXAMPLE0()' expands to '4'
'EXAMPLE1(<1>)' expands to '<1>'
'EXAMPLE3(<1>, <2>, <3>)' expands to '( <1> ? <2>(<3>) : false )'
'EXAMPLE3(EXAMPLE, EXAMPLE1, non_macro_symbol)' expands to '( ( 4 << 9 ) ? non_macro_symbol : false )'
'DUMP_MACRO(<1>)' expands to 'DUMP_MACRO (<1>)'
'DUMP_MACRO(<1>, <2>)' expands to 'DUMP_MACRO (<1>, <2>)'

However this yields only the full expansion. If you need single steps, Eclipse/CDT can help, but only if you teach it all the headers and compiler flags you use.

not-a-user
  • 4,088
  • 3
  • 21
  • 37
15

In Visual Studio, you can generate the preprocessor resulted translation unit file. You can go project options, C/C++/Preprocessor and put "Generate Preprocessed File" or "Preprocess to a File" on Yes (or use /P or /EP compiler switch to include line numbers or not).

marsh
  • 2,592
  • 5
  • 29
  • 53
Cătălin Pitiș
  • 14,123
  • 2
  • 39
  • 62
9

GCC -save-temps

The big advantage of this option over -E is that it is very easy to add it to any build script, without interfering much in the build itself:

gcc -save-temps -c -o main.o main.c

main.c

#define INC 1

int myfunc(int i) {
    return i + INC;
}

and now, besides the normal output main.o, the current working directory also contains the following files:

  • main.i is a contains the desired preprossessed file:

    # 1 "main.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 31 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    # 32 "<command-line>" 2
    # 1 "main.c"
    
    
    int myfunc(int i) {
        return i + 1;
    }
    
  • main.s is a bonus, and contains the desired generated assembly:

        .file   "main.c"
        .text
        .globl  myfunc
        .type   myfunc, @function
    myfunc:
    .LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    -4(%rbp), %eax
        addl    $1, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
    .LFE0:
        .size   myfunc, .-myfunc
        .ident  "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
        .section    .note.GNU-stack,"",@progbits
    

Docs: https://gcc.gnu.org/onlinedocs/gcc/Developer-Options.html#index-save-temps

-save-temps=obj

If you want to do it for a large number of files, consider using instead:

-save-temps=obj

which saves the intermediate files to the same directory as the -o object output instead of the current working directory, thus avoiding potential basename conflicts.

For example:

gcc -save-temps -c -o out/subdir/main.o subdir/main.c

leads to the creation of files:

out/subdir/main.i
out/subdir/main.o
out/subdir/main.s

Clearly an Apple plot to take over the world.

The advantage of this option over -E is that it is easy to add it to any build script, without interfering much in the build itself.

-save-temps -v

Another cool thing about this option is if you add -v:

gcc -save-temps -c -o main.o -v main.c

it actually shows the explicit files being used instead of ugly temporaries under /tmp, so it is easy to know exactly what is going on, which includes the preprocessing / compilation / assembly steps:

/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu main.c -mtune=generic -march=x86-64 -fpch-preprocess -fstack-protector-strong -Wformat -Wformat-security -o main.i
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -fpreprocessed main.i -quiet -dumpbase main.c -mtune=generic -march=x86-64 -auxbase-strip main.o -version -fstack-protector-strong -Wformat -Wformat-security -o main.s
as -v --64 -o main.o main.s

Tested in Ubuntu 19.04 (Disco Dingo) amd64, GCC 8.3.0.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 1
    This works with clang too. Tried it with clang version 12.0.1 on Arch Linux, and it works like a charm. The `save-temps=obj` option is particularly useful to keep the build clean. – Abhishek Chakravarti Aug 21 '21 at 06:31
9
gcc -E myfile.c
Markus Schnell
  • 1,075
  • 1
  • 10
  • 13
7

gcc even with -E needs the path of the header files ... like -I _path_to_your_headers...

If you've a Makefile, generally, what you could do is over-riding CC with gcc -E

Generally, cpp is only a script adding some flags to gcc for the preprocessor, like traditional...

LB40
  • 12,041
  • 17
  • 72
  • 107
6

Many IDEs will show you the expanded version of the macro in the editor when the mouse pointer hovers over the identifier (or some other way). I know Eclipse/CDT does this, and Visual Studio does this (at least VS 2008 does).

Having the compiler generate preprocessed output can be useful if you're tracking down a tricky problem, but for day in/day out use where you just want to know what's going on with the code on your screen,using the IDE is the way to go.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
4

If you use gcc you can also run

cpp myfile.c
qrdl
  • 34,062
  • 14
  • 56
  • 86
4

Try running cpp on your source file

Alex Brown
  • 41,819
  • 10
  • 94
  • 108
3

You want to run just the preprocessor stage of your compiler, responsible for expanding macros. For gcc, that's "gcc -E", but I'm not sure about other compilers.

Andrew Jaffe
  • 26,554
  • 4
  • 50
  • 59
1

Naive Approach

Basically here's my stringification macro:

#define stringify(exp) #exp

# is a preprocessor operator that makes strings in simple words, so stringify(foo) would give you "foo".


Problem

But if you used it on another macro like this #define FOO some_expression, it would just expand into "FOO" (the name of that macro) since it's not expanded yet.


Solution

This is why I have special macro that expands it first and then puts it through that special macro:

#define stringify_m(macro) stringify(macro)

Example

Now if we take this slightly more complex macro:

#define _padding_(size, id) char _padding##id##_ [((size) + sizeof(char) - 1) / sizeof(char)]

and put through stringify_m like this:

stringify_m(_padding_(8, 6502))

the result would be:

"char _padding6502_ [((8) + sizeof(char) - 1) / sizeof(char)]"
WENDYN
  • 650
  • 7
  • 15
1

Have you tried running gcc -E multiple times until there are no longer any macros?

Earlz
  • 62,085
  • 98
  • 303
  • 499
  • No, that will produce something different from what will be actually compiled, because a normal compile runs the preprocessor **exactly** once. Try `echo -e "#define __INTMAX_MAX__() __INTMAX_MAX__\n__INTMAX_MAX__()" | gcc -E - -o- | tee 1.c | gcc -E - -o2.c; diff <(tail -1 1.c) <(tail -1 2.c)`. On Ubuntu 15.04 x86_64 I get `1c1 < __INTMAX_MAX__ --- > 9223372036854775807L`. – not-a-user Jul 16 '15 at 16:42
0

When trapped in a sketchy IDE, try something like

#define DISPLAY_VALUE2(x) #x
#define DISPLAY_VALUE(x) DISPLAY_VALUE2(x)
#pragma message("#DEFINE F_CPU " DISPLAY_VALUE(F_CPU))

to produce

…/sketch_may21a.ino: In function 'void loop()':
…/sketch_may21a.ino:10:54: note: #pragma message: #DEFINE F_CPU 16000000L
#pragma message("#DEFINE F_CPU " DISPLAY_VALUE(F_CPU))
                                                     ^

thanks to "mdematos" at http://MicroChip.com/forums/m724722.aspx

Devon
  • 1,019
  • 9
  • 20
0

On macOS

gcc -E -dD source_file.c

-E Do nothing beyond preprocessing.

-dD Output the #define directives and the result of preprocessing.

Additional recommendations

Example code

#define max(A, B) ((A) > (B) ? (A) : (B))

int main(void) {
   int a = 1, b = 2;
   printf("Between %i and %i, %i is bigger\n", a, b, max(a, b));
   return 0;
}

Since we are only preprocessing we don’t need to #include <stdio.h> to have the printf() function or other libraries. In this example, including it will produce over 4,000 lines of output instead of less than 400 without.

Rahav
  • 1,755
  • 15
  • 18