探索叮当工具,第0部分:用叮当构建代码

这篇文章是一系列常规文章的一部分,其中C++产品团队和其他来宾回答了我们从客户那里收到的问题。这些问题可以是任何与C++相关的:MSVC工具集、标准语言和库、C++标准委员会、ISOCPP.ORG、CppCon等。

null

今天的帖子由客座作者Stephen Kelly撰写,他是Havok的开发人员,Qt和CMake的贡献者 博主 . 这是一系列文章中的第一篇,他将分享他在当前团队中使用叮当工具的经验。

一致性和兼容性

在C++编译器的长期发展过程中,一些编译器在主流领域占据主导地位。MSVC 6和GCC 2.95有时被描述为“太成功了”。它们的成功对驱动C++的使用产生了持久的影响,但它们之间存在许多不相容之处,导致了美丽的围墙花园。在某些情况下(例如省略 类别名 关键字内部模板),这使得开发任务更方便,即使所得代码与C++标准或GCC不同。对于开发人员来说,这是一个持久的烦恼,他们工作的代码可以在不同的平台之间移植,而同事们会在没有必要的条件下签入代码 类别名 关键字,导致其他使用更一致的编译器的同事修复代码。

在GCC主导的类Unix平台上,标准一致性占据了较高的地位,部分原因是许多兼容工具(和人)的自由软件文化可在标准场地内进行协作。Clang,最近进入C++编译器领域,分享了 标准符合性 但获得市场份额有几个原因,其中包括许可证选择、实现语言选择、交叉编译、改进的诊断消息等。这种竞争使GCC和Clang都受益,因为它们在法规遵从性和开发人员实用性方面相互匹配。Clang采用的部分策略是实现与GCC在行为方面的兼容性,标准Unix头中依赖的GCC bug,以及实现一个“编译器驱动程序”,该驱动程序在命令行上与GCC在警告标志和其他编译选项方面的兼容性。

MSVC编译器在任何方面都没有落后,它正在实现和驱动 标准符合性 对于重视可移植性的开发人员来说。这个 /放任的- 选项是标准一致模错误。随着发布的继续,这些剩余的bug将得到解决。

最近几年的另一个发展是基于CLAN的C++工具API的出现。CLAN开发人员已经创建了工具,可以用于机械地重构大量C++代码,包括 叮当声 叮当声格式 . 基于的自定义工具 叮当声 特别是这个博客系列的后续文章的主题。

获取Clang/LLVM工具

以及使用Clang作为一个可行的编译器 Windows上的生产使用 对于 多个web浏览器 ,所有这些都留下了一个问题,即如何从叮当作响的工具中获益。如果您的代码库只使用MSVC编译器构建过,那么必须首先使用Clang使代码可构建。

可以使用Clang for Windows的安装程序 在这里 . Clang在Windows上重获成功的方法之一就是实现 与MSVC的兼容性 . 用于Windows的Clang安装程序包括两个“编译器驱动程序”。 叮当声++ 即使在Windows上也是GCC兼容的,并且可以与依赖MinGW的代码库一起使用。 叮当声 是Clang安装程序附带的第二个编译器驱动程序,与MSVC编译器驱动程序兼容。这种兼容性表现在接受样式为的命令行标志方面 cl.exe文件 在允许模式下运行叮当声符合 cl.exe文件 .

运行Clang安装程序后,需要一个工具集文件来指示Visual Studio 2017使用 clang-cl.exe 而不是本地人 cl.exe文件 . “LLVM编译器工具链” Visual Studio 2017扩展 必须安装才能启用工具链。

安装“LLVM编译器工具链”后,平台工具集可以更改为 llvm公司 在里面 Visual Studio ,或在buildsystem生成器中指定它,例如 克马克 具有 克马克-T llvm公司 .

使用MSVC编译器的/permissive-flag

Clang检测到的一些C++一致性问题也被最近的MSVC版本检测到。 /放任的- 旗帜。因此,让代码用Clang构建的第一步可能是首先用MSVC一致性模式构建它。这样做的缺点是,在某些情况下,MSVC一致性模式比Clang更严格(因为Clang试图模拟允许的MSVC)。Clang CL默认允许某些MSVC行为,如不正确的两阶段查找,MSVC一致性模式则拒绝这些行为。

迭代一致性

通过添加compile标志,可以激活更一致的Clang CL模式 -fno延迟模板解析-缺少Werror=microsoft-Werror=typename . 即使在转弯后 微软 诊断错误, 个别此类诊断 如果需要,可以通过添加 -Wno公司- 文档化标志的版本,例如 -Wno微软密封 . 请注意,这样的标志必须出现在 -Werror=微软 选项。

即使使用Clang CL提供的兼容性 回退选项 可用于指示编译器使用 CL.exe文件 如果需要。

为了构建代码,可能需要在buildsystem中启用Clang的几个特性。内部函数的使用,例如 _洗牌8 需要使用 -第4.1章 旗子或类似物。

与在以前从未使用过的代码库上启用编译器警告一样,最明智的方法是使用最允许的必要设置开始使用Clang构建,然后随着时间的推移引入更多的一致性。

如果您对Clang的兴趣仅限于使用语义丰富的工具,那么一定要记住,您的代码不需要在编译后运行。它也不需要链接,甚至不需要编译——明智地使用 条件编译 s可用于使大多数代码可通过叮当工具访问,同时限制对受影响太大而无法编译的代码或无法控制的代码的影响。Windows头的某些部分无法编译 还有叮当声吗 . 这可能也会影响您的代码。 一定的努力 微软内部正在解决这些问题。

移植现有C++代码

以下是将代码移植到使用Clang或 /放任的- ,当您提高构建中的标准一致性级别时:

只能隐式调用一个用户定义的转换
允许模式中的MSVC接受C++代码,它同时调用两个隐式转换(而不是标准一致的转换)。Clang CL不接受这种双重转换。这可能会出现在 两种不同的字符串类型 例如(隐式的to,然后from 常量字符* ).
三元表达式中的两个结果语句必须具有相同的类型
如果ternaries与两种不同的结果类型一起使用,则必须确保两种结果类型相同,而不进行隐式转换。这可能发生在 简单包装类型 ,或更复杂的表达式,例如使用 智能指针 隐式转换为指针类型:

smart_pointer<int> func(bool b)
{
    int* defaultPtr = nullptr;
    smart_pointer<int> sp;
    return b ? sp : defaultPtr;
}
缺少“typename”和“template”关键字
C++ 需要用户 使用 类别名 关键字,以便 消除类型和值之间的歧义 在允许模式下,需要 模板 关键字之间消除歧义 模板函数和比较 .

对于模板中的变量,解决方法通常是使用 汽车 而不是添加 模板 关键字。这也可能发生在使用类型特征时,例如 标准::删除u引用 . 开发人员可能编写(或从代码库的其他地方复制)代码,例如

template<typename T>
void doSomething(T t)
{
    std::remove_reference<T>::type value = input;
    ++value;
    return value;
}

Clang正确地发布了诊断并建议使用 类别名 关键字。

然而,在这一点上,开发人员显然不会记得添加这样的关键字,除非他们被提醒,所以也许有一个不同的策略教开发人员使用 标准::删除u引用u t 取而代之的是,省略尾部 ●类型 . 它更简单、更全面、更便携。可以为代码中使用的任何自定义类型特性引入类似的转换。

过度 类别名 关键字
同样,有时MSVC 接受 类别名 关键字的位置。

不幸的是,有相反的情况下,叮当声 需要 模板 关键字 在不应该这样做的地方,所以要做到这一点有时需要 #ifdef公司 .

内部定义的静态变量 如果 陈述
MSVC不正确地接受如下代码

if (static bool oneTimeInit = true)
{
}

在现代的代码库中,这应该改为

if (static bool oneTimeInit = true; oneTimeInit)
{
}

这需要使用 /std:c++17 使用MSVC编译时的标志。

隐式删除默认构造函数
Clang不允许用户定义 默认构造函数 需要它们来初始化联合。

这里的解决方案是实际定义构造函数而不是默认构造函数:

B::B()
{        
}
叮当不接受 对于每个 延伸
这里的解决办法是 cxxU范围U 相反。替换

for each (auto item in container)

for (auto item : container)
限定模板相关基中的查找
不过,MSVC错误地接受(并消除)使用模板依赖基的声明 叮当声没有 . 这又是由于执行不严造成的 两阶段查找 . 这里的解决方案是在这些调用前面加上 这个-> 或者基类的名称。
总是错误的静态断言
静态断言(假) 如果试图实例化不应实例化的模板,则希望发出信息性错误消息的开发人员有时会尝试。不幸的是,它不适用于两阶段查找。MSVC接受这一点,但是 不是叮当声 . 解决方案是使值依赖于模板类型。
不同范围内的专门化
MSVC允许C++模板在不同的命名空间中被专门化为它声明的那个。叮当声不允许这样。专门化应该与初始模板位于同一命名空间中。

template<typename T>
struct Templ
{
};

namespace NS {

template<>
struct Templ<int>
// error: class template specialization of 'Templ' must occur at global scope
{
};

}

结论

重要的是要认识到,即使您不能运行代码(由于 #ifdef公司 或依赖性问题)。运行Clang构建的代码打开了使用Clang清理程序的可能性,但是只要能够用Clang编译代码就可以使用源代码转换工具,比如Clang tidy, 克拉齐 和叮当作响的格式。本系列的更多博客文章将探讨使用Clang进行源代码到源代码转换的工作流。

如果您遇到其他需要在代码中使用Clang CL编译的更改,请留下评论!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享