Reputation: 166132
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.
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
The include path problem is solved by using gcc -E and passing the include directory with the -I option
Upvotes: 77
Views: 88254
Reputation: 382762
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:
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
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
Reputation: 740
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"
.
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.
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)
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
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
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
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
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
Reputation: 63825
Have you tried running gcc -E multiple times until there are no longer any macros?
Upvotes: 1
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
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
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