Boost C++ 库

...世界上最受推崇和精心设计的 C++ 库项目之一。 Herb SutterAndrei AlexandrescuC++ 编码标准

Boost 库要求和指南

简介

本页描述了提交到 Boost 的库内容的要求和指南。

有关所涉及流程的描述,请参阅 Boost 库提交流程 页面。

要求

为了避免提议的库被拒绝而导致的沮丧和浪费时间,它必须满足以下要求。

作者在提交之前没有阅读邮件列表的要求。但是,已经注意到,以 "我刚开始阅读这个邮件列表..." 开头的提交似乎会失败,而且经常会很尴尬。

许可证要求

满足许可证要求的首选方法是使用 Boost 软件许可证。请参阅 许可证信息。如果您出于任何原因不打算使用 Boost 软件许可证,请首先在 Boost 开发者邮件列表 中讨论问题。

许可证要求

  • 必须易于阅读和理解。
  • 必须免费授予复制、使用和修改软件用于任何用途(商业和非商业)的许可。
  • 必须要求许可证出现在所有软件源代码副本上。
  • 不得要求许可证与可执行文件或库的其他二进制使用一起出现。
  • 不得要求源代码可用于库的可执行文件或其他二进制使用。
  • 可以限制将库的名称和描述用于 Boost 网站上找到的标准版本。

可移植性要求

  • 库的接口必须可移植,并且不受限于特定的编译器或操作系统。
  • 库的实现如果可能的话必须是可移植的,并且不受限于特定的编译器或操作系统。如果不可能实现可移植实现,则如果非可移植构造物易于移植到其他环境,并且至少为两种流行的操作系统(例如 UNIX 和 Windows)提供了实现,那么这些构造物是可以接受的。
  • 库至少在两个实现最新 ISO C++ 标准的 C++ 编译器上运行。
  • 库不需要在不符合 ISO 标准的 C++ 编译器上运行。
  • 库不需要在任何特定的 C++ 编译器上运行。Boost 贡献者通常试图确保他们的库与流行的编译器一起工作。boost/config.hpp 配置头文件 是解决编译器缺陷的首选机制。

由于没有绝对的方法来证明可移植性,因此许多 Boost 提交通过使用两个不同的 C++ 编译器(通常是在不同的操作系统下)正确地编译和执行来证明实际的可移植性。否则,审阅者可能会不相信移植实际上是可行的。

所有权

您确定拥有您要提交的库吗?MJ Salone 的 "如何对软件进行版权保护",Nolo Press,1990 年说

在您自己的时间里进行的工作与您为雇主在公司时间里进行的编程非常相似,这会导致严重的法律问题。在这种情况下,最好事先从您的雇主那里获得书面许可。

在您提交的所有重要文件中添加版权声明。Boost 不会接受没有明确版权信息的库。

组织

Boost 库的质量不仅仅是关于 API 和代码设计。还关于向用户呈现整个库的一致视图。接受后,库必须遵守此目录和文件结构

Boost 标准库组织
子目录或文件 内容 必选
build 库构建文件,如 Jamfile、IDE 项目、Makefiles、Cmake 文件等。 如果库有要构建的源代码,则必选。
config 用于构建时配置检查的文件。此目录可能包含源文件和构建系统脚本,这些脚本在构建库、测试或示例时使用,以检查目标系统是否满足某些条件。例如,检查可以测试编译器是否实现某个功能,或者目标系统是否支持某个 API。 可选。
doc 用于构建库的源代码以及为库构建的文档。如果库需要从非 HTML 文件构建文档,则此位置必须使用 Boost Build 可构建。 所有库都必选。
doc/html 文档 (HTML) 文件。 所有具有预生成文档的库都必选。并且生成的文档必须在此处生成。
example 示例程序文件。 如果库有示例文件,则必选。强烈建议使用示例文件。
index.html 重定向到 HTML 文档。有关此文件的模板,请参阅 "重定向" 所有库都必选。
include/boost/library 库的头文件。 所有库都必选。
meta 有关库的元数据。 所有库都必选。
meta/libraries.json 包含有关库信息的 JSON 文件,用于为 Boost C++ 库集合生成网站和文档。 所有库都必选。
meta/explicit-failures-markup.xml 描述预期测试失败的 XML 文件,用于生成测试报告。 可选
src 必须编译才能构建库的源文件。 如果库有要构建的源文件,则必选。
test 回归测试或其他测试程序或脚本。这是唯一用于自动化测试的位置。如果您有需要作为自动化测试一部分的额外位置,则要求此位置引用额外测试位置。 所有库都必选。
tools 库使用或提供的工具。此内的结构由库决定,但建议使用与常规 Boost 库或工具类似的结构。 具有可运行工具的库必选。

集成

一旦库被接受为 Boost C++ 库的一部分,它就需要正确地集成到开发、测试、文档和发布流程中。这种集成提高了所有库的最终质量,并且是用户对整个 Boost C++ 库的预期质量不可或缺的一部分。除了上面提到的 组织要求 之外,还需要以下集成

构建源代码

库需要提供一个 Boost Build 项目,用户和顶级 Boost 项目可以使用该项目来构建库(如果它有要构建的源代码)。源代码构建的 Jamfile 至少需要声明项目、库目标和注册要安装的目标。例如

project boost/my_lib ;

lib boost_my_lib : a.cpp ;

boost-install boost_my_lib ;

测试

库需要提供一个 Boost Build 项目,用户和根 Boost 测试脚本可以使用该项目来构建和运行库的测试。测试构建项目必须驻留在project-root/test目录中,并且必须从此目录或其他目录(例如b2 libs/library/test从 Boost 根目录必须有效)构建。

一个例子test/Jamfile如下所示

import testing ;

run default_constructor.cpp ;
run copy_constructor.cpp ;
compile nested_value_type.cpp ;
compile-fail invalid_conversion_1.cpp ;

警告:这是顶级测试脚本唯一考虑用于测试的位置。如果您想测试其他位置,您必须声明这些位置是作为依赖项构建的,或者使用build-project.

如果库需要某种程度的 C++ 符合性,从而阻止某些编译器或配置工作,则可以(并且建议)在测试中声明这些要求Jamfile这样就不会运行测试,以节省测试资源,如下面的示例所示

import testing ;
import ../../config/checks/config : requires ;

project : requirements [ requires cxx11_variadic_templates cxx11_template_aliases ] ;

run cpp11_test.cpp ;

有关更多信息,请参阅 Boost.Config 的文档

构建文档

库需要提供一个 Boost Build 项目来构建库的文档。该project-root/doc项目是顶级文档构建脚本和发布构建脚本唯一引用的位置。文档构建项目必须具有以下两个功能

  1. 定义一个boostdoc目标。此目标很可能是一个看起来大致类似的别名
    alias boostdoc : my_boostbook_target
        : : : <implicit-dependency>my_boostbook_target ;
    
    但如果您的项目没有集成到全局文档手册中,您可以使用一个空别名,例如
    alias boostdoc ;
    
  2. 如果项目有任何文档,则该项目必须默认构建独立文档。发布脚本构建此默认值,以确保所有项目都具有最新的文档。

指南

请使用这些指南作为检查库提交内容的清单。并非所有指南都适用于每个库,但应尽合理努力遵守指南。

向后兼容性

Boost 库通常拥有庞大而多样化的用户群。为了确保在这种情况下从旧 API 到新 API 的顺利过渡,鼓励库作者在库中引入重大更改时遵循一些指南
  1. 非重大更改可以不受限制地进行。
  2. 可以进行小的重大更改,但应在更改发布之前几个版本通知用户。大多数重大更改都属于此类别。
  3. 对于重大更改具有从旧 API 到新 API 的迁移路径(例如boost::filesystemv2 到 v3),新 API 应在单独的目录/命名空间中引入,并且应通知用户,并给予他们几个版本的时间进行迁移。一段时间后,可以删除旧 API。
  4. 对于重大更改没有迁移路径(例如boost::spiritv2 到 v3),新 API 应在单独的目录/命名空间中提供,并且应保留旧 API(因为没有迁移路径)。删除 API 应被视为删除 Boost 库,这可以进行,但需要更长的弃用期。
  5. 等同于库重新设计或重写的重大变更应被视为新库,鼓励进行正式审查(或至少进行简要审查)。

设计和编程

首先要追求清晰度和正确性;在大多数 Boost 库中,优化只应是次要考虑因素。

以 ISO 标准 C++ 为目标。这意味着有效地利用语言的标准特性,并避免非标准的编译器扩展。这也意味着在适用情况下使用 C++ 标准库。

头文件应是友好的邻居。参见 头文件策略。参见 命名一致性

遵循高质量编程实践。例如,参见 Scott Meyers 编著的 Addison Wesley 出版社出版的“Effective C++”第 2 版和“More Effective C++”。

使用 C++ 标准库或其他 Boost 库,但仅在收益超过成本时使用。不要使用除 C++ 标准库或 Boost 之外的库。参见 库重用

阅读 实现变体,了解如何提供性能、平台或其他实现变体。

浏览 最佳实践手册,获取现有 Boost 库中的源代码示例和链接。

阅读 具有单独源代码的库的指南,了解如何确保编译的链接库符合用户预期。

使用 C++ 标准库的命名约定(参见 命名约定原理

  • 名称(除非以下情况除外)应全部小写,单词之间用下划线分隔。
  • 缩略词应视为普通名称(例如,xml_parser 而不是 XML_parser)。
  • 模板参数名称以大写字母开头。
  • 宏(哎呀!)名称全部大写,以 BOOST_ 开头。

选择有意义的名称 - 明确优于隐含,可读性很重要。强烈建议使用清晰且描述性的名称,即使很长。

使用异常在适当情况下报告错误,并编写在异常情况下安全的代码。

避免异常规范。参见 异常规范原理

提供示例程序或置信度测试,以便潜在用户了解如何使用您的库。

提供符合 测试策略和协议 的回归测试程序或程序。

尽管一些 Boost 成员在自己的代码中使用比例字体、制表符和不受限制的行长,但 Boost 的广泛分发的源代码应遵循更保守的指南。

  • 使用等宽字体。参见 字体原理
  • 使用空格而不是制表符。参见 制表符原理
  • 将行长限制为 80 个字符。

所有文档文件(HTML 或其他)以版权信息和许可信息结尾。有关首选形式,请参见 许可信息 页面。

所有源文件(包括程序、头文件、脚本等)以以下内容开头:

  • 描述文件内容的注释行。
  • 描述版权和许可的注释:同样,首选形式在 许可信息 页面中给出。
  • 请注意,开发人员可以在其库的存储库中提供 LICENSE_1_0.txtLICENSE.txtLICENSE 文件中的许可证文本副本。
  • 引用您在 Boost 网站上的库的注释行。例如:
    // See https://boost.ac.cn/libs/foo for library home page.
    

    其中 foo 是库的目录名称(见下文)。除了帮助遇到与文档分离的 Boost 文件的用户之外,Boost 的一些自动工具还依赖于此注释来识别哪些库头文件属于。

断言:如果您想在代码中添加运行时断言(您应该这样做!),请避免使用 C 的 assert 宏,而应使用 Boost 的 BOOST_ASSERT 宏(在 boost/assert.hpp 中)。它更可配置。在公共头文件和库源代码中使用 BOOST_ASSERT(对于单独编译的库)。在示例和文档中使用 C 的 assert 宏是可以的。

确保您的代码在 min()max() 宏存在的情况下进行编译。一些平台头文件定义了 min()max() 宏,这些宏会导致一些常见的 C++ 结构无法编译。一些简单的技巧可以保护您的代码免受不合适的宏替换。

  • 如果您想调用 std::min()std::max()
    • 如果您不需要依赖于参数的查找,请使用 (std::min)(a,b)
    • 如果您需要依赖于参数的查找,您应该
      • #include <boost/config.hpp>
      • 使用 BOOST_USING_STD_MIN();std::min() 引入当前作用域。
      • 使用 min BOOST_PREVENT_MACRO_SUBSTITUTION (a,b);min(a,b) 进行依赖于参数的调用。
  • 如果您想调用 std::numeric_limits<int>::max(),请使用 (std::numeric_limits<int>::max)() 替代。
  • 如果您想调用 min()max() 成员函数,而不是执行 obj.min(),请使用 (obj.min)()
  • 如果您想声明或定义一个名为 minmax 的函数或成员函数,那么您必须使用 BOOST_PREVENT_MACRO_SUBSTITUTION 宏。不要写 int min() { return 0; },而应该写 int min BOOST_PREVENT_MACRO_SUBSTITUTION () { return 0; }。无论函数是自由(命名空间作用域)函数、成员函数还是静态成员函数,这都是正确的,它适用于函数声明和函数定义。

文件名

命名要求确保文件和目录名称具有相对可移植性,包括 ISO 9660:1999(带扩展名)和其他相对有限的文件系统。上标链接提供了对每个选择的详细原理。

  • 名称只能包含小写1 ASCII 字母('a'-'z')、数字('0'-'9')、下划线('_')、连字符('-')和句点('.')。不允许使用空格2
  • 目录名称不能包含句点('.'3
  • 文件名中的第一个和最后一个字符不能是句点('.'4
  • 名称的第一个字符不能是连字符('-'5
  • 目录和文件名的最大长度为 31 个字符6
  • 总路径长度不得超过 207 个字符7

其他约定简化了通信

  • 作为翻译单元一部分由 C++ 编译器处理的文件应具有以“pp”结尾的三字母文件名扩展名。其他文件不应使用以“pp”结尾的扩展名。此约定可以轻松识别 Boost 中的所有 C++ 源代码。
  • 所有库在其最高级别都具有一个以特定库命名的主目录。参见 命名一致性。主目录可能包含子目录。

重定向

主目录应始终包含一个名为 index.html 的文件。作者请求这样做,以便他们可以以https://boost.ac.cn/libs/lib-name 形式发布 URL,并确保文档重组不会使 URL 无效。Boost 的内部工具也通过了解库的文档始终可以通过简化的 URL 访问而变得更加简单。

主目录 index.html 文件只需自动重定向到 doc/html 子目录即可。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
  <title>Boost.Name Documentation</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="refresh" content="0; URL=doc/html/index.html" />
</head>

<body>
  Automatic redirection failed, please go to <a href=
  "doc/index.html">doc/index.html</a>
</body>
</html>

命名一致性

随着库开发人员和用户对 Boost 积累了经验,以下一致的命名方法已被视为非常有用,特别是对于需要自己的头文件子目录和命名空间的较大库。

以下是它的工作原理。库被赋予一个描述库内容的名称。强烈建议避免使用神秘的缩写。遵循 C++ 标准库的做法,名称通常是单数而不是复数。例如,处理文件系统的库可能会选择名称“filesystem”,而不是“filesystems”、“fs”或“nicecode”。

  • 库的主目录(在父 boost-root/libs 中)使用相同的名称。例如,boost-root/libs/filesystem
  • 库的主头文件目录(在 boost-root/libs/name/include 中)使用相同的名称。例如,boost-root/libs/filesystem/boost/filesystem
  • 库的主命名空间(在父 ::boost 中)使用相同的名称,除非存在具有该名称的组件(例如,boost::tuple),在这种情况下,命名空间名称将变为复数。例如,::boost::filesystem

在记录 Boost 库时,请遵循以下约定(另请参见本文档的下一节)

  • 库名称以罗马字体显示。
  • 库名称以大写字母开头。
  • 仅当库名称后面没有“library”一词时,才会在“Boost”和库名称之间使用句点(例如,Boost.Bind)。
  • “library”一词不是库名称的一部分,因此以小写字母开头。

以下是一些应用这些约定的示例

  • Boost.Bind 由 Peter Dimov 编写。
  • Boost Bind 库由 Peter Dimov 编写。
  • 我经常使用 Bind,它是 Peter Dimov 编写的 Boost 库。

文档

即使是最简单的库也需要一些文档;文档的数量应与需求成正比。文档应假定读者具有 C++ 的基本知识,但不一定是专家。

文档的格式应为 HTML,并且不应需要高级浏览器或服务器端扩展。样式表是可以接受的。不鼓励使用 ECMAScript/JavaScript。文档入口点应始终是一个名为 index.html 的文件;参见 重定向

没有单一正确的文档编写方式。HTML 文档的组织方式通常与传统的印刷文档截然不同。面向任务的风格不同于面向参考的风格。归根结底,它归结为一个问题:文档是否足以让传说中的“平均”C++ 程序员成功使用该库?

文档的适当主题通常包括

  • 库的一般介绍。介绍尤其需要包括
    • 对库用途的非常高级别的概述,以及可能不适用的用途,即使是那些对问题领域没有先验知识的人也能理解。
    • 使用库的最简单的(“Hello World”)示例。
  • 涵盖基本用例的教程。
  • 参考文档
    • 每个类的描述。
    • 类之间的关系。
    • 对于每个函数,在适用情况下,描述、需求(先决条件)、效果、后置条件、返回值和抛出。
    • 讨论错误检测和恢复策略。
  • 如何编译和链接。
  • 如何测试。
  • 版本或修订历史。
  • 设计决策的原理。参见 原理原理
  • 致谢。参见 致谢原理

如果您需要更多关于如何编写文档的帮助,您可以查看关于 为 Boost 编写文档 的文章。

原理

以下是某些需求和指南的理由。

异常说明理由

异常说明 [ISO 15.4] 有时用于指示可能抛出的异常,或者因为程序员希望它们能提高性能。但是,考虑以下来自智能指针的成员

T& operator*() const throw()  { return *ptr; }

此函数不调用任何其他函数;它只操作指针之类的基本数据类型。因此,异常说明的任何运行时行为都无法调用。该函数完全暴露给编译器;实际上它被声明为内联的。因此,一个智能编译器可以很容易地推断出这些函数无法抛出异常,并做出与基于空异常说明所做的相同的优化。然而,一个“愚蠢”的编译器可能会进行各种悲观优化。

例如,一些编译器在存在异常说明的情况下会关闭内联。一些编译器会添加 try/catch 块。这种悲观优化可能会导致性能灾难,使代码在实际应用中无法使用。

虽然最初看起来很有吸引力,但异常说明往往会产生后果,需要非常谨慎地思考才能理解。异常说明最大的问题是,程序员使用它们的方式好像它们产生了程序员想要的效果,而不是它们实际产生的效果。

非内联函数是“不抛出任何东西”的异常说明可能在一些编译器中具有一些好处的唯一地方。

命名约定理由

C++ 标准委员会的库工作组详细讨论了这个问题,并且讨论了很长时间。在早期的 boost 帖子中也重复了讨论。简要总结

  • 命名约定是有争议的,虽然有几种命名约定被广泛使用,但没有一种风格占主导地位。
  • 考虑到将 boost 的一部分提议用于下一版 C++ 标准库的意图,boost 决定遵循标准库的约定。
  • 一旦一个库确定了特定的约定,大多数利益相关者都希望这种风格能够始终如一地使用。

源代码字体理由

Dave Abrahams 评论道:源代码的一个重要目的(我敢说首要目的)是交流:意图的文档化。我认为,这对 boost 来说是一个双重重要的目标。使用等宽字体使我们能够以更多的方式(图示是可能的)与更多人进行沟通,就在源代码中。使用空格为等宽字体编写的代码在使用变宽字体查看时也会读起来相当好,而且据我所知,每个支持变宽字体的编辑器也支持等宽字体。我不认为反过来也是如此。

制表符理由

禁止使用制表符,因为制表符在像 Boost 这样的多开发人员项目中会导致实际问题,而不是任何原则上的不喜欢。见 邮件列表存档。问题包括使用制表符的程序员和使用空格的程序员维护单个源文件,以及除了“不使用制表符”之外,很难强制执行一致的制表符策略。讨论得出结论,Boost 文件应该全部使用制表符,或者全部使用空格,因此决定坚持使用空格进行缩进。

目录和文件名理由

1. 一些遗留文件系统要求使用单一大小写名称。单一大小写名称消除了从不区分大小写的文件系统移动到区分大小写文件系统时的大小写错误。

2. 这是 POSIX 可移植文件名字符集的小写部分。引用 POSIX 标准,“文件名应该用可移植文件名字符集构建,因为在某些情况下使用其他字符可能会令人困惑或模棱两可。”

3. ISO 9660:1999 的严格实现和一些遗留操作系统禁止在目录名中使用点。对这种限制的需要正在减弱,并且可能很快就会被移除。

4. POSIX 对以句点开头的名称有特殊规则。Windows 禁止以句点结尾的名称。

5. 在某些情况下会过于混乱或模棱两可。

6. 我们不得不在某个地方划清界限,因此多年前选择了现在已经过时的 Apple 文件系统强加的限制。这仍然是一个合理的限制,有助于人类理解。

7. ISO 9660:1999。

ECMAScript/JavaScript 理由

在 1.29.0 版本之前,两个 Boost 库添加了 ECMAScript/JavaScript 文档。随后出现了争议(见 邮件列表存档),开发人员被要求移除 ECMAScript/JavaScript。禁止的原因包括

  • 与某些较旧的浏览器和一些基于文本的浏览器不兼容。
  • 使打印文档页面变得困难。
  • 通常会导致非常糟糕的用户界面设计。
  • “它通常很烦人。”
  • 将要求 Boost 测试网页的 ECMAScript/JavaScript 兼容性。
  • 使非原始开发人员维护文档变得更加困难。

如果您决定必须使用 JavaScript,请考虑这些原因。特别是请记住,Boost 社区不负责测试您对 JavaScript 的使用。因此,您有责任确保上述问题在您的用例中得到完全解决。

ECMAScript/JavaScript 的使用是允许的,但出于上述原因,我们不鼓励使用。

理由理由

理由被美国遗产词典定义为“某事的基本原因;基础”。

Beman Dawes 评论道:许多软件项目在设计决策时未能提供同期理由,这是一个重大缺陷。缺乏准确的理由会导致问题被无休止地重新审视,当维护人员更改某些内容而没有意识到这样做是为了某种目的时会导致维护错误,并缩短软件的有效寿命。

在做出决策时提供理由相当容易,但在稍后甚至很短的时间内都很难准确地恢复。

致谢理由

随着库的成熟,它几乎总是会积累其他 boost 成员向作者提出的改进建议。承认这些贡献,识别提出建议的人,是 boost.org 文化的一部分。主要贡献通常会在文档中得到承认,而次要修复通常会在代码本身的注释中提到。