¿Detecta # # superfluos en C / C ++?

289

A menudo encuentro que la sección de encabezados de un archivo se hace cada vez más grande, pero nunca se vuelve más pequeña. A lo largo de la vida de un archivo fuente, las clases pueden haberse movido y refactorizado y es muy posible que haya bastantes #includesque ya no necesiten estar allí. Dejarlos allí solo prolonga el tiempo de compilación y agrega dependencias de compilación innecesarias. Tratar de descubrir 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 con seguridad?
¿Pelusa hace esto quizás?

shoosh
fuente
1
Ver también: stackoverflow.com/questions/74326/…
Eclipse
1
La pregunta vinculada solo parece abordar el problema en Windows, utilizando Visual Studio en particular.
D'Nabre
77
Votar para volver a abrir esto, ya que el duplicado se trata de usar Visual Studio, específicamente.
Drew Dormann

Respuestas:

42

No es automático, pero doxygen producirá diagramas de dependencia para los #includedarchivos. Tendrá que revisarlos visualmente, pero pueden ser muy útiles para obtener una imagen de qué está usando qué.

Albert
fuente
55
Esta es una excelente manera de ver las cadenas. Ver A -> B -> C -> D y A -> D revela inmediatamente la redundancia.
Tom
34
@Tom: Esa es una idea horrible: por un lado, no muestra si esas inclusiones son necesarias o no y, en segundo lugar, la lista de inclusiones no debe depender de las inclusiones indirectas que pueden cambiar en el futuro (las inclusiones redundantes generalmente no son tales gran problema de todos modos, gracias a incluir guardias y magia del compilador), pero en qué clases / funciones se usan realmente en el archivo (su compilador no debería tener que pasar por miles de líneas de código de plantilla que ni siquiera se instancian)
MikeMB
@albert, ¿puedes incluir capturas de pantalla de esto y describir brevemente dónde hacer clic en la salida de oxígeno?
Gabriel Staples
@GabrielStaples No es mi respuesta, así que no quiero agregarle información. Solo corregí el enlace (ya que el lugar de alojamiento al que se refería se detuvo / se incautó para ser utilizado).
Albert
177

El cppclean de Google (enlaces a: descarga , documentación ) puede encontrar varias categorías de problemas de C ++, y ahora puede encontrar #incluidos superfluos.

También hay una herramienta basada en Clang, que incluye lo que usa , que puede hacer esto. include-what-you-use puede incluso sugerir declaraciones reenviadas (para que no tenga que #incluir demasiado) y, opcionalmente, limpiar sus #include por usted.

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

Josh Kelley
fuente
2
Lo hace ahora Acabo de empezar a usarlo. Mira mi nota aquí. stackoverflow.com/questions/1301850/…
Probabilidad
1
El repositorio de cppclean está caído, ahora puede obtenerlo aquí: bitbucket.org/robertmassaioli/cppclean ( aunque el sitio original sigue siendo útil para algunos ejemplos de uso)
Nick
3
Actualicé el enlace a una bifurcación cppclean mantenida: github.com/myint/cppclean
BenC
1
Tenga en cuenta que cppclean parece encontrarlos solo en los archivos de encabezado, no en los archivos de cpp, del documento: "# innecesarios incluidos en los archivos de encabezado".
Zitrax
1
@wizurd: no me he mantenido al día con los desarrollos recientes en Eclipse CDT, pero no lo creo. iwyu es minucioso y relativamente lento. El análisis de Eclipse CDT es rápido (interactivo) y, cuando lo probé, menos preciso.
Josh Kelley
65

También revisa include-what-you-use , que resuelve un problema similar.

Tzafrir
fuente
66
En mi humilde opinión, esta respuesta necesita muchos más votos a favor, ya que una vez que se resuelvan los problemas, la herramienta IWYU de Google será la herramienta definitiva para esta tarea.
Dan Olson el
55
sudo apt-get install iwyu
Andrew Wagner
Parece genial: con dos cavaets 1) última actualización de febrero de 2106 2) Los propios Gogole lo usan solo para C ++, no C, que el OP solicitó.
Mawg dice que reinstalar a Monica el
¿Puedes explicar un poco cómo debería usarlo un usuario? El archivo README no es muy claro acerca de lo que contiene la salida del script python.
Bufón del Rey
Estoy usando esto, pero no siempre es 100% correcto. Tal vez el 70% de las veces da las sugerencias correctas.
InQusitive
25

El problema con la detección de superfluos incluye es que no puede ser solo un verificador de dependencia de tipo. Una inclusión superflua es un archivo que no aporta nada de valor a la compilación y no altera otro elemento del que dependen otros archivos. Hay muchas formas 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 nombre que altera la búsqueda de un nombre en algún momento. Para detectar elementos como el espacio de nombres, necesita mucho más que un preprocesador, de hecho, casi necesita un compilador completo.

Lint es más un corrector de estilo y ciertamente no tendrá esta capacidad completa.

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

JaredPar
fuente
8
Nada de esto será un problema si los archivos de inclusión están bien distribuidos. Si alguna vez necesita incluir el archivo A antes del archivo B, lo está haciendo mal (y he trabajado en proyectos donde lo hicieron mal).
David Thornley
9
@David, sí, pero eso depende de los años de desarrollo antes de hacerlo correctamente. Puedo decir con gran certeza que las probabilidades de que eso ocurra favorecen a la casa, no a usted :(
JaredPar 05 de
Sí, pero generalmente me entero de eso cuando modifico un programa, y ​​de repente tengo un error de compilación (si tengo suerte) o un error oscuro. Eso parece mantener honestos los #include archivos, al menos a largo plazo.
David Thornley
Yo diría exactamente lo contrario. Todo lo que necesitas es un verificador de dependencia de tipo. Es posible que no se compile después de que haya organizado las inclusiones en consecuencia, pero estos son problemas que deben abordarse de todos modos.
Benoît
1
@Benoit, entonces estaría ignorando una clase de problemas que compilan pero cambian semánticamente el significado de su programa. Considere cómo un #define en un archivo puede alterar una rama #if en otro. Extracción de una cabecera todavía puede permitir que esto compilar con diferentes resultados
JaredPar
15

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

Miré este blog y el autor habló un poco sobre la configuración de PCLint para encontrar las inclusiones no utilizadas. Podría valer la pena echarle un vistazo.

itsmatt
fuente
¡Buen descubrimiento! Tendré que usar eso.
crashmstr 05 de
44
Uso PCLint regularmente y me informa de los encabezados no utilizados. Tengo cuidado de comentar el encabezado #incluir y volver a compilar para asegurarme de que el encabezado no se use realmente ...
Harold Bamford
Gracias por la confirmación, Harold.
itsmatt 05 de
55
muy caro. No es una herramienta viable para las masas.
7

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

Diomidis Spinellis
fuente
5

Puede escribir una secuencia de comandos rápida que borre una sola directiva #include, compile los proyectos y registre el nombre en #include y el archivo del que se eliminó en caso de que no ocurrieran errores de compilación.

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

A veces la fuerza bruta simplemente funciona :-)


editar: y a 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. Esto encontrará una lista de archivos que puede eliminar de forma segura, aunque puede haber una solución con más archivos para eliminar que este algoritmo no encontrará. (es una búsqueda ambiciosa en el espacio de 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 construcción deberían detectar estos cambios.
Gilad Naor
fuente
1
Tenga cuidado con esto: digamos que hay dos archivos de encabezado que incluyen una definición de algo. Puede eliminar cualquiera de los dos, pero no ambos. Tendrá que ser un poco más minucioso en su enfoque de fuerza bruta.
Dominic Rodger
Tal vez esto es lo que quiso decir, pero un script que elimina una sola inclusión y deja fuera la última inclusión eliminada si se eliminó con éxito sería suficiente.
Dominic Rodger
1
Mala idea. Si un archivo de encabezado # define una BLAH constante y otro archivo de encabezado verifica #ifdef BLAH, la eliminación del primer archivo de encabezado aún puede compilarse correctamente pero su comportamiento ha cambiado.
Graeme Perrow
1
Esto también puede causar problemas con los encabezados del sistema, ya que diferentes implementaciones pueden tener diferentes cosas incluidas en #include <vector>. Incluso si se apega a un compilador, los encabezados podrían cambiar en diferentes versiones.
David Thornley
2
Esto no encontrará casos en los que incluya un encabezado que incluya el encabezado que realmente necesita.
bk1e 05 de
5

Lamento (re) publicar aquí, la gente a menudo no expande los comentarios.

Consulte mi comentario en 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, vuelva a ejecutar la pelusa, es posible que se hayan "innecesario" más archivos de encabezado una vez que elimine algunos encabezados innecesarios. (Eso puede sonar tonto, léelo lentamente y analícelo, tiene sentido).

Dan
fuente
Sé exactamente lo que quieres decir, y mi reacción fue "Ewwww". Odio el código así.
David Thornley
5

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

Dan Olson
fuente
4

Intenté usar Flexelint (la versión unix de PC-Lint) y obtuve resultados algo mixtos. Esto es probable porque estoy trabajando en una base de código muy grande y nudosa. Recomiendo examinar cuidadosamente cada archivo que se informa como no utilizado.

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

Una de las formas en que las herramientas automatizadas pueden equivocarse:

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, acumulará sus # dependencias incluidas. Hay más casos patológicos, pero básicamente necesitará inspeccionar los encabezados usted mismo para obtener mejores resultados.

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

Pautas

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

  1. Cada archivo cpp incluye su propio archivo de encabezado primero. [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]
Ben Martin
fuente
El libro de Lakos es excelente para la educación, aparte de sus observaciones obsoletas sobre la tecnología de compilación.
Tom
4

Si está utilizando Eclipse CDT, puede probar http://includator.com, que es gratuito para los probadores beta (en el momento de escribir este artículo) y elimina automáticamente #incluidos superfluos o agrega los que faltan. Para aquellos usuarios que tienen FlexeLint o PC-Lint y están usando Elicpse CDT, http://linticator.com podría ser una opción (también gratuita para la prueba beta). Si bien utiliza el análisis de Lint, proporciona soluciones rápidas para eliminar automáticamente las declaraciones superincluidas #include.

PeterSom
fuente
La razón de esto es que nuestro departamento de contabilidad no puede facturar cantidades menores. Si cuenta el tiempo que podría ahorrar, no es irrazonable. Una vez que tengamos la capacidad de obtener pagos con tarjeta de crédito, podemos bajar el precio significativamente. Otra opción sería patrocinar nuestros esfuerzos de desarrollo. Nuestro modelo de financiamiento requiere que obtengamos ganancias para financiar nuestro trabajo de investigación. Me encantaría vender licencias mucho más baratas, pero no puedo. Es posible que lo contribuyamos a CDT y lo obtenga de forma gratuita, pero eso tengo que financiarlo de alguna manera. Lo olvidé, ¡puedes probarlo gratis!
PeterSom
2

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

Steve Gury
fuente
1
El script encuentra algunas inclusiones para eliminar, pero también ofrece muchas inclusiones que no se pueden eliminar. Parece que no admite la enumeración de clase, parece que también tiene un mal momento con macro y, a veces, con espacio de nombres.
Baptiste Wicht
1

Hay dos tipos de archivos #include superfluos:

  1. En realidad, el módulo no necesita un archivo de encabezado (.c, .cpp)
  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 detectarlo:

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

    En el mundo real, puede exportar CFLAGS = -H antes de hacer, si todos los Makefile no anulan las opciones de CFLAGS. O como solía, puede crear un contenedor cc / g ++ para agregar opciones -H a la fuerza a cada invocación de $ (CC) y $ (CXX). y anteponga el directorio del contenedor a la variable $ PATH, entonces su make utilizará su comando de contenedor en su lugar. Por supuesto, su contenedor 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 ajustando con la línea de comando. Pero si quieres limpiar los encabezados de todo el proyecto. Puede capturar toda la salida de la siguiente manera:

    hacer limpia

    hacer 2> y 1 | tee result.txt

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

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

    pclint / flint -vf ...

    Esto hará que la salida de pclint incluya archivos de encabezado, los archivos de encabezado anidados se sangrarán adecuadamente.

zhaorufei
fuente
1

Para finalizar 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 deduce 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.

Algoman
fuente
55
¿Pedí una solución "siempre correcta"? Esta respuesta no es muy productiva para la discusión.
shoosh
1
Bueno, ha habido numerosas publicaciones que discuten problemas con los que tal programa tendría que lidiar. Mi publicación da una respuesta concluyente y correcta a esa parte de la discusión. Y por mi parte, no me gustaría, si un programa me lo dijera, podría eliminar con seguridad un #include y luego mi código ya no se compilará. (o peor, todavía compila pero hace algo diferente). CUALQUIER programa de este tipo conlleva este riesgo.
Algoman
44
Entre toda la ESPECULACIÓN sobre lo difícil que sería y cómo PODRÍAS resolver un obstáculo u otro, te di la única respuesta 100% correcta. Me parece bastante descarado decir que esto no fue productivo ...
Algoman
1
Recordé que el teorema de Rice dice: "No puede haber un programa que siempre pueda verificar si un programa determinado resuelve este problema superfluo-incluye". Puede haber algunos programas que resuelvan el problema de las superfluas inclusiones.
Zhe Yang el
1
personalmente encontré la entrada de @ Algoman muy útil. me hace darme cuenta de lo difícil que es este problema.
bogardon
0

PC Lint de 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 archivos de inclusión que no sean necesarios de la manera que está buscando.

Editar: puede. Ver la respuesta de itsmatt

crashmstr
fuente
¿Estás seguro de eso? No he usado FlexeLint (igual que PCL) en algunos años en el código C ++, pero incluso recientemente en el código C, podría jurar que vi un par de mensajes (¿creo que es el código 766?) Sobre archivos de encabezado no utilizados. Acaba de marcar (v8.0), ver sección 11.8.1. de manual.
Dan