Boost C++ 库

...世界上最受尊敬和设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码规范

Boost 库要求和指南

简介

此页面描述了提交给 Boost 的库的内容的要求和指南。

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

要求

为了避免提出的库被拒绝的挫败感和时间浪费,它必须满足以下要求

  • 许可证必须满足以下许可证要求。 受限许可证,如 GPL 和 LGPL,是不可接受的。
  • 版权所有权必须明确。
  • 该库应该具有普遍的用途。
  • 该库必须满足以下可移植性要求
  • 该库应尽可能满足以下组织要求。 但仅在接受后才需要满足它们。
  • 该库必须相当接近满足以下指南
  • 作者必须愿意参与邮件列表中的讨论,并相应地改进库。

作者无需在提交之前阅读邮件列表一段时间。 然而,已经注意到,以“我刚开始阅读此邮件列表...”开头的提交似乎会失败,而且通常令人尴尬。

许可证要求

满足许可证要求的首选方法是使用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 项目、Makefile、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 测试脚本使用来构建和运行库的测试。 测试构建项目必须位于项目根目录/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 项目来构建库的文档。项目根目录/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 的 "Effective C++" 第二版和 "More Effective C++",均由 Addison Wesley 出版。

使用 C++ 标准库或其他 Boost 库,但前提是收益大于成本。不要使用 C++ 标准库或 Boost 以外的库。请参阅库重用

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

浏览最佳实践手册,了解现有 Boost 库中的想法和源代码链接。

阅读带有单独源文件的库的指南,了解如何确保编译后的链接库满足用户的期望。

使用 C++ 标准库的命名约定(请参阅命名约定的基本原理

  • 名称(除非另有说明)应全部小写,单词之间用下划线分隔。
  • 首字母缩略词应被视为普通名称(例如,xml_parser 而不是 XML_parser)。
  • 模板参数名称以大写字母开头。
  • 宏 (gasp!) 名称全部大写并以 BOOST_ 开头。

选择有意义的名称 - 显式优于隐式,可读性很重要。强烈偏好清晰且描述性的名称,即使名称很长。

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

避免异常说明符。请参阅异常说明符的基本原理

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

提供一个或多个回归测试程序,遵循测试策略和协议

虽然一些 boost 成员在其自己的代码中使用比例字体、制表符和不受限制的行长度,但 boost 广泛分发的源代码应遵循更保守的准则

所有文档文件(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 文化的一部分,承认这些贡献,并确定提出建议的人。主要贡献通常在文档中得到承认,而小的修复通常在代码本身的注释中提到。