开发说明

  关于环境配置,详见运行。当前没有其它的面向最终用户的说明。

  以下为面向开发者的说明。

准备

  YSLib 项目文档位于 YSLib 项目而不是本 wiki 项目中。

  维护者参考的细节和一般规则详见 YSLib 项目文档 doc/ProjectRules.txt ;术语的完整定义详见 YSLib 项目文档 doc/CommonRules.txt

项目过程

  基本规则参见 YSLib 项目文档 doc/ProjectRules.txt

  本 wiki 项目作为用户手册和开发者补充文档的形式作为实现及之后阶段输出。

  实现的附加输出为库和工具。

  之前阶段(如设计)为前期过程,其文档和适用于维护过程的项目规则位于 YSLib 项目文档(位于 doc/ )。

  其中整体过程由 doc/Designation.txt 指定。当前内联设计以外的过程,因此不存在设计外前期过程的单独文档。

开发

平台

  关于外部依赖平台目标平台宿主平台等概念的一般定义参见术语概要

  YSLib 项目约定一个体系结构和使用的外部依赖是一个平台。

  YSLib 支持不同的目标平台。类似 ISO C/C++ 的独立实现(freestanding implementation) 和宿主实现(hosted implementation) ,平台分为两类:独立实现平台宿主实现平台。后者存在操作系统的支持而前者没有。

语言使用和实现要求

  本节适用于本项目,不直接限制依赖项和用户程序。项目中特定部分的规则及适用性详见 YSLib 项目文档 doc/ProjectRules.txt

  使用 ISO C++ 作为主要开发语言。

  不使用和 ISO C++03 以后被接受的特性不兼容的特性,包括但不限于:

  • 被取消的特性,如 export 关键字;
  • 在 ISO C++03 中标记为 deprecated 而在之后版本去除的特性,如 const char 数组类型左值到 char* 右值的转换,
  • 在 ISO C++03 中标记为 deprecated 但在之后版本重新取消 deprecated 的特性,如修饰命名空间作用域声明的 static
  • 实现的 Defect Report ,如 CWG 615

  注意即使使用特定模式,一些实现也可能引入之后的 Defect Report 而不保证兼容,如 GCC PR65890

  关于精确的特性使用规则、具体使用及备选的特性的清单等,详见标准使用(英文)

  以下对语言实现的要求和支持情况适用整个项目。具体内容可能会在未来变动。

基准实现要求

  默认基于 ISO C++11 环境,但并不要求实现完整支持所有特性。

  依赖 ISO C++ 独立实现或宿主实现,附加以下要求:

  • 满足 ISO C++11 [implimits] 建议的最小实现要求。
  • 标准库要求基于 ISO C++11 定义的宿主实现。
    • 需以下整数类型( ISO C++11 中为可选支持):
      • 定宽整数 std::intN_tstd::uintN_t (其中 N 为 8 、 16 、 32 或 64 );
      • 类型 std::uintptr_t
    • 满足以下实现定义行为的要求:
      • 至少支持 std::placeholders::_7(按 ISO C++11 Annex B [implimits] ,符合标准的下限为 10 )。
    • 假定用于迭代器 difference_type(含 std::ptrdiff_t )或坐标计算的有符号整数作为显式转换的目标类型且结果不能在范围内表示时,不引起副作用且结果的值不是能在此范围内表示的任意值(即为小于 0 的值)。
      • 当前标准中结果由实现定义。WG21 P0907 已提议修改使用补码表示,符合此要求。
    • 假定特定类型的特定操作无异常抛出(但不依赖异常规范的行为),当前包括:
      • std::string 的默认构造函数( WG21 N4002 引入了显式指定 noexcept ,仅从标准草案 WG21 N4296 起有效)。
  • 实现已解决标准使用(en-US) 中的 Workarounds 列出的 Defect Report 问题。
  • 假定被包含在具有外部链接实体的函数体或声明命名空间作用域中外部链接名称的被 ODR 使用(odr-used) 的 lambda 表达式相同。
  • 对宿主环境中程序外部的状态的并发修改不引起未定义行为。
    • 现有操作系统和文件系统提供的接口和实现普遍不能保证避免 TOCTTOU 访问(en-US) 导致的问题。具体修改的结果未指定,但应不直接引起无法预测的程序行为。
    • 除非另行指定,本项目的实现不保证检查外部程序的修改。

假定 YSLib 实现和用户程序的代码满足以下要求:

  • 假定异常和标准库 RTTI 对象满足 ODR ,即使是在使用动态库的宿主实现中,但影响用户代码生成的实现的二进制约定(如 ARM64 )除外。
    • 这要求用户代码不依赖影响相关符号可见性而导致 ODR 失效的特性。
      • 例如,这不允许如 dlopen 使用 RTLD_LOAD 加载具有相关符号的库。
    • 这允许 std::type_info 的比较操作的散列操作的高效实现。
      • 使用 libstdc++ 时,需要定义宏 __GXX_MERGED_TYPEINFO_NAMES1
        • 这对应默认选项的修改
        • 一般不调整 __GXX_TYPEINFO_EQUALITY_INLINE ,使用默认选项。
      • 其它实现暂不使用选项支持,以避免和二进制约定冲突。
        • libc++ 不支持另行调整,由实现根据平台环境指定 _LIBCPP_HAS_NONUNIQUE_TYPEINFO
    • 链接时除实现的二进制约定,假定 std::type_info 相关的符号被共享。
      • 这不允许在实现生成以外显式地隐藏相关的符号(如通过 #pragma 预处理指令或 -fvisibility=hidden 编译器命令行选项)。
      • 这一般要求在兼容 GNU ld 的链接器在命令行选项中使用 --dynamic-list-cpp-typeinfo 等方式导出相关符号(也允许但不要求使用 -rdynamic-export-dynamic )。
  • 假定在标准库宏 NDEBUG 被定义的翻译单元中的代码不违反异常规范。
    • 这允许改进代码生成,如使用 G++ 的 -fno-enforce-eh-specs 选项
    • 程序应不依赖违反异常规范时调用标准库的函数的行为。否则,程序行为未定义。

历史实现要求

  以下要求已被修改或取消。

  • 假定提供撤销标准库未定义行为的保证:
    • 撤销 [res.on.functions]/2.5 对特定不完整类型作为模板实际参数引起未定义行为的限制,包括:
      • 使用默认分配器( std::allocator 的实例)的 std::vectorvalue_type 类型。
        • 这被包含在 WG21 N4510
        • ystdex::pmr::pool_resource 的实现从 b843[2018-11-10] 起依赖这项特性。
        • 从 b863[2019-07-26] 起不使用不完整类型的元素而不再依赖这项特性。
      • 使用默认分配器( std::allocator 的实例)的关联容器(即 std::mapstd::set )的 value_type 类型。
        • std::vectorstd::liststd::forward_listWG21 N4510 引入了不完整类型的支持。这不在此处要求。关联容器的要求预期在未来被添加。
        • 已知 libstdc++ 的实现符合这个条件。
        • 在 YSLib 中仅被 YSLib::ValueNode 通过在 std::map 使用递归的键类型的实现从 b338[2012-09-13] 起依赖。
        • 因为使用 ystdex::map 替代 std::map ,从 b830[2017-08-11] 起取消这个要求,不再依赖标准库实现提供的扩展特性。
  • 假定特定类型的特定操作无异常抛出(但不依赖异常规范的行为),包括:
    • std::function::swapLWG 2062 起有效)。
      • 因为使用 ystdex::function 替代 std::function ,从 b848[2018-12-24] 起取消这个要求。

可选实现支持

  允许使用 ISO C++11 以后兼容最新标准草案的正式标准中的特性(可通过 __cplusplus 宏和 SG10 建议的特性检查判断)。

其它

  不依赖实现的方言扩展,但在确保实现能支持时,在特定的代码中可通过条件包含等方式选用。

  若实现默认具有不符合标准的特性,在本项目的代码中不依赖这些特性,即便外部依赖项可能对此进行配置(如 MinGW G++ 为了和 Microsoft VC++ 兼容启用的 -mms-bitfields ,而 MSYS2 安装的 freetype2 的 pkg-config 的 CFLAGS 隐含此参数)。

保留名称

  YSLib 项目中,除了 YBase.LibDefect 是对标准库实现的修正外,并不是语言的实现,因此公开接口遵循 ISO C++ 对保留名称的使用,如不引入以 __ 起始的标识符。

  对实现环境已经以保留标识符提供的接口,适用以下规则:

  • <ydef.h> 提供宏包装特定实现的标识符。
  • 除标准预定义的(如 __cplusplus )和用于特性检查的标识符(以 __cpp__has 起始),以及上述被包装时的宏定义,不在注解(作为宏 YB_ATTRYB_ATTR_STD 的参数)外直接使用保留标识符。

  <ydef.h> 和其它一些 YSLib 项目头文件保留特定的不被标准保留的标识符,详见 YSLib 项目文档 doc/Definitions.txt

库概述

  YSLib 项目由多个子项目组成。其中主要的有顶级子项目:YBase 和 YFramework 。它们是开发 YSLib 应用的必备的库。每个库被构建为单独的映像(静态库或动态库)。

  YSLib 的组件有些是依赖于特定平台的,但更多是平台中立的。关于库的组件在此的不同,详见下文的解释。

  静态库、动态库或其它可能被库构建时依赖的输入以及构建使用的工具是库构建的依赖项。关于依赖项的一般说明,详见术语中关于依赖管理的说明。

平台模拟

  除非另行指定,文档中的狭义的“模拟”概念指程序模拟。

  YSLib 项目中,**平台模拟(platform emulation)**主要指直接以运行时环境适配层嵌入宿主平台运行时,在具体程序中提供类似被模拟的目标平台的具体特性和接口。

  完整的定义详见 YSLib 项目文档 doc/CommonRules.txt

外部依赖项

  外部依赖项在构建项目的上下文中,指构建时可能使用的外部依赖,是不由本项目维护和单独发布的依赖项。

  YSLib 项目严格使用 ISO C++ 的子集和特定实现的可选的扩展。关于依赖的语言特性,参见这里(en-US)

  在所有目标平台上,除了系统库外,外部依赖项是相同的,但可能使用不同的版本,也不一定按相同的配置构建。系统库的概念和 GNU GPLv3system library exception 中的 system library 定义类似,在此指特定平台或操作系统提供运行时支持的、由特定第三方环境提供开发支持的外部依赖,例如提供特定平台的 ISO C++ 标准库部分实现的 libstdc++ 和提供 Windows API 实现的 GDI32 等。

  除了系统库外,一部分外部依赖项可选或必须使用自行构建的(可能被修改的)版本。这些外部依赖项的修改和构建脚本位于版本库的 3rdparty 目录,默认按原始许可证发行。

  YBase 只直接依赖 ISO C++ 标准库。

  YFramework 默认依赖经过修改的 FreeType2 和 FreeImage 。其中前者当前仅修改头文件,经过特别处理和官方发布的直接构建的版本二进制兼容,可以被系统库替换。

  当前版本中,不同宿主平台对应的静态库文件(后缀名为 .a )查找的目录如下:

  • /YFramework/DS/lib
  • /YFramework/MinGW32/lib
  • /YFramework/MinGW64/lib
  • /YFramework/Android/lib
  • /YFramework/Linux/lib

  其中,使用的 FreeImage 静态库对应 YFramework 的 debug 和非 debug 配置,文件名分别为 libFreeImaged.alibFreeImage.a 。这可在同一个目录树中共存。

  (当前 Android 和 Linux 仅支持单一本机体系结构,实际仅测试 Android ARMv7 和 Linux x86_64 。)

  构建 YFramework 时需包含 3rdparty/include 目录的头文件。 Sysroot 安装脚本 Tools/install-sysroot.sh 会复制包括上面的头文件在内的文件。

  当前已经使用的详细外部依赖项详见 YSLib 项目文档 doc/Dependencies.txt

注意 版本库历史中包括静态库(.a) 文件,但为减少版本库大小,不再更新且可能移除,使用外部源或自行构建的方式替代。参见归档获取单独配置或和源代码目录集成的已编译的外部依赖项。

  在 build 885 之前,版本库历史的对应的宿主平台中的静态库位于以下位置:

  • /YFramework/DS/lib
  • /YFramework/MinGW32/lib-i686
  • /YFramework/Android/lib
  • /YFramework/Linux/lib-x86_64

  其中 Android 平台只包括 FreeType2 ,Linux 平台只包括 FreeImage 。其它平台包括 FreeType2 和 FreeImage 库文件。

  其它平台中,只有随其它文件的发布版本包含完整更新,否则压缩包中可能只有其中一个库文件。

  关于自行构建外部依赖项的方法,参见构建说明

模块

  YSLib 项目组织为一个逻辑上的树形结构,其叶节点称为模块。类似文件系统,非叶节点称为模块目录。在源代码中使用模块路径来标识不同的模块,其分隔符为 :: 。在 YSLib 项目下直接划分的顶级子项目的名称仅在必要时出现在模块路径中,完整模块路径一般从次级子项目的名称起始。

  C 和 C++ 源代码的一个模块由以下三种形式之一构成:

  • 一个头文件
  • 一个非头文件的源文件
  • 一个头文件和对应的源文件

  作为公开接口的模块是公开模块。公开模块的头文件在单独的目录中以便部署。

  按 ISO C 和 ISO C++ 规定, C 或 C++ 模块中存在的非头文件的源文件和包含的头文件构成一个 C 或 C++ (预处理)翻译单元,简称单元。注意这里包含的头文件不仅限于模块中的头文件。

  关于模块的进一步说明以及模块路径的形式文法和头文件依赖的基本规则,参见 YSLib 项目文档 doc/ProjectRules.txt

文件系统布局

  作为 C++ 项目, YSLib 把每个顶级子项目的源文件和公开模块头文件分别保存在不同的目录中,即 includesource 。非公开模块若有头文件,也位于 source

  特定于目标平台配置的代码会直接位于 平台名 目录下,称为平台扩展。对应的两个目录为 平台名/include平台名/source

  除了平台扩展的内容,文件系统目录和模块目录的每一级对应。平台扩展的模块目录名是对应平台中立部分加上后缀 _(平台名)

  举例:顶级子项目 YFramework 下的次级子项目 Helper 的非平台扩展的源代码在目录 YFramework/include/HelperYFramework/source/Helper 中,它的 DS 平台扩展的源代码位于 YFramework/DS/include/HelperYFramework/DS/source/Helper 中。

  提供平台扩展的次级子项目只有 YCLib 和 Helper 。

  编译项目时包含的头文件是合并的,如编译器命令行 -IYFramework/DS/include -IYFramework/include 在一次编译中同时使用平台中立和特定于平台 DS 的模块的 YFramework 头文件。

YSLib 及其本体

  在 YBase 和 YFramework 分离之前, YSLib 是一整个库。原 YSLib 大部分仍然在 YFramework 中,仍然可称为 YSLib ,是一个 YFramework 下的次级子项目。注意和整个项目名的不同,以下称为 YSLib 库,以示区分。

  YSLib 库中, Adaptor 用于适配特定于具体外部依赖的接口。可以通过修改其中的代码替换外部依赖,包括部分标准库兼容接口。

  其它部分的接口和实现都是严格平台中立且不依赖外部特定接口而变化的,称为本体。本体中提供了 YSLib 的主要功能。

Helper

  若需要开发依赖平台特定的应用,本体接口可能不足,而需要使用平台扩展。此外,可能需要一些便利功能。

  在 YSLib 库之上,Helper 对此类需求提供了一致而灵活的接口。

  若需要更接近特定平台实现的接口,可以使用 YCLib 及其平台扩展。

  YCLib 和 Helper 在宿主实现上都提供了更加丰富的功能。

代码规范

  YSLib 项目维护的代码规范符合 YSLib 项目中的文档约定的规则,包括:

  • doc/CommonRules.txt 一般规范
  • doc/ProjectRules.txt 项目规范
  • doc/LanguageConvention.txt 语言使用约定

代码格式化

  doc/CommonRules.txt 中规定了命名风格和参考的代码格式。由于格式化代码涉及语义分析,并不保证可以完全自动化进行,需要在编码时注意调整。

  以下工具配置可以把其中的主要工作自动化进行:

  • clang-format
    • 可通过 MSYS2 包 mingw-w64-i686-clangmingw-w64-x86_64-clang 安装,以下配置以这里的 3.7 版本为基准测试
    • 配置选项的文档参见这里
    • 通过命令行 -style= 指定使用选项文件 Tools/YSLib.clang-format
    • 注意 至少以下格式需要手动调整
    • 通过命令行 -i 直接编辑文件而不是打印结果到标准输出
    • 命令行示例: find YBase YFramework YSTest -name "*.h" -o -name "*.hpp" -o -name "*. cpp" | xargs clang-format -i -style=file
    • 命令行示例: find YBase YFramework YSTest -name "*.h" -o -name "*.hpp" -o -name "*. cpp" | xargs -i sh -c "clang-format -i -style=file {}"
    • 命令行示例(同时替换 template < ): find YBase YFramework YSTest -name "*.h" -o -name "*.hpp" -o -name "*. cpp" | xargs -i sh -c "clang-format -i -style=file {} && sed -bi 's/template </template</g' {}"
  • astyle
    • 可通过 MSYS2 包 mingw-w64-i686-astylemingw-w64-x86_64-astyle 安装,以下命令行以这里的 2.05.1 版本为基准测试
    • (版本 2.05 )命令行选项的文档参见这里
      • --help 取得的文档可能有问题--delete-empty-lines 对应的短选项应为 -xe 而不是 -xd
    • 使用命令行选项 -A1 -T -p -U -k1 -xj -xy -xC80
      • 短选项可以缩写
      • 和上述 clang-format 比较,少了部分功能,主要有
        • AlignAfterOpenBracket: DontAlign
        • AlignTrailingComments: true
        • AlwaysBreakAfterReturnType: All
        • MaxEmptyLinesToKeep: 2
    • 注意 至少以下格式需要手动调整
      • 无法正确识别 constexpr 导致错误的缩进
      • 虽然右值引用识别问题已解决,但实测对模板参数无效
      • extern "C" 块冗余缩进
      • 断行后的缩进
      • lambda 表达式的捕获列表中的 = 周围的冗余空格以及对应的 { 断行
      • static_cast 等关键字后的 <> 周围的冗余空格
      • 宏实际参数列表头部的 ( 和作为第一个参数的标点可能有冗余空格
      • 宏实际参数列表尾部的 ,) 没有以空格隔离
      • 初始化数组的列表 { 和之前的 ] 存在冗余空格
    • 不使用 -xp ,尽管一些注释需要(移除行首 * 后保持一级缩进)的此类格式,但它会不必要地影响大部分 Doxygen 注释块
    • 默认备份文件后缀 .orig ;可选使用 -n 取消备份文件,或 --suffix= 修改备份文件后缀
    • 使用 -r 递归处理子目录
    • 可选使用 -v 显示详细过程
    • 可选使用 -Q 只显示被处理的文件
    • 可选使用 --dry-run 不实际处理文件
    • 命令行示例: astyle -vQnrA1TpUk1xjxyxC80 YBase/*.h* YBase/*.cpp YFramework/*.h* YFramework/*.cpp YSTest/*.h* YSTest/*.cpp

参考

  • YSLib 项目文档 doc/Dependencies.txt 了解组织结构、开发规则、默认使用的外部依赖项(包括语言实现)和相关约定
  • 结构和特性 中的树形结构
  • YSLib 项目文档 doc/ProjectRules.txt 了解组织结构、开发规则和相关约定
  • YSLib 项目文档 doc/YBase.txt 了解顶级子项目 YBase
  • YSLib 项目文档 doc/YFramework.txt 了解顶级子项目 YFramework
  • YSLib 项目文档 doc/YSLib.txt 了解 YFramework 的次级子项目 YSLib