如何出版一本技术图书 -- 写在《C++20 高级编程》出版之时

2022-07-01 19:18:53 +08:00
 netcan

原文地址:如何出版一本技术图书 -- 写在《 C++20 高级编程》出版之时

《 C++20 高级编程》介绍

我人生中第一本技术图书《 C++20 高级编程》即将上市,由机械工业出版社出版,目前下单链接:https://item.jd.com/10054930586115.html

C++语言至今拥有 40 多年的历史,目前最新的 C++标准已经到了令人兴奋的 C++20 ,它给我们带来了相当重要的四大特性:概念约束、ranges (范围)标准库、协程以及模块:

本书将深入这些新特性,笔者为它们准备了丰富的代码样例,通过这些代码相信读者很容易掌握这些特性。作为一本讲解 C++高级编程的书,笔者还探讨很多元编程话题,这是作为库开发必不可少的技能,它们也随着 C++的演进而不断演进,大大提高了库开发者的编程体验,尤其是近年来 C++的标准提案经历了从模板元编程向 constexpr 元编程转换过程。

纵观 C++的演进历程可以发现,每一次演进提供的特性大多数和编译时相关,因为它的特点是零成本抽象,允许程序员表达抽象的概念而无需忍受不必要的运行时开销。而一些运行时特性相当少,在面向对象的虚函数特性之后再无运行时特性,或者它们通常以 的形式提供,例如 C++17 起标准库引入的 variant 类型,它通过元编程技术生成虚函数表。

C++对语言特性与库特性区分的非常清楚,它希望程序员能够在不引入语言机制的情况下实现一些功能,例如其他编程语言常常将元组 tuple 作为内建类型,而在 C++中它们以库的方式提供,程序员能够利用现有的语言特性实现这些组件。

如何合理的、高效的运用这些知识,它们背后通常蕴含着什么指导思想?那就是组合式思想,将问题分而治之,它能够应对许多难题。C++语言提供了足够多的抽象机制,允许程序员提出各种假设,并基于这些假设进行灵活组合

本书话题不局限于 C++20 ,现代 C++中一些很多重要的特性也会深入探讨,例如右值引用。一些编程原则,面向对象设计模式也会探讨。最后一章带领读者实现两个库:配置文件反序列库与协程库,它们大量使用 C++20 提供的特性,并使用元编程的方式构建,以对全书知识进行一个总结。

本书要求读者需要有 C++的基础知识,最好能够掌握一些现代 C++的知识,考虑到市面上的书籍以及网络上这方面的资料比较丰富,笔者在提及会引用相关链接供读者查阅。想要系统性的学习 C++20 ,并进阶 C++的技能,那么一定不要错过本书。

本书结构

笔者成书、编程环境采用 Debian bookworm/macOS Monterey/Windows 10 操作系统,编译器用的是 GCC 12.0.0/Clang 14.0.0/MSVC 19.30 ,采用 -std=c++2a/std:c++20编译选项,请读者自行安装这几个编译器。

在代码风格上,笔者力求代码简洁、精巧,例如花括号通常不独立成行,它们常常被压缩成一行;采用两空格缩进等。

全书共分为十章,C++20 的四大特性独立成章,一些小的特性则贯穿于全书,其他章节则探讨了一些面向对象、元编程、函数式编程、并发编程等话题。章节之间尽可能地按照前后依赖顺序组织,读者可以选择从头读到尾,也可以选择感兴趣的部分阅读。

类型与对象

介绍了 C++的类型系统,读者应该能认识到类型在语言中的重要性,类型安全即防止因为类型导致的错误,紧接着介绍 C++对类型处理能力的演进过程。

值类别将表达式划分为左值、右值与将亡值,从而能够表达移动语义;为了解决泛型编程中因为值类别导致重复实现多遍的问题,引入转发引用与完美转发来保留其引用性质。

介绍了类型推导相关知识,这部分知识在库开发中相当重要。

函数对象是另一个重要的概念,使用 lambda 可以简化定义函数对象的难度,标准库提供的函数适配器也能达到运行时多态。

随着union特性的限制放开,对其封装的std::variant更容易地表达运行时值语义多态,这也补充了运行时多态手段。

最后也最为重要的是调试手段,合理利用可事半功倍。

编译时多态

介绍了 C++语言在编译期进行多态的手段,最常见的是函数重载机制,支持这个特性的语言相当少。

函数重载方式给开发人员很大的灵活性,同一个语义拥有不同的实现,交由编译器进行重载决策;操作符重载可以大大提升计算的表达力。灵活的同时带来的复杂度就是语言需要很复杂的规则进行决策。

模板函数在重载过程中可能因为模板参数替换过程中可能失败,而仅仅从候选集中删除该函数并不触发编译错误,利用这个特点开发人员可以基于模板类型的特征( type traits )选择最佳的实现,例如可以针对不同迭代器的特点决策出最优的算法。

通过函数重载机制衍生了一系列相关技术:使用enable_if进行模板类型约束;使用标签分发技术进行偏序决策。使用奇异递归模板模式可以实现代码复用,做到静态多态。

最后介绍表达式模板,在编译期构造表达式树进行延迟计算,避免一些中间计算产生的临时变量,并生成高效的代码。

这些技术或多或少都是因为语言上的缺失导致的变通办法,我们即将看到 C++20 提供的新特性将代替这些技术。

概念约束

concept 非常容易地定义与使用,它解决了模板与泛型编程的很多痛点问题,极大改善了模板与泛型编程的代码质量。它就和基础的语言特性诸如函数、类一样,需要理解才能高效使用。

与未受约束的模板相比,它们没有引入额外的运行时开销。这也符合了 C++的设计原则,不要强迫程序员去做机器能做的更好的事,并且简单的事简单做,以及零成本抽象的哲学。它达到了最初所设想的 C++模板系统应有的样子,而不是语言特性的扩展。它以扩展transform变换算法结束这章。

元编程介绍

元编程技术是由编译器在编译时解析执行代码,并生成最终代码、数据的技术,它是高性能库开发必不可少的技术,通过最少的代价实现更多的功能、满足更大的灵活性,同时最大化复用代码。元编程特性是 C++语言提供的最高抽象手段。这章为后续章节做铺垫。

模板元编程

C++使用模板做元编程拥有足够悠久的历史,并且语言的发展使得元编程的体验越来越好。它为框架、库的开发提供了强有力的手段,通常是零成本抽象的基础。

在 C++中我们能够与编译器交互的工具是类型,通过强大的类型系统能够在编译期完成许多工作,并且能够生成最终高效的代码,元编程的本质是类型计算。

它以两个例子结束这章:定义图 EDSL 语言并于编译时计算最短路径、编译时生成数据表。理智的程序员发现,尽管模板元编程有着各种问题,但仍比起其他方案要好。当然,C++程序员必须学会限制编译期计算和元编程的使用,只有在值得为了代码紧凑性、表达力和运行时性能才引用它们。

constexpr 元编程

早在 2003 年,C++标准委员会提出了用于在 C++中进行常量表达式求值的一种根本不同且明显更好的机制。人们当时使用无类型的宏和简陋的 C 语言定义的常量表达式。另一些人则开始使用模板元编程来计算值,这既乏味又容易出错,constexpr一定程度上简化了元编程难度。

这章提供了constexpr元编程库,它将模板元编程数据结构TypeList以值的形式表达,并利用管道操作符组合对值进行编译时计算,然后使用它重构前一章的例子。最后以编译时操作、生成字符串为例结束这章。

Ranges 标准库

ranges 正式成为 C++20 标准库中的一部分,它的出现代表了标准库自 98 年以来最大的转变。这是重新实现 C++标准库的第一步,在这个标准库中,除了迭代器之外,接口还按照 ranges 指定。

除了提供高质量的 ranges 标准库之外,还从语言层面上使用 concept 提供支持以验证机制的完备性。

range 是对一系列数据的抽象,它即可以是一个有界容器,也可以是一个无穷列表,只要有头有尾。一旦我们有了这种抽象,那么我们可以进一步构造 range 的适配器,它们能够被管道操作符进行组合,以便对数据进行灵活、优雅与有趣地处理,而这些处理是延迟计算的。

它使你的程序通过消除循环来避免大量的状态操作,程序涉及的状态越少,代码的正确性越容易推理,因此 bug 也越少。最后以矩阵乘法、日历库为例结束这章。

无栈协程

协程提供了一种协作式的多任务模型,在并发计算领域,它通常要比多线程或多进程要高效地多。C++20 中的协程仅仅提供了机制,而没有标准库的支持,这将可能留给下一个标准 C++23 中提供。

这章深入探讨了协程机制所要求的接口,以及相关概念。最后通过三个例子结束这章:生成器是协程的经典应用,它是理解协程机制的基础;利用协程机制非侵入式地扩展标准库async接口;利用它封装异常处理流程。

协程机制的理解难度非常大,笔者在此章耗时最长,最初我在 2020 年开始学习协程机制,编写此章的时候仍花了 3 个月时间,期间通过反反复复阅读现有资料,以及编写协程库加深理解。所以读者可能需要花点功夫才能熟练掌握它。

模块

C++20 提供了模块特性,一个库与软件组件化的现代解决方案,它能够像头文件一样在源代码间共享符号,与头文件不同的地方在于,模块并不会泄露宏的定义以及一些私有实现细节。

模块容易被组合,它们能够精确地控制哪些接口需要暴露给导入它们的源文件,并且不会因为导入顺序、宏定义等改变一个模块的语义。

遗憾的是目前主流编译器中对模块的支持都不完备,因此这章对 C++模块的介绍比较保守,没有深入其中的细节,因为它们暂时还无法被验证。

综合运用

本书最后一章将分享笔者的两个的工程项目,它们分别为配置文件反序列化框架与 AsyncIO 协程库,以便对全书的知识进行融会贯通,它们都重度依赖现代 C++提供的特性,尤其是在协程库中。

传统的使用配置文件的方式是每添加一个配置文件都需要程序员去手写解析配置文件到数据结构中的代码,这个过程极其乏味,且容易出错,通过框架为数据结构定义 Schema ,元编程将能够生成读取的接口,避免了这个繁杂的过程。这部分讨论了静态反射话题,目前 C++标准仍没有提供静态反射机制,它依赖宏做代码片段生成。

AsyncIO 协程库是笔者仿照 Python 的标准协程库并使用 C++协程机制实现,它以单线程的事件驱动循环为根基,并在这基础之上封装协程表现层。在最后给出了协程的性能表现。

推荐序(吴咏炜)

我跟罗能认识已经有一年多了。初次相识是在我的一次关于嵌入式系统的 C++ 重构演讲之后,他——和一些其他人一起——就在会后加了我的联系方式。不过,跟其他大部分人不同,我们之间一直确实在“联系”,而且是很频繁的双向联系。原因无他,我们都是真正的 C++ 和编程爱好者。他会把他的一些文章和代码发给我看,而我,在写了一些有( hěn )意( dé)思( yì)的代码和文章时,也会发给他交流。虽然他比我年轻了差不多一辈,但我在他面前并没有多少倚老卖老的资格,反而是有点身为前浪的压力。事实上,我有些实际的工作项目和演讲,已经借鉴了他的想法和代码。

有没有注意到我说的是“C++ 和编程爱好者”?罗能并不只会 C++,他对 Rust 和函数式编程语言(多半还有其他我不知道的语言)都有所涉猎。显然,C++ 仍是他看来最 有用(在没有更好的形容词的情况下,姑且这么说吧)的语言。所以,这本以 C++ 为主题的书,也就成了他的第一本关于编程的书籍。

C++20 是 C++ 在 C++11 之后最大的一次语言变革,里面引入了大量具有革命性的新特性。罗能从一个独特的视角,讲解了其中最重要的四大特性。虽然 C++20 的新特性不止这四种,但编程并不是只讲特性:罗能的独特着眼点在于外面讨论较少的一些高级编程技巧,尤其是模板元编程方面。这比起干巴巴地讨论语言特性,明显要有用得多——毕竟,参考资料我们从 cppreference.com 之类的网站上自己就能找到,而高手的心得并不常有。因此,他的内容编排,也不是基于语言特性,而更多是基于讲解高级编程的逻辑顺序。在讲解了所有这些高级编程的基本概念之后(包括 C++20 的新特性),他通过一章综合运用,把知识点串到一起,展示了非常有意思的实际项目应用。——学语言的关键(不管是编程语言,还是平时说的语言)在于应用,因此这样的讲解是能够真正展现现代 C++ 威力的。基于对高级编程及其应用的深入理解,他讲解相关的 C++ 特性,可谓得心应手、游刃有余。

罗能的书稿我完整地看了,也做了些小小的贡献。限于时间,我没有深入其中所有的细节,但我也已经从中感受到了他对 C++ 和编程的许多独特见解。不夸张地说,我在阅读中也学到了很多新东西。因此,我相信这本书对于 C++ 相关的编程爱好者,一定是有所裨益的——即使对编程老手都是如此。

是为序。

本书评论

吴咏炜,Boolan 首席咨询师,国内知名 C++ 专家

C++20 是 C++ 在 C++11 之后最大的一次语言变革,里面引入了大量具有革命性的新特性。罗能从一个独特的视角,讲解了其中最重要的四大特性。虽然 C++20 的新特性不止这四种,但编程并不是只讲特性:罗能的独特着眼点在于外面讨论较少的一些高级编程技巧,尤其是模板元编程方面。这比起干巴巴地讨论语言特性,明显要有用得多——毕竟,参考资料我们从 cppreference.com 之类的网站上自己就能找到,而高手的心得并不常有。

袁英杰,软件架构咨询师,C++ 资深专家

C++发展到 2x ,在继续坚守 C++的核⼼原则——你⽆需为你不需要的东⻄付出代价——的同时,已经演化为⼀⻔⾮常现代的语⾔,在保证运⾏时效率的同时,让你拥有更多的抽象⼿段,更加优雅地解决很多问题。为了更好地运⽤这⻔语⾔,你需要深刻理解每⼀个新特性的⽬的、机制和⽤法。罗能的这本《 C++20⾼级编程》通过详尽⽽深⼊的解释,丰富的例⼦,会⼤⼤加速掌握这⻔令⼈兴奋的语⾔的进程。

许胜军,华为 Camera 领域资深软件专家

这是一本能让人全面了解 C++20 特性以及高级模板编程的一本书。书中不仅对 C++语言新特性有着详细地描述,更难得的是对其这些特性的设计目的和方法也进行了描述。让人不再限于“会使用”,知其然且知其所以然。虽然这些语言特性,我所在的项目并没有全部都用过,作为同事,也曾和作者本人进行过交流,其关于这些特性的理解能引发我们对 C++编程更深入地思考。总之这是一本独特的精神粮食,值得推荐!

孙孟越,清华大学,罡兴投资量化开发总监

这本书是少有的中文 C++20 精品读物,作者从实践的角度出发,带领大家学习了高级模板编程、ranges 库、协程、modules 等 C++20 的重要内容。作者把自己的经验掰开揉碎了介绍给大家,这是每个 C++程序员提升自我水平的大好机会。

为何编写本书?

我从大学起便有写博客的习惯,这个习惯更多是梳理对技术知识的总结,倘若要寻找人生中的价值,那么记录我曾经存在过这个世界无疑是令人心动的。

我从 14 岁开始接触编程,像我这个年龄接触编程的大多数是起因于学校的 NOI 竞赛,通过他可以保送某些名校(直到大学才知道有这么一条途径),而我却是因为一台 Linux 学习机提供了编程的窗口,从此便一发不可收拾,差点连高中都没考上。

我接触的前两门语言分别是 Bash 和 C 语言,时常用它们写些( Vim 编辑器便是那时学会的,直到工作仍然是我唯一的选择)小工具玩,当我写的越多,我发现 C 语言越简陋,尤其是语言机制上的不足带来的抽象手段有限,程序员不得不使用大量的宏技巧来展开代码避免性能上的开销,以及到处都是细节的面向过程思想。

高中时我开始学习 C++语言,便被它所吸引,只可惜学习机那羸弱的性能编译一个简单的 hello world 程序都需要半分钟,我只好沉浸在C++ Primer Plus的世界里,尤其是面向对象的多态、虚函数机制非常迷人。

C++是自由的,它允许你在遵守规则的情况下做任何事,包括 shoot yourself in the foot ,这就对 C++程序员要求相当高。语言是用来解决问题的工具,因而 C++演进过程中的大多特性都是基于问题与痛点出发的,如果程序员能理解这些特性背后的缘由,在遇到问题时便能使用合适的手段解决之。

直到大学时我才拥有第一台电脑,那时候便尝试写博客,鉴于国内主流博客平台需要审核文章,从此便选择了自建博客,然后同步到知乎平台。直到大学毕业工作的时候,写的多了便会有出版社编辑联系,经过某些原因,我在人民邮电出版社和机械工业出版社之间选择了后者。

在写博客的经历中,我也会时常看看其他大牛的博客、论文、CppCon 技术分享,主要和 C++相关,喜欢 C++并关注 C++的也主要是国外程序员,主要会看如下作者的:

在这期间正好编辑找上门来,问我能不能出一本 C++高级编程相关的书,以弥补国内出版社这方面话题的不足,国外同类书有Modern c++ design, C++ Templates: The Complete Guide等,要么年代比较久远,要么涉及语言细节过深,一下子便激发了我的写作热情:能否用更现代的 C++特性(尤其是 C++20 )去解决那些问题?而且当时国内也没有 C++20 相关的书籍。

从 2021 年 2 月起开始,每天下班后写作,到 2022 年 2 月交稿,正好历时一年,交由出版社走流程花了 4 个月,最近已经开始预售了。有趣的是,正好和另一本国外引进的《 C++20 高级编程》(原书名*Professional C++*)同名了,碰巧的是,我们都请到了吴咏炜作序。

写作部分,我很讨厌排版(尤其是 Word ),更倾向于使用 Markdown/AsciiDoc 等标记语言排版,它们从设计上就让作者更关注内容而不是排版,但考虑 Markdown 等工具不能很好地给出当前写了多少页,加上排版效果比不上 LaTeX ,因此整个过程中我选择了 LaTeX 进行写作。这就导致了最后交稿,出版社不支持 LaTeX 排版,而是从我出的 pdf 文件中重排,最终或多或少有细微影响如字体上的差异。

如果期待写书就能够获得很多收入是不现实的,目前技术书的销量相当确定,如果能达到三千本左右,便是畅销书了,而一本定价的 8%即作为作者的收入,扣掉税后剩下来的就更少了,而投入的时间却相当大。因此如果你计划出书,那一定要考虑收入之外的因素。

本书勘误

此部分留给本书勘误,由于笔者的水平有限,在写作过程中难免留有错误,恳请读者批评指正。

3470 次点击
所在节点    C++
22 条回复
iriyave
2022-07-13 09:10:38 +08:00
正好前几天搜到这本书,今天居然在这看到作者了,下单了,支持一下
edimetia3d
2022-09-11 10:22:37 +08:00
惭愧, 过了这么久才来给个简单的评价. 由于我只看了 Concept 相关的部分, 所以可能会有失偏颇.

首先给出一个总体的评论: 这是一本不错的 Reference, 值得放在手头查阅, 但是可能不太适合作为入门书使用, 读者阅读前应该有一定的经验.

正文:

由于我在阅读之前已经对 Concept 及 Meta Programing 有了一定的认识, 所以读起来总的来说是比较顺畅的, 也确实有查漏补缺的效果.
书的语言风格和内容排布也比较符合我记笔记的风格: 会强调重要的概念, 再列一些简单的例子.当忘记某些内容的时候, 可以更快拿起来. 具体的说, 书中的内容基本把所有 Concept 相关的要点都提到了, 可以作为一本不错的参考书, 这些要点也是我在学习过程中总结过的, 如
1. requires expression 及 concept 自身都有 constexpr bool 的语义
2. `requires requires` 这样的语句出现的场景及其意义.

不过就我个人而言, 阅读这样风格的书可能需要读者需要有一定经验, 书中给出的实践例子看起来比较少, 对于没有经验的读者, 可能会出现看懂之后不知道在哪使用的问题, 进而导致学完就忘, 所以如果要系统学习的话, 可能不太适用于刚入门的程序员.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/863511

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX