看到扩展的C宏

has*_*sen 47 c c-preprocessor

如果我想扩展一个C宏,有什么好方法可以做到这一点(除了手动跟踪)?

例如,GTK_WIDGET_SET_FLAGS它使用一个使用宏的宏(或两个)......

我希望看到它以某种方式自动扩展,而不是搜索每一个宏,每一步.

UPDATE

我试过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

UPDATE2:

通过使用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)

  • 您可以使用`-E -dD`在输出中包含宏定义. (14认同)
  • 重要说明:您还需要传递gcc与正常编译期间相同的-I标志,以便它可以找到正确的头文件. (2认同)

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 中测试。


not*_*ser 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;
}
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可以提供帮助,但前提是您教授它使用的所有头文件和编译器标志.


Mic*_*urr 6

当鼠标指针悬停在标识符上(或其他方式)时,许多 IDE 会在编辑器中显示宏的扩展版本。我知道 Eclipse/CDT 可以做到这一点,Visual Studio 也可以做到这一点(至少 VS 2008 可以做到)。

如果您正在追踪一个棘手的问题,让编译器生成预处理的输出可能会很有用,但对于日复一日的使用,您只想知道屏幕上的代码发生了什么,使用 IDE 是解决问题的方法。去。

  • 我的 VS 2008 不会在悬停时展开宏。我必须在某处打开此选项吗? (2认同)

LB4*_*B40 6

gcc 即使使用 -E 也需要头文件的路径......比如 -I _path_to_your_headers ...

如果你有一个 Makefile,通常你可以做的是用 gcc -E 覆盖 CC

通常,cpp 只是一个脚本,为预处理器向 gcc 添加一些标志,就像传统的...