Clang:从命令行或 Python 可靠地检测支持的 C++ 标准

Clang: reliably detect supported C++ standard from command line or Python

在 Python 脚本中,我试图 确定安装的 Clang 支持的最高 C++ 标准。

一个问题是我不能依赖 clang --version 的输出总是相同的 - 最好的例子是 OSX 上的 AppleClang。 尝试使用 -std=c++11-std=c++14 等测试标志编译 hello world .cpp 文件,...似乎不是最可靠的方法,需要创建临时文件。

是否有任何命令可以 运行 测试某种方言是否可用 而无需实际编译任何东西

Is there any command one could run to test if a certain dialect is available without actually compiling anything?

是的。您可以要求编译器只预处理一个空文件。它会那样做 无投诉:

$ clang++ --version
clang version 4.0.1-6 (tags/RELEASE_401/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

$ echo '' | clang++ -x c++ -E -
# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2

$ echo $?
0

然后您可以顺便添加一个 -std 选项。如果编译器支持:

$ echo '' | clang++ -std=c++98 -x c++ -E -
# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 326 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2

$ echo $?
0

仍然没有抱怨。但如果不是:

$ echo '' | clang++ -std=c++17 -x c++ -E -
error: invalid value 'c++17' in '-std=c++17'
$ echo $?
1

在 python 脚本中,您可以方便地以空字符串的形式提供一个空输入文件给执行编译器探测命令的 subprocess.run 调用, 并同时吞下不需要的标准输出。你会这样迭代 调用按时间顺序排序的 -std 值列表以查找 最新支持。谨慎的做法不仅仅是测试 return 代码,而是 还捕获 stderr,并在失败的情况下正确解析它 某种诊断,以防命令因某些意外原因失败。

这是一个为 GCC 和 clang 服务的镜头:

$ cat std_max.py
#!/usr/bin/python3
import subprocess

standards = ['98','03','11','14','17']
gpp_barf_pattern = "error: unrecognized command line option ‘-std=c++{0}’"
clangpp_barf_pattern = "error: invalid value 'c++{0}'"

def has_standard(compiler, std_year, barf_pattern):
    std_opt = '-std=c++' + std_year
    try:
        subprocess.run([compiler,std_opt,'-x','c++','-E','-'],\
            check=True,input=b'',stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    except subprocess.CalledProcessError as e:
        barf = barf_pattern.format(std_year)
        strerr = e.stderr.decode('utf8','strict')
        if barf in strerr:
            return False
        raise
    return True

def get_std_max(compiler,standards,barf_pattern):
    max_std = standards[0] if len(standards) else ''
    for std_year in standards:
        if not has_standard(compiler,std_year,barf_pattern):
            break
        max_std = 'c++' + std_year
    return max_std

这会正确地告诉我:

$ python3
Python 3.6.3 (default, Oct  3 2017, 21:45:48) 
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from std_max import *
>>> get_std_max('clang++',standards,clangpp_barf_pattern)
'c++14'
>>> get_std_max('g++',standards,gpp_barf_pattern)
'c++17'
>>>

还没有 C++20:

>>> has_standard('g++','20',gpp_barf_pattern)
False
>>>