DOM 工作线程 & 存储 C++ 代码风格

此页面描述了 DOM 工作线程 & 存储团队维护的组件的代码风格。它们位于树内 ‘dom/docs/indexedDB’ 目录下。

简介

此代码风格目前适用于以下目录中的组件

  • dom/file

  • dom/indexedDB

  • dom/localstorage

  • dom/payments

  • dom/quota

  • dom/serviceworkers

  • dom/workers

从长远来看,代码旨在使用 Mozilla 编码风格,该风格参考了 Google C++ 编码风格

但是,代码的大部分是在规则制定之前编写的,特别是对 Google C++ 编码风格的引用是在后来才加入的,并且由于代码量庞大,短期内无法解决这种不一致性。为了避免任意混合旧风格和新风格的代码,本文档明确了与“全局”代码风格的偏差,并在将来进行修改以描述迁移路径。

此外,为了在团队维护的组件中实现更高的一致性,并减少审查期间关于风格的讨论,使审查能够专注于更重要的问题,这里描述了超出全局代码风格的更具体的规则。这些主题可能在全局代码风格中被有意或无意地忽略了。根据更广泛的共识和适用性,这些特定规则将来可能会迁移到全局代码风格中。

请注意,本文档不涵盖纯格式问题。代码使用提供的配置文件由 clang-format 自动格式化,并且 clang-format 的任何操作都优先于关于格式化的任何其他规则。

与 Google C++ 编码风格的差异

尚未记录的差异。

与 Mozilla C++ 编码风格的差异

Mozilla 风格

普遍的 WAS 风格

偏差范围

演变

我们更喜欢使用“static”,而不是匿名 C++ 命名空间。

将所有应该具有内部链接的符号放在实现文件顶部的单个匿名命名空间块中,而不是将它们声明为 static。

所有文件

不清楚。Mozilla 代码风格中的建议指出,这可能会在将来根据调试器支持而改变,因此这种偏差可能会变得过时。

所有通过左值引用传递的参数都必须标记为 const。[…] 输入参数可以是 const 指针,但我们从不允许非 const 引用参数,除非约定要求,例如 swap()。

可以使用非 const 引用参数。

所有文件

不清楚。也许至少将非 const 引用参数的使用限制在不是明确的输出参数(即被赋值的)的情况下。

Google/Mozilla C++ 代码风格的补充

本节包含不与 Google 或 Mozilla C++ 代码风格冲突的样式指南,但可能使指南更具体或在这些样式指南根本未涵盖的主题上添加指南。

命名

gtest 测试名称

gtest 从不同的片段构建完整的测试名称。基本测试和参数化测试的测试名称构建方式略有不同。

测试的前缀应以组件和类的标识符开头,该标识符基于源代码目录的名称,转换为 PascalCase 并使用下划线作为分隔符,例如,对于 dom/indexedDB 中的类 Key,使用 DOM_IndexedDB_Key 作为前缀。

对于使用 TEST(test_case_name, test_name) 构建的基本测试:使用前缀作为 test_case_name。测试 test_name 应以被测方法的名称开头,并以 . 结尾。在 test_name 中使用下划线作为分隔符。

值参数化测试使用 TEST_P(parametrized_test_case_name, parametrized_test_name) 构建。它们需要一个自定义测试基类,其名称用作 parametrized_test_case_name。类名以 TestWithParam_ 开头,并以参数类型的音译结尾(例如,std::pair<nsString, int>String_Int_Pair),并将其放在(匿名)命名空间中。

注意

将类放在(匿名)命名空间中非常重要,因为根据此指南,其名称在 libxul-gtest 中不唯一,并且可能发生名称冲突,否则会导致 ODR 冲突。

根据上面针对 test_name 描述的相同规则构建 parametrized_test_name

值参数化测试的实例使用 INSTANTIATE_TEST_CASE_P(prefix, parametrized_test_case_name, generator, ...) 构建。作为 prefix,使用上面描述的前缀。

类似的考虑适用于类型参数化测试。如有必要,此处将添加类型参数化测试的具体规则。

基本原理

所有 gtest(不仅来自 WAS 组件)都链接到 libxul-gtest,这要求名称在该大范围内是唯一的。此外,应该从测试名称(例如,在测试执行日志中)清楚地了解可以在哪个源文件(或至少哪个目录)中找到测试代码。理想情况下,测试名称应具有层次结构,以便轻松选择要执行的测试组。但是,gtest 有一些限制,不能完全做到这一点。这些指南尽量满足这些限制。请注意,gtest 通常建议不要在测试名称中使用下划线,因为这可能导致保留名称和命名冲突,但此处陈述的规则应避免这种情况。如果出现任何问题,我们可以修改规则以适应这种情况。

指定类型

使用 auto 声明变量

关于 auto 的 Google C++ 代码风格 通常允许使用 auto,并鼓励在特定情况下使用,这仍然为解释留下了相当大的空间。

我们通过一些额外的鼓励和劝阻来扩展它

  • 如果类型已存在于初始化表达式中(尤其是模板参数或类似内容),则**使用** auto,例如 auto c = static_cast<uint16_t>(*(iter++)) << 8;auto x =  MakeRefPtr<MediaStreamError>(mWindow, *aError);

  • 如果拼写出的类型很复杂,则**使用** auto,例如嵌套 typedef 或类型别名,例如 foo_container::value_type

  • 如果类型被拼写成内置整数类型或 <cstdint> 中的类型之一,则**不要使用** auto,例如,不要使用 auto foo = funcThatReturnsUint16();,而要使用 uint16_t foo = funcThatReturnsUint16();

注意

使用 auto 的一些缺点与在合适的 IDE/编辑器外部无法获得类型信息有关。这可以通过解决 Bug 1567464 来在一定程度上得到解决,该 Bug 将使类型信息在 searchfox 中可用。因此,指南可能会进行修改以推广更广泛地使用 auto

指针类型

普通指针

使用普通指针容易出错。避免使用拥有普通指针。特别是,避免使用文字,非放置 new。有各种类型的智能指针,并非所有智能指针都提供合适的工厂函数。但是,在存在此类工厂函数的情况下,请使用它们(以及 auto)。以下是智能指针类型和相应工厂函数的不完整列表

类型

工厂函数

头文件

mozilla::RefPtr

mozilla::MakeRefPtr

"mfbt/RefPtr.h"

mozilla::UniquePtr

mozilla::MakeUnique

"mfbt/UniquePtr.h"

std::unique_ptr

std::make_unique

<memory>

std::shared_ptr

std::make_shared

<memory>

此外,为了创建一个 already_AddRefed<> 作为函数的参数或返回值,而无需解引用它,请使用 MakeAndAddRef,而不是首先创建一个可解引用的 RefPtr(或类似类型),然后使用 .forget()

智能指针

在函数签名中,优先使用 RefPtr 作为参数或返回值,而不是结合常规 std::move 使用 already_AddRefed,而不是 .forget()。这提高了可读性和代码生成效率。 already_AddRefed 的主要合法用法在其文档中进行了描述。

优先使用 mozilla::UniquePtr 而不是 nsAutoPtr,因为后者已弃用(例如,没有工厂函数,参见Bug 1600079)。

仅当 T 是 XPCOM 接口类型时才使用 nsCOMPtr<T>MDN 上有更多详细信息 <https://mdn.org.cn/en-US/docs/Mozilla/Tech/XPCOM/nsCOMPtr_versus_RefPtr>)。

枚举

使用作用域枚举或强类型枚举(enum struct),而不是非作用域枚举。使用 PascalCase 为作用域枚举的值命名。

演进过程

本节介绍了演进本文档中描述的编码风格的过程。为了清晰起见,在本节中,我们将编码任务与代码风格演进任务区分开来。

管理代码风格演进任务

代码风格演进任务是指应修改或修订本文档中描述的编码风格的任务。

代码风格演进任务应在 Bugzilla 中作为每个主题的单个 Bug 进行管理。所有此类任务都应阻塞元 Bug 1586788 <https://bugzilla.mozilla.org/show_bug.cgi?id=1586788>

当您着手处理代码风格演进任务时

  • 该任务可能已包含解决方案的草图。如果尚无明显的首选解决方案,请先通过 Bug 上的评论讨论解决方法。

  • 当总体思路准备好在本文档中详细说明时,请相应地修改或修订它。

  • 将对本文档的更改作为补丁提交到 Phabricator,并将其提交审查。由于这将影响许多人,因此每个更改都应由至少两个人审查。理想情况下,这应该包括此样式文档的所有者和一个了解此样式适用的代码库部分的人。

  • 如果存在已知的违反编码风格修订版的情况,请考虑修复其中一些,以便在实际代码上测试修订版。如果代码风格演进任务引用了来自审查的特定代码位置,则至少应修复该位置以符合修改后的编码风格。

  • 当您获得两个 r+ 时,请合并补丁。

  • 在下一次团队会议上报告添加内容,以提高认识。

代码风格演进任务的基础

演进代码风格的愿望或必要性可能源于不同的活动,包括 - 代码审查 - 本地阅读或编写代码 - 阅读编码风格 - 对编码风格的一般想法

代码风格不应充斥着很少相关或很少引发讨论的方面,因为维护代码风格也需要成本。代码风格应尽可能全面,以降低代码和代码风格的整体维护成本。

因此,特别关注在代码审查中引发了一些讨论的方面,因为减少审查中必要的样式讨论的数量或冗长性是衡量文档化样式有效性的主要指标。

基于代码审查演进代码风格

此处描述的过程的目标是利用源自代码审查的与样式相关的讨论,但将代码风格的演进与审查过程分离,以便它不会阻碍底层 Bug 的进展。

执行审查时应考虑以下事项

  • 提醒自己有关代码风格的信息,在开始审查之前可能需要浏览一下文档,或者在进行审查时将其并排打开。

  • 如果您发现违反了现有规则,请添加内联注释。

  • 注意代码本身或与作者讨论后的与样式相关的方面。考虑是否可以将其概括为样式规则,但尚未涵盖文档化的全局或局部样式。这可能是与其他位置的样式不同、与您的个人样式不同等内容。

  • 在这种情况下,为手头的代码片段找到一个可接受的临时解决方案,该解决方案对于补丁的 r+ 是可接受的。也许与代码作者达成一致,添加一条注释,说明稍后应在编写规则时对其进行修改。

  • 如上所述,在 Bugzilla 中创建代码风格演进任务。在 Bug 的描述中,引用导致该 Bug 的审查注释。如果您能建议解决方案,请将其包含在描述中,但这不是创建任务的必要条件。

编写代码时改进代码风格合规性

定期查看代码风格文档,并提醒自己其规则,并特别注意最近的更改。

编写代码时,即添加新代码或修改现有代码时,请提醒自己检查代码的样式合规性。

时间允许的情况下,作为代码区域中其他工作的一部分,随时解决现有的违规行为。在专用的补丁中提交此类更改。如果您发现难以随时解决的主要违规行为,请考虑创建一个专门用于解决该违规行为的 Bug,然后可以在计划过程中对其进行安排。

与全局 Mozilla C++ 编码风格同步

此处描述的编码风格的几个方面将适用于整个代码库。但是,对全局编码风格的修改将影响大量代码作者,可能需要进行广泛的讨论。从长远来看,应限制与全局编码风格的偏差。另一方面,与代码库的所有部分都不相关的修改,或者在全局范围内难以达成共识的修改,可能更有意义地保留在局部样式中。

与全局样式同步的详细信息应与全局编码风格的所有者和同行进行讨论(参见Bug 1587810 <https://bugzilla.mozilla.org/show_bug.cgi?id=1587810>)。

常见问题解答

  • 当有人引入符合当前风格的新代码,但函数/类/文件其余部分不符合时,他们是否有责任随时更新该其余部分?

    代码作者没有义务更新其余部分,但鼓励他们在时间允许的情况下这样做。是否需要这样做取决于许多因素,包括现有样式违规的数量和复杂性、随时更改带来的风险等。判断这一点留给代码作者。至少,函数/类/文件的状态不应比之前更糟糕。

  • 仅将此处定义的样式应用于新代码而引入的样式不一致是否被认为是可以接受的?

    虽然这当然不是最佳的,但为了在一定程度上接受此类不一致性是不可避免的,以便能够朝着改进的样式迈进。关于程度的个人偏好可能会有所不同,但如有疑问,此类不一致性应被视为可以接受。它们不应阻止 Bug 关闭。