看到扩展的C宏

时间:2009-06-12 07:18:22

标签: c c-preprocessor

如果我想扩展一个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

完全相同的输出

UPDATE2:

使用gcc -E并使用-I选项

传递include目录来解决包含路径问题

13 个答案:

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