Pregunta

Suponiendo un largish de la plantilla de la biblioteca con alrededor de 100 archivos que contiene alrededor de 100 plantillas con un total de más de 200.000 líneas de código.Algunas de las plantillas de utilizar la herencia múltiple para hacer el uso de la biblioteca en sí misma bastante simple (es decir,heredar de alguna base de plantillas y sólo tener que aplicar ciertas reglas de negocio).

Todo lo que existe (crecido a lo largo de varios años), "obras" y se utiliza para los proyectos.

Sin embargo, la compilación de los proyectos de uso de la biblioteca consume una cantidad creciente de tiempo y se toma bastante tiempo para encontrar el origen de ciertos errores.La fijación a menudo causa efectos secundarios inesperados o es bastante difícil, porque algunos interdependientes plantillas de la necesidad de cambiar.La prueba es casi imposible debido a la gran cantidad de funciones.

Ahora, me gustaría simplificar la arquitectura a utilizar menos las plantillas y los más especializados en clases más pequeñas.

Es allí cualquier manera probada para ir sobre esa tarea?¿Cuál sería un buen lugar para empezar?

¿Fue útil?

Solución

No estoy seguro de ver cómo y por qué las plantillas son el problema, y por qué llanura sin plantilla clases sería una mejora.No se que decir solo incluso más clases, menos el tipo de seguridad y para mayor potencial de errores?

Puedo entender la simplificación de la arquitectura, la refactorización y la eliminación de las dependencias entre las distintas clases y plantillas, pero automáticamente suponiendo que "plantillas de menos va a hacer la mejor arquitectura" es errónea la omi.

Yo diría que las plantillas potencialmente permitir la construcción de una mucho más limpio de la arquitectura que se obtendría sin ellos.Simplemente porque usted puede hacer clases separadas totalmente independiente.Sin plantillas, clases de funciones que llamar a otra clase debe saber acerca de la clase o una interfaz hereda, por adelantado.Con las plantillas, esta conexión no es necesaria.

La eliminación de las plantillas sólo conduciría a más las dependencias, no menos.El añadido de seguridad de tipo de plantillas se pueden utilizar para detectar un montón de errores en tiempo de compilación (Espolvorear el código generosamente con el static_assert para este propósito)

Por supuesto, el tiempo de compilación puede ser una razón válida para evitar plantillas en algunos casos, y si sólo tiene un montón de programadores de Java, que están acostumbrados a pensar en la "tradición" programación orientada a objetos términos, las plantillas pueden confundir, lo que puede ser otra razón válida para evitar las plantillas.

Pero a partir de una arquitectura punto de vista, creo que evitando las plantillas es un paso en la dirección equivocada.

Refactorizar la aplicación, claro, suena como lo que se necesita.Pero no tiren una de las herramientas más útiles para la producción de extensible y robusta código sólo porque la versión original de la aplicación se utiliza mal.Especialmente si usted ya está preocupado con la cantidad de código, la eliminación de las plantillas más probable dar lugar a más líneas de código.

Otros consejos

Necesita pruebas automatizadas, de esa manera en diez años, cuando su sucesor tenga el mismo problema, puede refactorizar el código (probablemente para agregar más plantillas porque cree que simplificará el uso de la biblioteca) y sabe que aún cumple con todas las pruebas casos. Del mismo modo, los efectos secundarios de cualquier corrección de errores menores serán visibles de inmediato (suponiendo que sus casos de prueba sean buenos).

Aparte de eso, " divide y vencerás "

Escribir pruebas unitarias.

Donde el nuevo código debe hacer lo mismo que el código anterior.

Esa es una sugerencia al menos.

Editar:

Si desprecia el código antiguo que ha reemplazado con la nueva funcionalidad, puede pasar al nuevo código poco a poco.

Bueno, el problema es que la forma de pensar de la plantilla es muy diferente de la forma basada en la herencia orientada a objetos. Es difícil responder algo más que & Quot; rediseñar todo y comenzar desde cero & Quot ;.

Por supuesto, puede haber una manera simple para un caso particular. No podemos saberlo sin saber más sobre lo que tiene.

El hecho de que la solución de la plantilla sea tan difícil de mantener es una indicación de un diseño pobre de todos modos.

Algunos puntos (pero tenga en cuenta: estos son no malvados. Sin embargo, si desea cambiar a un código que no sea de plantilla, esto puede ayudar):


Busque sus interfaces estáticas . ¿Dónde dependen las plantillas de qué funciones existen? ¿Dónde necesitan typedefs?

Coloque las partes comunes en una clase base abstracta. Un buen ejemplo es cuando tropiezas con el lenguaje CRTP. Simplemente puede reemplazarlo con una clase base abstracta que tenga funciones virtuales.

Buscar listas de enteros . Si encuentra que su código usa listas integrales como list<1, 3, 3, 1, 3>, puede reemplazarlas con std::vector, si todos los códigos que las usan pueden funcionar con valores de tiempo de ejecución en lugar de expresiones constantes.

Rasgos de tipo de búsqueda . Hay mucho código involucrado para verificar si existe algún tipo de definición, o si existe algún método en un código con plantilla típico. Las clases base abstractas resuelven estos dos problemas utilizando métodos virtuales puros y heredando typedefs a la base. A menudo, los typedefs solo son necesarios para activar características horribles como SFINAE , que también serían superfluas.

Plantillas de expresión de búsqueda . Si su código usa plantillas de expresión para evitar crear temporarios, tendrá que eliminarlos y usar la forma tradicional de devolver / pasar temporarios a los operadores involucrados.

Objetos de función de búsqueda . Si encuentra que su código usa objetos de función, puede cambiarlos para usar también clases base abstractas, y tener algo como void run(); para llamarlos (o si desea seguir usando operator(), ¡mejor! Puede ser virtual también ).

Según tengo entendido, ¿está más preocupado por los tiempos de compilación y la capacidad de mantenimiento de su biblioteca?

Primero, no intente " corregir " todo a la vez.

Segundo, entiende lo que arreglas. La complejidad de la plantilla suele existir por una razón, p. para imponer cierto uso y hacer que el compilador lo ayude a no cometer un error. Esa razón a veces puede llegar demasiado lejos, pero arrojando 100 líneas porque & "; Nadie sabe realmente lo que hacen &"; No debe tomarse a la ligera. Todo lo que sugiero aquí puede introducir errores realmente desagradables, te lo advertimos.

Tercero, considere primero las soluciones más baratas: p. máquinas más rápidas o herramientas de construcción distribuidas. Al menos, arroje toda la RAM que tomarán las placas y tire los discos viejos. Hace una diferencia. Una unidad para el sistema operativo, una unidad para la compilación es un RAID de hombre barato.

¿Está bien documentada la biblioteca? Esa es su mejor oportunidad para hacerlo. Busque herramientas como doxygen que lo ayuden a crear dicha documentación.

¿Todo considerado? OK, ahora algunas sugerencias para los tiempos de compilación;)


Comprenda el C ++ modelo de construcción : cada .cpp se compila individualmente. Eso significa muchos archivos .cpp con muchos encabezados = gran compilación. Sin embargo, esto NO es un consejo para poner todo en un archivo .cpp. Sin embargo, un truco (!) Que puede acelerar inmensamente una compilación es crear un único archivo .cpp que incluya un montón de archivos .cpp, y solo alimentar ese & "; Maestro &"; archivo al compilador. Sin embargo, no puede hacerlo a ciegas: debe comprender los tipos de errores que esto podría introducir.

Si aún no tiene uno, obtenga una máquina de compilación separada en la que pueda acceder de forma remota. Tendrás que hacer muchas compilaciones casi completas para verificar si rompiste algunas. Querrá ejecutar esto en otra máquina, que no le impida trabajar en otra cosa. A largo plazo, de todos modos lo necesitará para las compilaciones de integración diarias;)

Utilice encabezados precompilados . (escala mejor con máquinas rápidas, ver arriba)

Verifique su política de inclusión de encabezados . Si bien cada archivo debe ser & Quot; independiente & Quot; (es decir, incluir todo lo que necesita ser incluido por otra persona), no incluir generosamente. Desafortunadamente, aún no he encontrado una herramienta para encontrar declaraciones #incldue innecesarias, pero podría ayudar pasar algún tiempo eliminando los encabezados no utilizados en & Quot; hotspot & Quot; archivos.

Cree y use declaraciones directas para las plantillas que usa. A menudo, puede incluir un encabezado con declaraciones forwad en muchos lugares, y usar el encabezado completo solo en unos pocos. Esto puede ayudar mucho a compilar el tiempo. Compruebe el encabezado <iosfwd> cómo la biblioteca estándar hace eso para las secuencias de E / S.

sobrecargas para plantillas para algunos tipos : si tiene una plantilla de función compleja que es útil solo para unos pocos tipos como este:

// .h
template <typename FLOAT> // float or double only
FLOAT CalcIt(int len, FLOAT * values) { ... }

Puede declarar las sobrecargas en el encabezado y mover la plantilla al cuerpo:

// .h
float CalcIt(int len, float * values);
double CalcIt(int len, double * values);

// .cpp
template <typename FLOAT> // float or double only
FLOAT CalcItT(int len, FLOAT * values) { ... }

float CalcIt(int len, float * values) { return CalcItT(len, values); }
double CalcIt(int len, double * values) { return CalcItT(len, values); }

esto mueve la plantilla larga a una sola unidad de compilación.
Desafortunadamente, esto solo tiene un uso limitado para las clases.

Compruebe si el idioma PIMPL puede mover el código de los encabezados a los archivos .cpp.

La regla general que se esconde detrás de eso es separar la interfaz de su biblioteca de la implementación . Use comentarios, detail nombres de espacios y encabezados .impl.h separados para aislar mental y físicamente lo que debe saber el exterior de cómo se lleva a cabo. Esto expone el valor real de su biblioteca (¿realmente encapsula la complejidad?), Y le da la oportunidad de reemplazar & "; Objetivos fáciles &"; primero.


Consejos más específicos, y cuán útil es el que recibís: depende en gran medida de la biblioteca real.

¡Buena suerte!

Como se mencionó, las pruebas unitarias son una buena idea. De hecho, en lugar de romper su código introduciendo & Quot; simple & Quot; cambios que probablemente se extiendan, solo enfóquese en crear un conjunto de pruebas y corregir el incumplimiento de las pruebas. Tenga una actividad para actualizar las pruebas cuando salgan a la luz errores.

Más allá de eso, sugeriría actualizar sus herramientas, si es posible, para ayudar con la depuración de problemas relacionados con plantillas.

A menudo me he encontrado con plantillas heredadas que eran enormes y requerían mucho tiempo y memoria para crear instancias, pero no era necesario. En esos casos, la forma más fácil de eliminar la grasa era tomar todo el código que no dependía de ninguno de los argumentos de la plantilla y ocultarlo en funciones separadas definidas en una unidad de traducción normal. Esto también tuvo el efecto secundario positivo de desencadenar menos recompilaciones cuando este código tuvo que modificarse ligeramente o la documentación cambió. Parece bastante obvio, pero es realmente sorprendente la frecuencia con la que las personas escriben una plantilla de clase y piensan que TODO lo que tiene que hacer debe definirse en el encabezado, en lugar de solo el código que necesita la información con plantilla.

Otra cosa que quizás desee considerar es la frecuencia con la que limpia las jerarquías de herencia haciendo las plantillas " mixin " estilo en lugar de agregaciones de herencia múltiple. Vea cuántos lugares puede salirse con la suya haciendo que uno de los argumentos de la plantilla sea el nombre de la clase base de la que debería derivarse (la forma en que funciona boost::enable_shared_from_this). Por supuesto, esto normalmente solo funciona bien si los constructores no toman argumentos, ya que no tiene que preocuparse por inicializar nada correctamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top