我如何编写标准的C ++ 11库,或者为什么boost如此令人恐惧。 引言

是的-是的,我奉行这一座右铭。

而不是前言


也许有了这张图片,关于boostLoki ,独立以及编译器提供的标准C ++库的实现的所有故事都应该开始。

是的,是的,如果您认为同一g ++,clang,Visual Studio的标准库的开发人员,或者上帝原谅我,C ++ Builder(以前是Borland,但现在是Embarcadero)是精打细算的大师,那么他们不会破坏其编译器的标准并且不写自行车,那么很可能您没有像您想的那样积极地使用标准C ++库。

本文写成一个故事,包含很多“水”和题外话,但我希望我的经验和所产生的代码对在C ++中开发时遇到类似问题的人(特别是在较旧的编译器上)有用。 链接到GitHub,为不耐烦的读者和非读者提供今天的结果:

https://github.com/oktonion/stdex (欢迎提交和进行建设性的批评)

现在,首先是第一件事。


目录


引言
第1章。Viam supervadet vadens
第2章。#ifndef __CPP11_SUPPORT__#定义__COMPILER_SPECIFIC_BUILT_IN_AND_MACRO_HELL__ #endif
第3章。找到理想的nullptr实现
第4章C ++模板魔术
.... 4.1我们从小处着手
.... 4.2关于日志为我们编译了多少个奇迹般的错误
.... 4.3指针和所有所有
.... 4.4模板库还需要什么
第五章
...

参赛作品


到了2017年,C ++ 11长期涌入了新的相对较新的编译器流,带来了流,互斥量的标准化工作,互斥量,扩展模板编程和对其的标准化方法,最后,标准中存在“ 长”长类型摆脱了使用auto显示编译器类型的广泛需求(再见std :: map <type,type> :: const_iterator it = ...-嗯,您了解我),并且此功能与每个功能的新功能的结合已成为最常见的功能之一使用循环迭代器实现。 最终,我们(开发人员)可以人工告诉用户(开发人员)为什么不使用static_assertenable_if来收集代码,因为enable_if现在可以选择必要的重载,就像魔术一样。

在院子里是2017年! 已经在GCC,clang,Visual Studio中积极引入了C ++ 17,到处都有decltype (自C ++ 11起), constexpr (自C ++ 11起,但已有显着改进),这些模块即将问世,这是一个好时机。 我当时在工作,有些不赞成的地方是我的Borland C ++ Builder 6.0中的下一个内部编译器错误,以及下一个Boost库版本的大量构建错误。 我想现在您了解这种对自行车制造的渴望是从哪里来的。 对于Windows,我们使用Borland C ++ Builder 6.0和Visual Studio 2010,对于QNX和某些Unix系统,使用g ++ 4.4.2或更低版本。 我们免于MacOS,这无疑是一个加分项。 出于我们不在本文讨论范围内的原因,甚至无法考虑使用其他编译器(包括C ++ 11)。

“在那里可能会变得如此复杂” –一种想法潜入了我竭尽全力的尝试,以期在好的老建筑商的脑海中振作起来。 “我需要的只是type_traitsthreadMutexchrononullptr会很好。” 我推理并开始工作。

第1章。Viam supervadet vadens


有必要从哪里开始,从哪里开始-自然地,我有许多头文件和源代码散布在各个项目中,这些项目的实现与我开发的标准C ++ 11标准库中的功能相似或相同,并且诚实地从该代码中借用或处理过同样的海湾合作委员会和升压。 将所有这些结合在一起,我得到了一些混乱的函数,类和宏,这些东西本应变成优雅而纤细的标准库。 估算了工作量之后,我立即决定放弃所有事情的实现,将自己局限于开发该编译器随附的标准C ++ 98库的“附加组件”。

在最初的版本中,没有特别遵守该标准,主要解决了应用中的问题。 例如, nullptr如下所示:

#define nullptr 0 

static_assert也可以简单地解决:

  #define STATIC_ASSERT(expr) typedef int test##__LINE__##[expr ? 1 : -1]; 

std :: to_string是通过std :: stringstream实现的 ,在没有sstream头文件的实现中, std :: strstream替换了std :: strstream ,所有这些都被立即推送到命名空间std中

  #ifndef NO_STD_SSTREAM_HEADER #include <sstream> #else #include <strstream> namespace std {typedef std::strstream stringstream;} #endif namespace std { template<class T> string to_string(const T &t) { stringstream ss; ss << t; return ss.str(); } } 

还有一些“技巧”没有包含在标准中,但是仍然在日常工作中有用,例如forevercountof宏

  #define forever for(;;) //     #define countof(arr) sizeof(arr) / sizeof(arr[0]) //        C 

countof然后转换为更多的C ++选项:

  template <typename T, std::size_t N> char(&COUNTOF_REQUIRES_ARRAY_ARGUMENT(T(&)[N]))[N]; //        C++ (       ): #define countof(x) sizeof(COUNTOF_REQUIRES_ARRAY_ARGUMENT(x)) 

通过一些Tiny库实现了线程工作(来自std的头文件线程 ),并考虑了整个编译器和OS的功能进行了重写。 也许type_traits在某种程度上已经与C ++ 11标准相似,其中包括std :: enable_ifstd :: integral_constantstd :: is_const等已在开发中使用的模板。

  namespace std { template<bool Cond, class Iftrue, class Iffalse> struct conditional { typedef Iftrue type; }; // Partial specialization for false. template<class Iftrue, class Iffalse> struct conditional<false, Iftrue, Iffalse> { typedef Iffalse type; }; template <bool, class T = void> struct enable_if { }; template <class T> struct enable_if<true, T> { typedef T type; }; template<class Tp, Tp Val> struct integral_constant { // convenient template for integral constant types static const Tp value = Val; typedef const Tp value_type; typedef integral_constant<Tp, Val> type; }; typedef integral_constant<bool, true> true_type; typedef integral_constant<bool, false> false_type; template<bool Val> struct bool_constant : public integral_constant<bool, Val> { }; template<class, class> struct is_same : public false_type { }; template<class Tp> struct is_same<Tp, Tp> : public true_type // specialization { }; } // ...     

决定将所有非标准和“编译器”宏,函数,类型分离到单独的头文件core.h中。 并且,与boost的实践相反,在boost的实践中,广泛使用使用宏的“切换”实现来放弃与所有依赖于编译器的事物相关的宏,但core.h除外 另外,如果不使用“ hacks”(违反标准,依赖未定义行为要进行某种定义)就无法实现该功能,或者为每个编译器单独实现(例如通过其内置宏),因此决定不将其添加到库中,以免产生另一个可怕的(但美丽的)刺激。 结果,使用core.h的主要方法(实际上是唯一的方法)是确定是否支持内置nullptr (因为编译器会宣告覆盖保留字),是否支持内置static_assert (再次避免与保留字相交)以及对C ++内置类型的支持。 11 char16_tchar32_t

展望未来,我可以说这个想法几乎成功了,因为 取决于特定编译器的硬宏在boost中定义的大多数内容,在此实现中,是由编译器本身在编译阶段确定的。

第一章结束。 在第二章中,我将继续讲述与编译器打交道的困难,在gcc,boost和Visual Studio中发现的拐杖和优雅的解决方案,以及对我对代码示例的了解和经验的印象的描述。

谢谢您的关注。

Source: https://habr.com/ru/post/zh-CN417027/


All Articles