2010-11-14 7 views
53

El STL es una pieza fundamental del mundo de C++, la mayoría de las implementaciones derivan de los esfuerzos iniciales de Stepanov y Musser.¿Por qué el código en la mayoría de las implementaciones de STL es tan intrincado?

Mi pregunta es dada la criticidad del código, y es una de las fuentes principales para que las personas vean ejemplos de C++ bien escrito para asombro y propósitos de aprendizaje: ¿Por qué son tan desagradables las diversas implementaciones de la STL? a - ejemplos enrevesados ​​y generalmente buenos de cómo no escribir código C++ desde un punto de vista estético.

Los siguientes ejemplos de código no pasarán la revisión de código en los lugares en los que he trabajado por razones que varían desde nomenclatura variable, diseño, macros y usos de operadores que requieren más que un simple vistazo para descubrir qué está ocurriendo realmente .

template<class _BidIt> inline 
bool _Next_permutation(_BidIt _First, _BidIt _Last) 
{ // permute and test for pure ascending, using operator< 
_BidIt _Next = _Last; 
if (_First == _Last || _First == --_Next) 
    return (false); 

for (; ;) 
    { // find rightmost element smaller than successor 
    _BidIt _Next1 = _Next; 
    if (_DEBUG_LT(*--_Next, *_Next1)) 
     { // swap with rightmost element that's smaller, flip suffix 
     _BidIt _Mid = _Last; 
     for (; !_DEBUG_LT(*_Next, *--_Mid);) 
     ; 
     _STD iter_swap(_Next, _Mid); 
     _STD reverse(_Next1, _Last); 
     return (true); 
     } 

    if (_Next == _First) 
     { // pure descending, flip all 
     _STD reverse(_First, _Last); 
     return (false); 
     } 
    } 
} 


_Ty operator()() 
    { // return next value 
    static _Ty _Zero = 0; // to quiet diagnostics 
    _Ty _Divisor = (_Ty)_Mx; 

    _Prev = _Mx ? ((_Ity)_Ax * _Prev + (_Ty)_Cx) % _Divisor 
     : ((_Ity)_Ax * _Prev + (_Ty)_Cx); 
    if (_Prev < _Zero) 
     _Prev += (_Ty)_Mx; 
    return (_Prev); 
    } 

Tenga en cuenta que no estoy criticando la interfaz, ya que está muy bien diseñada y es aplicable. Lo que me preocupa es la legibilidad de los detalles de implementación.

A preguntas similares se han planteado anteriormente:

Is there a readable implementation of the STL

Why STL implementation is so unreadable? How C++ could have been improved here?

Nota: El código presentado anteriormente se toma de MSVC 2010 algoritmo de cola o cabeceras.

+1

Según lo leí, la pregunta no es realmente "¿Por qué el STL es tan intrincado?" pero está más en la línea de "¿Por qué las implementaciones comúnmente usadas de la STL tienen un código fuente aparentemente complicado?" ¿Es eso correcto? –

+0

@James: Eso es correcto. Además califiqué mi pregunta con una declaración en la parte inferior. –

+2

STL no es exactamente un modelo de objeto que fue ampliamente adoptado en otros idiomas. Pero ese no es el punto de su queja. Este es el tipo de basura que los diseñadores de la biblioteca tienen que pasar cuando el idioma tiene un preprocesador. Ah, y tener que escribir toda la implementación en un encabezado. –

Respuesta

14

Acerca de los nombres de variables, los implementadores de bibliotecas deben usar convenciones de nombres "locas", como los nombres que comienzan con un guión bajo seguido de una letra mayúscula, porque dichos nombres están reservados para ellos. No pueden usar nombres "normales", porque pueden haber sido redefinidos por una macro de usuario.

Sección 17.6.3.3.2 "nombres globales" § 1 establece:

Ciertos conjuntos de nombres y firmas de función son siempre reservados a la aplicación:

  • Cada nombre que contiene una doble subrayado o comienza con un guión bajo seguido de una letra mayúscula que se reserva para la implementación para cualquier uso.

  • Cada nombre que comienza con un guión bajo está reservado a la implementación para usarlo como un nombre en el espacio de nombres global.

(Tenga en cuenta que estas reglas prohíben a los guardias de cabecera como __MY_FILE_H que he visto muy a menudo.)

+0

@ peoro, los espacios de nombres no ayuda para las variables dentro de una clase. –

+6

@peoro namespaces no lo protegen de las macros del preprocesador. '_Names' está reservado para la implementación, por lo que si tiene macros así, está en su cabeza. Por otro lado, no se debe forzar a adivinar qué una implementación de STL puede decidir usar un identificador interno que no debe crear una macro, especialmente porque puede variar desde la implementación hasta la implementación. –

+0

Tienes razón. Me salté "macro" y pensé en las variables/funciones del usuario, lo cual no tenía sentido. – peoro

9

Los nombres de variables por la razón de que este es el código de la biblioteca estándar, y se debe utilizar nombres reservados para detalles de implementación en encabezados. Lo que sigue debe no romper las bibliotecas estándar:

#define mid 
#include <algorithm> 

cabeceras biblioteca Así estándar no pueden usar mid como nombre de variable, por lo tanto, _Mid.El STL era diferente, no era parte de la especificación del lenguaje, se definió como "aquí hay algunos encabezados, úselos como lo hará"

Su código o el mío, por otro lado, no serían válidos si _Mid utilizado como nombre de variable ya que es un nombre reservado - se permite la puesta en práctica de hacerlo:

#define _Mid 

si se siente como él.

Layout - meh. Probablemente tengan una guía de estilo, probablemente la sigan, más o menos. El hecho de que no coincida con mi guía de estilo (y por lo tanto, fallaría mi revisión del código) no es nada para ellos.

Operadores que son difíciles de resolver, ¿a quién le cuesta? El código debe escribirse para las personas que lo mantienen, y GNU/Dinkumware/quien probablemente no quiera dejar que las personas pierdan en las bibliotecas estándar que no pueden descifrar el *--_Next de un vistazo. Si usa ese tipo de expresión, se acostumbrará, y si no lo hace continuará teniendo dificultades.

Yo le daré, sin embargo, que la sobrecarga operator() es un galimatías. [Editar: Lo entiendo, es un generador congruente lineal, hecho muy genéricamente, y si el módulo es "0" eso significa simplemente usar el envolvente natural del tipo aritmético.]

+2

Pero esto rompe las bibliotecas estándar, por supuesto que sí. ¿Cómo pueden definir 'std :: pair' cuando' first' se cambia a nada? –

+0

P.S. Entiendo tu punto, pero este es un mal ejemplo :-) –

+0

@Peter: d'oh! Pensaré en uno mejor. –

2

Sospecho que parte de la razón es que el código en el STL está altamente optimizado. El tipo de código que se implementa tiene un rendimiento mucho más importante que la legibilidad. Debido a que son tan ampliamente utilizados, tiene sentido hacerlos lo más rápido posible.

+6

puede escribir código realmente óptimo y no hacerlo complicado, los STL que se están produciendo son solo variaciones de la biblioteca iniciada hace dos décadas. –

+1

@sonicoder, puedes pero eso no significa que la gente lo haga. –

3

Las implementaciones varían. libc++ por ejemplo, es mucho más fácil para los ojos. Todavía hay un poco de ruido de guión bajo. Como otros han notado, desafortunadamente se requieren los caracteres de subrayado iniciales. Aquí está la misma función en libC++:

template <class _Compare, class _BidirectionalIterator> 
bool 
__next_permutation(_BidirectionalIterator __first, _BidirectionalIterator __last, _Compare __comp) 
{ 
    _BidirectionalIterator __i = __last; 
    if (__first == __last || __first == --__i) 
     return false; 
    while (true) 
    { 
     _BidirectionalIterator __ip1 = __i; 
     if (__comp(*--__i, *__ip1)) 
     { 
      _BidirectionalIterator __j = __last; 
      while (!__comp(*__i, *--__j)) 
       ; 
      swap(*__i, *__j); 
      _STD::reverse(__ip1, __last); 
      return true; 
     } 
     if (__i == __first) 
     { 
      _STD::reverse(__first, __last); 
      return false; 
     } 
    } 
} 
+3

Solo son necesarios para las palabras clave y las macros específicas del compilador, no para los miembros de clase/estructura. aunque la convención ha sido para otro anterior y "M_" o "m_" o anexar un "_" para denotar miembro de clase en lugar de variable local o global. –

+0

Bueno, supongo que hay margen de mejora :) – ergosys

+1

Sin embargo, este código realmente no es tan diferente del código en la pregunta original. Simplemente reformatea la sangría, usa 'while' en lugar de' for' para los bucles y corta las macros de soporte de depuración. Todos caen en la categoría de "problemas estándar de codificación trivial" y ninguno es particularmente importante (bueno, con la excepción de las macros de soporte de depuración, pero si quieres soporte de depuración, necesitas escribir dos implementaciones o usar macros para compilar condicionalmente en). –

16

Neil Butterworth, que figura ahora como "anon", proporciona un enlace útil en su respuesta al pliego de cuestionar "Is there a readable implementation of the STL?". Citando su respuesta allí:

Hay un libro de la biblioteca de plantillas de C++ estándar , co-escrito por los diseñadores STL originales Stepanov & Lee (junto con PJ Plauger y David Musser), que describe una posible implementación, completa con el código - ver http://www.amazon.co.uk/C-Standard-Template-Library/dp/0134376331.

Consulte también las otras respuestas en ese hilo.

De todos modos, la mayor parte del código STL (por STL I significa el subconjunto similar a STL de la biblioteca estándar de C++) es código de plantilla, y como tal debe ser solo de encabezado, y como se usa en casi todos los programas paga para tener ese código lo más corto posible.

Por lo tanto, el punto de equilibrio natural entre concisión y legibilidad está mucho más lejos en el extremo de concisión de la escala que con el código "normal". Además, la biblioteca estándar es donde la vista independiente del sistema del código de la aplicación está conectada al sistema subyacente, utilizando todo tipo de cosas específicas del compilador que usted como desarrollador de aplicaciones debería evitar.

Saludos & HTH.,

+3

Un excelente enlace, voy a agregarlo a la pregunta. +1 –

+1

la concisión es un aspecto, el rendimiento es otro (lo que también puede dañar la legibilidad): las personas siempre se quejan de la grasa y las ineficiencias sospechadas en las implementaciones de AWL. Con el paso de los años, casi todo se ha reducido, pero habrá intercambios ocasionales. offs en legibilidad y elegancia. –

1

para añadir en lo que se ha dicho ya, el estilo que se ve es el estilo de GNU. ¿Feo? Tal vez, eso está en el ojo del espectador. Pero es un estilo estrictamente definido, y hace que todos los códigos se vean de manera similar, a diferencia de los que se resisten a acostumbrarse.

Cuestiones relacionadas