hasen
hasen

Reputation: 166132

Seeing expanded C macros

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

Upvotes: 77

Views: 88254

Answers (14)

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.

Visual Studio Code mouse hover

Hovering over macros automatically expands them, it tends to work very well! Not sure if proper cross file referencing is needed or not, for that I normally use clangd: VSCode "go to definition" not working

Example from the Git source code file remote.c on vscode 1.87.1, C/C++ extension v1.19.6, Ubuntu 23.10 after opening the source directory directly as:

git clone https://github.com/git/git
cd git
git checkout e09f1254c54329773904fe25d7c545a1fb4fa920
code .

as I hover over the ALL_REV_FLAGS macro:

enter image description here

I can then also select text on the hover popup, which contains:

#define ALL_REV_FLAGS (((1u<<11)-1) | NOT_USER_GIVEN | TRACK_LINEAR | PULL_MERGE)

Expands to:

(((1u<<11)-1) | (1u<<25) | (1u<<26) | (1u<<15))

So we see that it gives both the toplevel expansion in terms of other macros, as well as the full final recursive expansion.

Also in this case we see that it worked across files, as in this case the definition comes from file revision.h.

Upvotes: 15

Rahav
Rahav

Reputation: 1855

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.

Upvotes: 0

WENDYN
WENDYN

Reputation: 740

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

Upvotes: 1

Devon
Devon

Reputation: 1093

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

Upvotes: 1

Cătălin Pitiș
Cătălin Pitiș

Reputation: 14341

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

Upvotes: 16

not-a-user
not-a-user

Reputation: 4327

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.

Upvotes: 19

LB40
LB40

Reputation: 12331

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

Upvotes: 7

Earlz
Earlz

Reputation: 63825

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

Upvotes: 1

Michael Burr
Michael Burr

Reputation: 340198

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.

Upvotes: 6

Alex Brown
Alex Brown

Reputation: 42872

Try running cpp on your source file

Upvotes: 4

qrdl
qrdl

Reputation: 34967

If you use gcc you can also run

cpp myfile.c

Upvotes: 4

Markus Schnell
Markus Schnell

Reputation: 1095

gcc -E myfile.c

Upvotes: 11

Andrew Jaffe
Andrew Jaffe

Reputation: 27077

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.

Upvotes: 3

unwind
unwind

Reputation: 399803

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

Upvotes: 91

Related Questions