📜 ⬆️ ⬇️

As I wrote the standard C ++ 11 library or why the boost is so scary. Introduction

Yes - yes, with this motto I rushed into battle.

Instead of the preface


Perhaps this story should begin any story about boost , Loki , independent, and also supplied with the compilers implementations of the standard library C ++.

Yes, yes, and if you thought that the developers of the standard library for the same g ++, clang, Visual Studio, or God forgive me, C ++ Builder (formerly Borland, and the current Embarcadero) are gurus that do not cram down crutches, do not break the standard under their compiler and do not write bicycles, then, most likely, you are not actively using the standard C ++ library as you thought.

The article is written as a story, and contains a lot of “water” and digressions, but I hope that my experience and the resulting code will be useful for those who have encountered similar problems when developing in C ++, especially on older compilers. Link to GitHub with the result today for the impatient and non-readers:
')
https://github.com/oktonion/stdex (commits and constructive criticism are welcome)

And now, first things first.


Table of contents


Introduction
Chapter 1. Viam supervadet vadens
Chapter 2. #ifndef __CPP11_SUPPORT__ #define __COMPILER_SPECIFIC_BUILT_IN_AND_MACRO_HELL__ #endif
Chapter 3. Finding the perfect implementation of nullptr
Chapter 4. C ++ Template "Magic"
.... 4.1 Starting small
.... 4.2 How many we get wonderful errors are compiled by the log
.... 4.3 Pointers and all-all-all
.... 4.4 What else is needed for the template library
Chapter 5
...

Introduction


The year was 2017, C ++ 11 has long been burst into a new stream of new and relatively new compilers, bringing standardized work with threads, mutexes, expanded template programming and standardized approaches to it, there are “big” types long long in the standard , finally got rid of the ubiquitous need to output types for the compiler using auto (goodbye std :: map <type, type> :: const_iterator it = ... - well, you understand me), and a bunch of this possibility with the new for each has become one of the most common used loop implementations by iterators. Finally, we (the developers) were able to humanly tell the user (developer) why the code is not collected using static_assert , as well as enable_if , that selected the required overloads now as if by magic.

It was 2017 in the yard! Already C ++ 17 was actively introduced into GCC, clang, Visual Studio, everywhere there was decltype (since C ++ 11), constexpr (since C ++ 11, but significantly improved), modules are almost on the way, there was a good time. I was at work, and with some disapproval, I looked at the next Internal Compiler Error in my Borland C ++ Builder 6.0, as well as at the many build errors with the next version of the boost library. I think now you understand where this craving for cycling has come from. We used Borland C ++ Builder 6.0 and Visual Studio 2010 under Windows, g ++ version 4.4.2 or lower under QNX and under some unix systems. We were spared from MacOS, which undoubtedly was a plus. There could be no talk of any other compilers (including C ++ 11), for reasons that we leave outside of this article.

“And what could be so complicated there?” - the thought crept into my exhausted attempts to start a boost under the good old builder's brain. “I just need type_traits , thread , mutex , maybe chrono , nullptr would be nice, too ,” I reasoned and set to work.

Chapter 1. Viam supervadet vadens


It was necessary to start somewhere, and start somewhere. Naturally, I had a number of header files and source files scattered around projects with implementations of a similar or identical functionality from the C ++ 11 standard library of my development, as well as honestly borrowed or reworked from codes same gcc and boost. Combining all this together, I got some porridge from functions, classes, macros, which was supposed to turn into an elegant and slim standard library. Assessing the amount of work, I immediately decided to abandon the implementation of everything and everything, limiting myself to developing the “add-on” over the standard C ++ 98 library supplied with the compiler.

In the initial version there was no special adherence to the standard, mainly applied tasks were solved. For example, nullptr looked like this:

#define nullptr 0 

static_assert was solved too simply:

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

std :: to_string was implemented via std :: stringstream , which was replaced by std :: strstream in implementations without the sstream header file, all of which was shoved immediately into the namespace 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(); } } 

There were also "tricks" that are not included in the standard, but nevertheless useful in everyday work, such as the forever or countof macros :

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

countof then transformed into a more C ++ version:

  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)) 

Work with threads (header file from std) was implemented through some of the Tiny libraries, rewritten taking into account the features of the entire compiler zoo and OS. And unless type_traits to some extent was already similar to what the C ++ standard required. 11. There was std :: enable_if , std :: integral_constant , std :: is_const, and similar templates that were already used in development.

  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 { }; } // ...     

It was decided to allocate all non-standard and “compiler” macros, functions, types into a separate header file core.h. And, contrary to the practice of boost, where "switching" implementations with the help of macros are commonly used, the macros related to compiler- dependent things in all library files, except core.h, should be abandoned to the maximum . Also, the functionality that can not be implemented without the use of "hacks" (violation of the standard, relying that Undefined Behavior will be Somewhat Defined), or implemented individually for each compiler (through its build-in macros for example), it was decided not to add to the library, so as not to produce one more monstrous (but wonderful) boost. As a result, the main and practically the only thing core.h is used for is to determine if the built-in nullptr supports (because compilers swear if they override the reserved words), support the built-in static_assert (again to avoid overlapping the reserved word) and support the built-in C ++ types 11 char16_t and char32_t .

Looking ahead, I can say that the idea almost succeeded, because most of the fact that boost is determined depending on the specific compiler by hard macros, in this implementation it is determined by the compiler itself at the compilation stage.

The end of the first chapter. In the second chapter, I will continue the story about the difficulties of dealing with compilers, about the found crutches and elegant solutions in the depths of gcc, boost and Visual Studio, as well as describing my impressions of what I saw and the experience gained with code examples.

Thank you for attention.

Source: https://habr.com/ru/post/417027/


All Articles