如果我想扩展一个C宏,有什么好方法可以做到(除了手动跟踪)?
例如,GTK_WIDGET_SET_FLAGS
,它使用的宏使用宏(或两个)......
我希望看到它以某种方式自动扩展,而不是每一步都在搜索每一个宏。
我试过cpp,但它似乎只做了第一次传递
在:
GTK_WIDGET_SET_FLAGS(obj, 13)
我扩展了包含文件,然后:
G_STMT_START{ ((GTK_OBJECT_FLAGS (obj)) |= (13)); }G_STMT_END
这可以通过这些错误消息解释我在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在其中搜索呢?
btw,gcc -E
提供与cpp
使用gcc -E并使用-I选项
传递include目录来解决包含路径问题答案 0 :(得分:57)
根据您使用的编译器,应该有一种方法可以在preprocessor(完成宏扩展,编译器完全不知道宏)之后查看代码。
使用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;
}
答案 1 :(得分:11)
在Visual Studio中,您可以生成预处理器生成的翻译单元文件。您可以使用项目选项,C / C ++ /预处理器并将“生成预处理文件”或“预处理到文件”置于是(或使用/ P或/ EP编译器开关以包含行号或不包括行号)。
答案 2 :(得分:9)
您可以在运行时转储宏的扩展,如下所示:
#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;
}
程序打印:
'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>)'
然而,这仅产生完整扩展。如果您需要单个步骤,Eclipse / CDT可以提供帮助,但前提是您教它使用的所有头文件和编译器标志。
答案 3 :(得分:4)
gcc -E myfile.c
答案 4 :(得分:3)
如果你使用gcc,你也可以运行
cpp myfile.c
答案 5 :(得分:3)
当鼠标指针悬停在标识符上(或其他方式)时,许多IDE将在编辑器中显示宏的扩展版本。我知道Eclipse / CDT会这样做,而Visual Studio会这样做(至少VS 2008会这样做)。
如果您正在追踪一个棘手的问题,让编译器生成预处理输出会非常有用,但是对于日常使用,您只想知道屏幕上的代码是什么,使用IDE是要走的路。
答案 6 :(得分:3)
gcc,即使-E需要头文件的路径......比如-I _path_to_your_headers ......
如果你有一个Makefile,一般来说,你可以做的是用gcc -E覆盖CC
通常,cpp只是一个脚本,为预处理器的gcc添加一些标志,就像传统的......
答案 7 :(得分:2)
您只想运行编译器的预处理器阶段,负责扩展宏。对于gcc
,那是“gcc -E”,但我不确定其他编译器。
答案 8 :(得分:2)
尝试在源文件上运行cpp
答案 9 :(得分:1)
您是否尝试多次运行gcc -E,直到不再有任何宏?
答案 10 :(得分:1)
海湾合作委员会-save-temps
与-E
相比,此选项的最大优势在于,很容易将其添加到任何构建脚本中,而不会干扰构建本身。
操作时:
gcc -save-temps -c -o main.o main.c
main.c
#define INC 1
int myfunc(int i) {
return i + INC;
}
现在,除了正常输出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;
}
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
如果要处理大量文件,请考虑改用:
-save-temps=obj
将中间文件保存到与-o
对象输出相同的目录中,而不是当前的工作目录中,从而避免了潜在的基名冲突。
关于此选项的另一个很酷的事情是,如果您添加-v
:
gcc -save-temps -c -o main.o -v main.c
它实际上显示了/tmp
下使用的显式文件而不是难看的临时文件,因此很容易确切地知道发生了什么,其中包括预处理/编译/组装步骤:
/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
在Ubuntu 19.04 amd64,GCC 8.3.0中进行了测试。
答案 11 :(得分:0)
当陷入粗略的IDE时,请尝试类似
的内容#define DISPLAY_VALUE2(x) #x
#define DISPLAY_VALUE(x) DISPLAY_VALUE2(x)
#pragma message("#DEFINE F_CPU " DISPLAY_VALUE(F_CPU))
生产
…/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))
^
感谢&#34; mdematos&#34;在my related answer
答案 12 :(得分:0)
基本上这是我的字符串化宏:
#define stringify(exp) #exp
#
是一个预处理运算符,它可以用简单的词来制作字符串,所以
stringify(foo)
会给你 "foo"
。
但是如果你在另一个像 #define FOO some_expression
这样的宏上使用它,它只会扩展为 "FOO"
(那个宏的名称),因为它还没有扩展。< /p>
这就是为什么我有一个特殊的宏,先将它展开然后再通过那个特殊的宏:
#define stringify_m(macro) stringify(macro)
现在,如果我们采用这个稍微复杂一点的宏:
#define _padding_(size, id) char _padding##id##_ [((size) + sizeof(char) - 1) / sizeof(char)]
然后像这样输入stringify_m
:
stringify_m(_padding_(8, 6502))
结果是:
"char _padding6502_ [((8) + sizeof(char) - 1) / sizeof(char)]"