
En lugar del prólogo
Quizás con esta imagen debería comenzar cualquier historia sobre
boost ,
Loki , independiente y también implementaciones de la biblioteca estándar de C ++ suministrada con compiladores.
Sí, sí, y si pensabas que los desarrolladores de la biblioteca estándar para el mismo g ++, clang, Visual Studio o, Dios me perdone, C ++ Builder (anteriormente Borland, pero el actual Embarcadero) son gurús que no hacen muletas, no rompen el estándar para su compilador y no escriba bicicletas, lo más probable es que no esté usando la biblioteca C ++ estándar tan activamente como pensaba.
El artículo está escrito como una historia y contiene mucha "agua" y digresiones, pero espero que mi experiencia y el código resultante sean útiles para aquellos que enfrentaron problemas similares al desarrollar en C ++, especialmente en compiladores más antiguos. Enlace a GitHub con el resultado de hoy para impacientes y no lectores:
https://github.com/oktonion/stdex (se aceptan confirmaciones y críticas constructivas)
Y ahora, lo primero es lo primero.
Tabla de contenidos
Introduccion
Capítulo 1. Viam supervadet vadensCapítulo 2. #ifndef __CPP11_SUPPORT__ #define __COMPILER_SPECIFIC_BUILT_IN_AND_MACRO_HELL__ #endifCapítulo 3. Encontrar la implementación nullptr perfectaCapítulo 4. Magia de plantilla de C ++....
4.1 Comenzamos pequeño....
4.2 Acerca de cuántos errores milagrosos compila el registro para nosotros....
4.3 Punteros y todo-todo-todo....
4.4 ¿Qué más se necesita para la biblioteca de plantillas?Capitulo 5
...
Entrada
Era 2017, C ++ 11 ha estallado en un nuevo flujo de compiladores nuevos y relativamente nuevos, trayendo trabajo estandarizado con flujos, mutexes, ampliando la programación de plantillas y estandarizando enfoques, hay tipos
largos largos "grandes"
en el estándar , finalmente se deshizo de la necesidad generalizada de mostrar tipos para el compilador usando
auto (goodbye
std :: map <type, type> :: const_iterator it = ... - bueno, me entiendes), y la combinación de esta función con la nueva
para cada uno se ha convertido en una de las más comunes implementaciones de iterador de bucle utilizado. Finalmente, nosotros (los desarrolladores) pudimos decirle humanamente al usuario (desarrollador) por qué el código no se está construyendo usando
static_assert , así como
enable_if , que ahora selecciona las sobrecargas necesarias como por arte de magia.
En el patio fue 2017! C ++ 17 ya se introdujo activamente en GCC, clang, Visual Studio, en todas partes había
decltype (desde C ++ 11),
constexpr (desde C ++ 11, pero mejoró significativamente), los módulos estaban casi en camino, hubo un buen momento. Estaba en el trabajo y, con cierta desaprobación, miré el siguiente error interno del compilador en mi Borland C ++ Builder 6.0, así como muchos errores de compilación con la próxima versión de la biblioteca de impulso. Creo que ahora entiendes de dónde vino este deseo de construir bicicletas. Utilizamos Borland C ++ Builder 6.0 y Visual Studio 2010 para Windows, g ++ versión 4.4.2 o inferior para
QNX y para algunos sistemas unix. Nos ahorramos MacOS, lo que sin duda fue una ventaja. Ningún otro compilador (incluido C ++ 11) podría considerarse por razones que dejamos fuera de este artículo.
"Y qué podría ser tan complicado allí", un pensamiento se deslizó en mis intentos exhaustivos de impulsar el buen cerebro del viejo constructor. "Todo lo que necesito es
type_traits ,
thread ,
mutex , quizás
chrono ,
nullptr sería bueno". Razoné y me puse a trabajar.
Capítulo 1. Viam supervadet vadens
Era necesario comenzar desde dónde y comenzar desde dónde, naturalmente, tenía una cantidad de archivos de encabezado y códigos fuente dispersos en los proyectos con implementaciones de funcionalidad similar o idéntica de la biblioteca estándar C ++ 11 estándar de mi desarrollo, así como prestado o procesado honestamente a partir de los códigos de ese mismo gcc y boost. Combinando todo esto, obtuve un montón de funciones, clases, macros que se suponía que se convertiría en una biblioteca estándar elegante y esbelta. Habiendo estimado la cantidad de trabajo, inmediatamente decidí abandonar la implementación de todo y todo, limitándome al desarrollo de un "complemento" sobre la biblioteca estándar de C ++ 98 que viene con el compilador.
En la versión inicial no había una adherencia particular al estándar, principalmente se resolvieron los problemas aplicados. Por ejemplo,
nullptr se veía así:
#define nullptr 0
static_assert también se resolvió simplemente:
#define STATIC_ASSERT(expr) typedef int test##__LINE__##[expr ? 1 : -1];
std :: to_string se implementó a través de
std :: stringstream , que fue reemplazado por
std :: strstream en implementaciones sin el archivo de encabezado
sstream , y todo esto se introdujo inmediatamente en el
espacio de nombres 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(); } }
También había "trucos" que no estaban incluidos en el estándar, pero que, sin embargo, eran útiles en el trabajo diario, como las
macros forever o
countof :
#define forever for(;;)
countof luego se transformó en una opción más C ++:
template <typename T, std::size_t N> char(&COUNTOF_REQUIRES_ARRAY_ARGUMENT(T(&)[N]))[N];
El trabajo con subprocesos (el
subproceso del archivo de encabezado de std) se implementó a través de algunas de las bibliotecas de Tiny, reescrito teniendo en cuenta las características de todo el zoológico de compiladores y el sistema operativo. Y quizás
type_traits en cierta medida ya era similar a lo que requería el estándar C ++ 11. Había
std :: enable_if ,
std :: integral_constant ,
std :: is_const y las plantillas similares que ya se usaban en el desarrollo.
namespace std { template<bool Cond, class Iftrue, class Iffalse> struct conditional { typedef Iftrue type; };
Se decidió separar todas las macros, funciones y tipos no estándar y "compiladores" en un archivo de encabezado separado
core.h. Y, contrariamente a la práctica de boost, donde se utilizan ampliamente las implementaciones de "cambio" utilizando macros, para abandonar las macros relacionadas con elementos
dependientes del compilador en todos los archivos de la biblioteca, excepto
core.h. Además, la funcionalidad que no se puede implementar sin el uso de "hacks" (violación del estándar, confiando en que el comportamiento indefinido esté algo definido), o se implementa individualmente para cada compilador (a través de sus macros incorporadas, por ejemplo), se decidió no agregar a la biblioteca, para no producir otro impulso monstruoso (pero hermoso). Como resultado, lo principal y prácticamente lo único para lo que se usa
core.h es determinar si hay soporte para
nullptr incorporado (porque los compiladores juran si anulan las palabras reservadas), soporte para
static_assert incorporado (nuevamente, para evitar la intersección de una palabra reservada) y soporte para tipos incorporados C ++ 11
char16_t y
char32_t .
Mirando hacia el futuro, puedo decir que la idea fue casi un éxito, porque La mayor parte de lo que se define en el impulso por macros duras dependiendo de un compilador particular, en esta implementación, está determinado por el compilador en la etapa de compilación.
El final del primer capítulo. En el
segundo capítulo, continuaré la narración sobre las dificultades de tratar con compiladores, sobre muletas encontradas y soluciones elegantes en las entrañas de gcc, boost y Visual Studio, así como una descripción de mis impresiones de lo que vi y obtuve experiencia con ejemplos de código.
Gracias por su atencion