gcc - 如何在源代码中的特定行检查 GCC 预处理器定义

标签 gcc macros c-preprocessor

有没有办法在源文件中的某个点转储所有当前的预处理器定义? 或者另一种检查源文件中两点之间预处理器指令更改的方法?

我没有找到任何东西 here .这是一个给出这个想法的例子:

#define FOO

#pragma message "defines before whatever.h"
#pragma please_dump_all_defines
#include <whatever.h>
#pragma message "defines after whatever.h"
#pragma please_dump_all_defines

// rest of the file

另一种获取信息的方式也可以使用,例如使用 gcc -E不知何故,只要考虑到 FOO以上可能会影响包含文件的确切定义,并且可以跟踪多个#define/#undef , 等等。

最佳答案

当然,GCC 预处理器不能完全按照您的要求做,但它确实可以
有一个 -dCHARS选项,用于标志组合 CHARS , 那
您可以利用一些脚本来提取
翻译单元中两点之间的预处理器定义。

我将用包含这两个文件的翻译单元进行说明:

foo.c

#define B 1
#define C 2
#pragma message Begin
#include "bar.h"
#pragma message End
#undef B
#undef C
#define B 4
#define C 5 

bar.h
#ifndef BAR_H
#define BAR_H

#undef B
#undef C
#ifdef A
#define B 2
#define C 3
#endif

#endif

调用:
cpp -dD foo.c
-dD选项保留 #define|#undef否则的指令
预处理输出。该输出有 > 500 行,所以我只是
引用有趣的尾部:
# 1 "<command-line>" 2
# 1 "foo.c"
#define B 1
#define C 2

# 3 "foo.c"
#pragma message Begin
# 3 "foo.c"

# 1 "bar.h" 1

#define BAR_H 

#undef B
#undef C
# 5 "foo.c" 2

# 5 "foo.c"
#pragma message End
# 5 "foo.c"

#undef B
#undef C
#define B 4
#define C 5

或者,调用:
cpp -dD -DA foo.c

相应的尾部(带有我的评论)是:
# 1 "<command-line>" 2
# 1 "foo.c"
#define B 1
#define C 2

# 3 "foo.c"
#pragma message Begin
# 3 "foo.c"

# 1 "bar.h" 1

#define BAR_H 

#undef B
#undef C

#define B 2     //<- New with -DA
#define C 3     //<- New with -DA
# 5 "foo.c" 2

# 5 "foo.c"
#pragma message End
# 5 "foo.c"

#undef B
#undef C
#define B 4
#define C 5

与此相关的脚本是:
  • 1) 提取 #define|#undef在起点之前的指令
    标记,#pragma message Begin ,仅保留每个宏名称的最新滚动。
  • 2) 从起点到终点标记#pragma message End重复1) .
  • 3) 按宏名称匹配两组并报告前后差异。

  • 就像你的运气一样,碰巧我需要吹掉我的蜘蛛网(从不
    非常有光泽的)python 用于面试,所以这里有一个脚本(只是粗略地
    调试):

    macrodiff.py
    #!/usr/bin/python
    
    import sys, argparse, os, string, re, subprocess, shlex
    from subprocess import call, CalledProcessError
    
    class macro_directive:
        def __init__(self,directive = None,name = None,definition = None):
            self.__directive = directive
            self.__name = name
            self.__definition = definition
        def __eq__(self,other):
            return  self.__name == other.__name and \
                    self.__directive == other.__directive and \
                    self.__definition == other.__definition
        def __neq__(self,other):
            return not __eq__(self,other)
        @property
        def empty(self):
            return not self.__directive
        @property
        def directive(self):
            return self.__directive
        @property
        def name(self):
            return self.__name
        @property
        def definition(self):
            return self.__definition
    
        @property
        def desc(self):
            desc = self.__directive + ' ' + self.__name
            if self.__definition:
                desc += ' '
                desc += self.__definition
            return desc
    
        @staticmethod
        def read(line):
            match = re.match('^\s*#\s*(define|undef)\s+(\w+) \s*(.*)$',line)
            if match:
                directive = match.group(1)
                name = match.group(2)
                if directive == 'define':
                    return macro_directive(directive,name,match.group(3))
                else:
                    return macro_directive(directive,name)
            else:
                return macro_directive()
        @staticmethod
        def make_dict(lines):
            d = {}
            for line in lines:
                md = macro_directive.read(line);
                if not md.empty:
                    d[md.name] = md
            return d
    
    
    def find_marker(lines,marker):
        for i, line in enumerate(lines):
            if line.find(marker) == 0:
                return i;
        return -1
    
    def split_by_marker(lines,marker):
        mark_i = find_marker(lines,marker)
        if mark_i != -1:
            return [lines[:mark_i],lines[mark_i:]]
        return [[],lines]
    
    parser = argparse.ArgumentParser(
        prog="macrodiff",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description='Extract changes in simple preprocessor macro values between' +
            ' two marked points in a C/C++ translation unit. ' +
            'Function-like macros are not supported')
    parser.add_argument('-s', '--start', metavar='STARTSTR',required=True,
        help='The initial macro values will be those in effect when the' +
            ' first line commencing with STARTSTR is read')
    parser.add_argument('-e', '--end', metavar='ENDSTR',
        help='The final macro values will be those in effect when the first line' +
        ' commencing with ENDSTR is read, or if --end is not given then those' +
        ' in effect at end-of-file ')
    parser.add_argument('--pp', default='cpp -dD',metavar='PP',
        help='PP is the preprocessor command to invoke. Default \'cpp -dD\'')
    parser.add_argument('--ppflags',default='',metavar='PPFLAGS',
        help='PPFLAGS are additional options to be passed to PP')
    parser.add_argument('infile',metavar='FILE',nargs=1,
        help='FILE is a C/C++ source file to be processed')
    
    args = vars(parser.parse_args())
    startstr = args['start'];
    endstr = args['end'];
    stdout = ''
    command = args['pp'] + ' ' + args['ppflags'] + ' ' + args['infile'][0]
    
    try:
        stdout = subprocess.check_output(shlex.split(command))
    except CalledProcessError, e:
        sys.stderr.write( '***Error: Command \"' + command + '\" failed: \"' + \
            e.output + '\": ' + 'syscode = ' + str(e.returncode) + '\n')
        sys.exit(e.returncode)
    lines = stdout.splitlines();
    lines_before,lines_after = split_by_marker(lines,startstr);
    if not lines_before:
        sys.stderr.write( '***Error: STARTSTR \"' + startstr + '\" not found\n')
        sys.exit(1)
    if endstr:
        lines_after, ignore = split_by_marker(lines_after,endstr);
        if not lines_after:
            sys.stderr.write( '***Error: ENDSTR \"' + endstr + '\" not found\n')
            sys.exit(1)
    
    directives_dict_before = macro_directive.make_dict(lines_before)
    directives_dict_after = macro_directive.make_dict(lines_after)
    intersection = \
        directives_dict_before.viewkeys() & directives_dict_after.viewkeys()
    
    for key in intersection:
        before = directives_dict_before[key]
        after = directives_dict_after[key]
        if before != after:
            print 'BEFORE[' + before.desc + '] AFTER[' + after.desc +']'  
    
    sys.exit(0)
    

    用法
    $ ./macrodiff.py -h
    usage: macrodiff [-h] -s STARTSTR [-e ENDSTR] [--pp PP] [--ppflags PPFLAGS]
                        FILE
    
    Extract changes in simple preprocessor macro values between two marked points in a C/C++ translation unit. Function-like macros are not supported
    
    positional arguments:
      FILE                  FILE is a C/C++ source file to be processed
    
    optional arguments:
      -h, --help            show this help message and exit
      -s STARTSTR, --start STARTSTR
                            The initial macro values will be those in effect when
                            the first line commencing with STARTSTR is read
      -e ENDSTR, --end ENDSTR
                            The final macro values will be those in effect when
                            the first line commencing with ENDSTR is read, or if
                            --end is not given then those in effect at end-of-file
      --pp PP               PP is the preprocessor command to invoke. Default 'cpp
                            -dD'
      --ppflags PPFLAGS     PPFLAGS are additional options to be passed to PP
    

    试试这个:
    $ ./macrodiff.py -s='#pragma message Begin' -e='#pragma message End' foo.c
    

    输出:
    BEFORE[define C 2] AFTER[undef C]
    BEFORE[define B 1] AFTER[undef B]
    

    或者:
    $ ./macrodiff.py -s='#pragma message Begin' -e='#pragma message End' --ppflags='-DA' foo.c
    

    输出:
    BEFORE[define C 2] AFTER[define C 3]
    BEFORE[define B 1] AFTER[define B 2]
    

    或者:
    $ ./macrodiff.py -s='#pragma message Begin' --ppflags='-DA' foo.c
    

    这次的差异取自 #pragma message Begin
    文件结尾。输出:
    BEFORE[define C 2] AFTER[define C 5]
    BEFORE[define B 1] AFTER[define B 4]
    

    如果您更愿意使用独特的注释而不是编译指示作为
    开始和结束标记,然后添加 -CPPFLAGS .这将保留评论
    预处理的输出,指令中的输出除外。

    对类似函数的宏的支持留作练习。

    关于gcc - 如何在源代码中的特定行检查 GCC 预处理器定义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23239090/

    相关文章:

    c - Gtk-ERROR ** : GTK+ 2. 检测到 x 符号

    c - 宏中的可变参数

    c++ - C 预处理器作为语言创建工具的长度/限制是什么?我在哪里可以了解更多关于这些的信息?

    c++ - 如何在 Linux 中优化构建

    c++ - GCC 可以从静态库中内联函数吗?

    c++ - 如何在 Xcode 中检测带有宏的 C++ 编译器?

    objective-c - Mac OSX/iPhone 上的 C 预处理器, '#' key 的用法?

    swift - 如何在 Swift 中使用 libjpeg 复杂宏?

    c++ - 使用 stdlibc++ 4.7 启用 C++11 时,出现 clang 错误,而 gcc 编译正常

    ruby-on-rails - 如何在 shoulda 宏中创建上下文