如果我想扩展一个C宏,有什么好方法可以做到这一点(除了手动跟踪)?
例如,GTK_WIDGET_SET_FLAGS
它使用一个使用宏的宏(或两个)......
我希望看到它以某种方式自动扩展,而不是搜索每一个宏,每一步.
我试过cpp,但似乎只做了第一次传球
上:
GTK_WIDGET_SET_FLAGS(obj, 13)
Run Code Online (Sandbox Code Playgroud)
我扩展了包含文件,然后:
G_STMT_START{ ((GTK_OBJECT_FLAGS (obj)) |= (13)); }G_STMT_END
Run Code Online (Sandbox Code Playgroud)
这是由这些错误消息解释我在stderr上得到这个(当使用-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
gtk,atk和gdk目录都在当前的工作目录中,那么如何让cpp在其中搜索呢?
顺便说一句,gcc -E
给出完全相同的输出cpp
通过使用gcc -E并使用-I选项传递include目录来解决包含路径问题
unw*_*ind 62
根据您使用的编译器,应该有一种方法可以在预处理器(完成宏扩展,编译器根本不知道宏)之后查看代码.
使用gcc,选项是-E.这是一个简化的例子,使用玩具代码而不是实际的GTK +宏:
~/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;
}
Run Code Online (Sandbox Code Playgroud)
Căt*_*tiș 13
在Visual Studio中,您可以生成预处理器生成的翻译单元文件.您可以使用项目选项,C/C++ /预处理器并将"生成预处理文件"或"预处理到文件"置于是(或使用/ P或/ EP编译器开关以包含行号或不包括行号).
Cir*_*四事件 12
海湾合作委员会-save-temps
该选项的一大优点-E
是可以很容易地将其添加到任何构建脚本中,而不会过多干扰构建本身:
gcc -save-temps -c -o main.o main.c
Run Code Online (Sandbox Code Playgroud)
主程序
#define INC 1
int myfunc(int i) {
return i + INC;
}
Run Code Online (Sandbox Code Playgroud)
现在,除了正常的输出之外main.o
,当前工作目录还包含以下文件:
main.i
是一个包含所需的预处理文件:
# 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;
}
Run Code Online (Sandbox Code Playgroud)
main.s
是一个额外的好处,并且包含所需的生成程序集:
.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
Run Code Online (Sandbox Code Playgroud)
文档:https ://gcc.gnu.org/onlinedocs/gcc/Developer-Options.html#index-save-temps
-save-temps=obj
如果您想对大量文件执行此操作,请考虑改用:
gcc -save-temps -c -o main.o main.c
Run Code Online (Sandbox Code Playgroud)
它将中间文件保存到与-o
对象输出相同的目录而不是当前工作目录,从而避免潜在的基本名称冲突。
例如:
gcc -save-temps -c -o out/subdir/main.o subdir/main.c
Run Code Online (Sandbox Code Playgroud)
导致文件的创建:
out/subdir/main.i
out/subdir/main.o
out/subdir/main.s
Run Code Online (Sandbox Code Playgroud)
显然,苹果公司正在密谋占领世界。
此选项的优点-E
是可以轻松地将其添加到任何构建脚本中,而不会过多干扰构建本身。
-save-temps -v
关于此选项的另一个很酷的事情是如果您添加-v
:
#define INC 1
int myfunc(int i) {
return i + INC;
}
Run Code Online (Sandbox Code Playgroud)
它实际上显示了正在使用的显式文件,而不是下的丑陋的临时文件/tmp
,因此很容易准确地知道发生了什么,其中包括预处理/编译/组装步骤:
# 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;
}
Run Code Online (Sandbox Code Playgroud)
在 Ubuntu 19.04 (Disco Dingo) amd64、GCC 8.3.0 中测试。
您可以在运行时转储宏的扩展,如下所示:
#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;
}
Run Code Online (Sandbox Code Playgroud)
程序打印:
'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>)'
Run Code Online (Sandbox Code Playgroud)
然而,这仅产生完全扩展.如果您需要单个步骤,Eclipse/CDT可以提供帮助,但前提是您教授它使用的所有头文件和编译器标志.
当鼠标指针悬停在标识符上(或其他方式)时,许多 IDE 会在编辑器中显示宏的扩展版本。我知道 Eclipse/CDT 可以做到这一点,Visual Studio 也可以做到这一点(至少 VS 2008 可以做到)。
如果您正在追踪一个棘手的问题,让编译器生成预处理的输出可能会很有用,但对于日复一日的使用,您只想知道屏幕上的代码发生了什么,使用 IDE 是解决问题的方法。去。
gcc 即使使用 -E 也需要头文件的路径......比如 -I _path_to_your_headers ...
如果你有一个 Makefile,通常你可以做的是用 gcc -E 覆盖 CC
通常,cpp 只是一个脚本,为预处理器向 gcc 添加一些标志,就像传统的...