Seeing expanded C macros

Viewed 72318

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

14 Answers

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;
}

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).

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...

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.

When you do:

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
    

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.

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 amd64, GCC 8.3.0.

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.

If you use gcc you can also run

cpp myfile.c

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.

Try running cpp on your source file

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

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)]"

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

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.

Related