refactoring español - C / C++:¿Detectando#incluidos superfluos?




evacuación que (17)

Aquí hay una forma simple de fuerza bruta para identificar encabezados superfluos incluidos . No es perfecto, pero elimina los "obvios" innecesarios que incluye. Deshacerse de estos va un largo camino en la limpieza del código.

Se puede acceder a los scripts directamente en GitHub.

A menudo encuentro que la sección de encabezados de un archivo se hace cada vez más grande, pero nunca se hace más pequeña. A lo largo de la vida de un archivo fuente, es posible que las clases se hayan movido y refaccionado y es muy posible que existan algunos #includes que ya no necesitan estar allí. Dejarlos allí solo prolonga el tiempo de compilación y agrega dependencias de compilación innecesarias. Tratar de averiguar cuáles son todavía necesarios puede ser bastante tedioso.

¿Existe algún tipo de herramienta que pueda detectar directivas #incluidas superfluas y sugerir cuáles puedo eliminar de forma segura?
¿La pelusa hace esto tal vez?


Pensé que PCLint haría esto, pero han pasado algunos años desde que lo miré. Deberías revisarlo.

Miré este blog y el autor habló un poco sobre la configuración de PCLint para encontrar los objetos no utilizados. Podría valer la pena un vistazo.


Para terminar esta discusión: el preprocesador de c ++ se está completando. Es una propiedad semántica, si una inclusión es superflua. Por lo tanto, del teorema de Rice se desprende que es indecidible si una inclusión es superflua o no. NO PUEDE haber un programa que (siempre correctamente) detecte si una inclusión es superflua.


El problema con la detección de las aplicaciones superfluas es que no puede ser simplemente un comprobador de dependencia de tipo. Una inclusión superflua es un archivo que no proporciona nada de valor a la compilación y no altera otro elemento del que dependen otros archivos. Hay muchas maneras en que un archivo de encabezado puede alterar una compilación, por ejemplo, definiendo una constante, redefiniendo y / o eliminando una macro usada, agregando un espacio de nombres que altera la búsqueda de un nombre de alguna manera en la línea. Para detectar elementos como el espacio de nombres necesita mucho más que un preprocesador, de hecho casi necesita un compilador completo.

La pelusa es más un verificador de estilo y ciertamente no tendrá esta capacidad completa.

Creo que encontrará que la única forma de detectar una inclusión superflua es eliminar, compilar y ejecutar suites.


He intentado usar Flexelint (la versión unix de PC-Lint) y tuve resultados un tanto mezclados. Esto es probable porque estoy trabajando en una base de código muy grande y con nudos. Recomiendo examinar cuidadosamente cada archivo que se informa como no utilizado.

La principal preocupación son los falsos positivos. Las inclusiones múltiples del mismo encabezado se informan como un encabezado innecesario. Esto es malo ya que Flexelint no le dice en qué línea está incluido el encabezado o dónde se incluyó antes.

Una de las formas en que las herramientas automatizadas pueden hacer esto mal:

En A.hpp:

class A { 
  // ...
};

En B.hpp:

#include "A.hpp

class B {
    public:
        A foo;
};

En C.cpp:

#include "C.hpp"  

#include "B.hpp"  // <-- Unneeded, but lint reports it as needed
#include "A.hpp"  // <-- Needed, but lint reports it as unneeded

Si sigue ciegamente los mensajes de Flexelint, arruinará sus #incluidas dependencias. Hay más casos patológicos, pero básicamente necesitarás inspeccionar los encabezados para obtener mejores resultados.

Recomiendo encarecidamente este artículo sobre Estructura física y C ++ de los juegos de blog desde dentro. Recomiendan un enfoque integral para limpiar el desorden #include:

Pautas

Aquí hay un conjunto detallado de pautas del libro de Lakos que minimizan la cantidad de dependencias físicas entre los archivos. Los he estado usando durante años y siempre he estado muy contento con los resultados.

  1. Cada archivo cpp incluye primero su propio archivo de encabezado. [recorte]
  2. Un archivo de encabezado debe incluir todos los archivos de encabezado necesarios para analizarlo. [recorte]
  3. Un archivo de encabezado debe tener el número mínimo de archivos de encabezado necesarios para analizarlo. [recorte]

Cppclean de Google (enlaces a: download , documentation ) puede encontrar varias categorías de problemas de C ++, y ahora puede encontrar #includes superfluos.

También hay una herramienta basada en Clang, que include-what-you-use , que puede hacer esto. include-what-you-use puede incluso sugerir declaraciones hacia adelante (para que no tenga que # incluir tanto) y opcionalmente limpiar sus #includes para usted.

Las versiones actuales de Eclipse CDT también tienen esta funcionalidad incorporada: ir bajo el menú Fuente y hacer clic en Organizar Incluye ordenará alfabéticamente sus # include, agregará los encabezados que Eclipse cree que está usando sin incluirlos directamente, y comenta los encabezados que no. No creo que necesites. Sin embargo, esta característica no es 100% confiable.


PCLint Gimpel Software puede informar sobre cuándo un archivo de inclusión se ha incluido más de una vez en una unidad de compilación , pero no puede encontrar los archivos de inclusión que no son necesarios en la forma que está buscando.

Edición: se puede. Ver la respuesta de itsmatt.


Este artículo explica una técnica de #include eliminar mediante el análisis de Doxygen. Eso es solo un script de perl, por lo que es bastante fácil de usar.


Puede escribir un script rápido que borre una sola directiva #include, compile los proyectos y registre el nombre en #include y el archivo del que se eliminó en el caso de que no se hayan producido errores de compilación.

Deje que se ejecute durante la noche, y al día siguiente tendrá una lista 100% correcta de los archivos de inclusión que puede eliminar.

A veces la fuerza bruta simplemente funciona :-)

Edición: ya veces no :-). Aquí hay un poco de información de los comentarios:

  1. A veces puede eliminar dos archivos de encabezado por separado, pero no ambos juntos. Una solución es eliminar los archivos de encabezado durante la ejecución y no recuperarlos. Encontrará una lista de archivos que puede eliminar de forma segura, aunque podría haber una solución con más archivos para eliminar que este algoritmo no encontrará. (Es una búsqueda codiciosa sobre el espacio de los archivos de inclusión para eliminar. Solo encontrará un máximo local)
  2. Puede haber cambios sutiles en el comportamiento si tiene algunas macros redefinidas de manera diferente dependiendo de algunos #ifdefs. Creo que estos son casos muy raros, y las pruebas unitarias que forman parte de la compilación deberían captar estos cambios.



Nunca he encontrado una herramienta completa que cumpla con lo que estás preguntando. Lo más parecido que he usado es IncludeManager , que representa IncludeManager el árbol de inclusión de encabezado para que pueda ver visualmente cosas como los encabezados incluidos en un solo archivo e inclusiones de encabezado circular.


Perdón por (re) publicar aquí, la gente a menudo no amplía los comentarios.

Revise mi comentario a crashmstr, FlexeLint / PC-Lint lo hará por usted. Mensaje informativo 766. La sección 11.8.1 de mi manual (versión 8.0) discute esto.

Además, y esto es importante, siga iterando hasta que el mensaje desaparezca . En otras palabras, después de eliminar los encabezados no utilizados, volver a ejecutar la pelusa, más archivos de encabezados podrían haberse "perdido" una vez que elimines algunos encabezados innecesarios. (Eso puede sonar tonto, léalo lentamente y analícelo, tiene sentido).


Hay dos tipos de archivos #include superfluos:

  1. Un archivo de encabezado realmente no es necesario para el módulo (.c, .cpp) en absoluto
  2. El módulo necesita un archivo de encabezado, pero se incluye más de una vez, directa o indirectamente.

Hay 2 formas en mi experiencia que funcionan bien para detectarla:

  • gcc -H o cl.exe / showincludes (resolver problema 2)

    En el mundo real, puede exportar CFLAGS = -H antes de realizar, si todas las opciones de Makefile no anulan las opciones de CFLAGS. O como utilicé, puede crear un contenedor cc / g ++ para agregar forzosamente las opciones -H a cada invocación de $ (CC) y $ (CXX). y anteponer el directorio del envoltorio a la variable $ PATH, entonces, en su lugar, make usará su comando de envoltorio. Por supuesto, su envoltorio debe invocar el compilador gcc real. Estos trucos deben cambiar si su Makefile usa gcc directamente. en lugar de $ (CC) o $ (CXX) o por reglas implícitas.

    También puede compilar un solo archivo mediante la modificación con la línea de comandos. Pero si quieres limpiar los encabezados de todo el proyecto. Puede capturar toda la salida por:

    hacer limpia

    hacer 2> & 1 | tee result.txt

  • PC-Lint / FlexeLint (resuelva el problema 1 y 2)

    asegúrese de agregar las opciones + e766, esta advertencia trata sobre: ​​archivos de encabezado no utilizados.

    pclint / flint -vf ...

    Esto provocará que los archivos de encabezado incluidos en la salida pclint, los archivos de encabezado anidados se sangrarán adecuadamente.


No es automático, pero Doxygen producirá diagramas de dependencia para los archivos #included. Tendrá que examinarlos visualmente, pero pueden ser muy útiles para obtener una imagen de lo que está usando qué.


El navegador de refactorización de CScout puede detectar directivas de inclusión superfluas en código C (desafortunadamente no en C ++). Puede encontrar una descripción de cómo funciona en this artículo de la revista.


extern "C" está destinado a ser reconocido por un compilador de C ++ y notificar al compilador que la función anotada se compila (o se debe) compilar en estilo C. De modo que, al enlazar, se vincula a la versión correcta de la función desde C.





c++ c refactoring include dependencies