Pregunta

Estoy trabajando en un gran proyecto de C++ en Visual Studio 2008 y hay muchos archivos con archivos innecesarios. #include directivas.A veces el #includes son solo artefactos y todo se compilará bien una vez eliminados, y en otros casos, las clases podrían declararse hacia adelante y #include podría moverse al .cpp archivo.¿Existen buenas herramientas para detectar ambos casos?

¿Fue útil?

Solución

Si bien no revelará archivos de inclusión innecesarios, Visual Studio tiene una configuración /showIncludes (haga clic derecho en un .cpp archivo, Properties->C/C++->Advanced) que generará un árbol de todos los archivos incluidos en el momento de la compilación.Esto puede ayudar a identificar archivos que no deberían incluirse.

También puedes echar un vistazo al modismo pimpl para que puedas salirte con la tuya con menos dependencias del archivo de encabezado para que sea más fácil ver el contenido que puedes eliminar.

Otros consejos

Pelusa de PC funciona bastante bien para esto y también encuentra todo tipo de otros problemas tontos.Tiene opciones de línea de comando que se pueden usar para crear herramientas externas en Visual Studio, pero descubrí que pelusa visual Es más fácil trabajar con el complemento.Incluso la versión gratuita de Visual Lint ayuda.Pero dale una oportunidad a PC-Lint.Configurarlo para que no te dé demasiadas advertencias lleva un poco de tiempo, pero te sorprenderá lo que aparece.

Hay una nueva herramienta basada en Clang, incluye-lo-que-usas, que pretende hacer esto.

!!¡¡DESCARGO DE RESPONSABILIDAD!!Trabajo en una herramienta comercial de análisis estático (no PC Lint).!!¡¡DESCARGO DE RESPONSABILIDAD!!

Hay varios problemas con un enfoque simple sin análisis:

1) Conjuntos de sobrecarga:

Es posible que una función sobrecargada tenga declaraciones que provengan de archivos diferentes.¡Puede ser que al eliminar un archivo de encabezado se elija una sobrecarga diferente en lugar de un error de compilación!El resultado será un cambio silencioso en la semántica que puede resultar muy difícil de rastrear después.

2) Especializaciones de plantilla:

De forma similar al ejemplo de sobrecarga, si tiene especializaciones parciales o explícitas para una plantilla, querrá que todas sean visibles cuando se utilice la plantilla.Es posible que las especializaciones de la plantilla principal se encuentren en archivos de encabezado diferentes.Eliminar el encabezado con la especialización no provocará un error de compilación, pero puede dar lugar a un comportamiento indefinido si se hubiera seleccionado esa especialización.(Ver: Visibilidad de la especialización de plantilla de la función C++)

Como señala 'msalters', realizar un análisis completo del código también permite analizar el uso de la clase.Al verificar cómo se usa una clase a través de una ruta específica de archivos, es posible que la definición de la clase (y por lo tanto todas sus dependencias) se pueda eliminar por completo o al menos mover a un nivel más cercano a la fuente principal en la inclusión. árbol.

No conozco ninguna herramienta de este tipo y he pensado en escribir una en el pasado, pero resulta que es un problema difícil de resolver.

Digamos que su archivo fuente incluye a.h y b.h;a.h contiene #define USE_FEATURE_X y b.h usa #ifdef USE_FEATURE_X.Si #include "a.h" está comentado, es posible que su archivo aún se compile, pero que no haga lo que espera.Detectando esto programáticamente no es trivial.

Cualquiera que sea la herramienta que haga esto, también necesitará conocer su entorno de construcción.Si a.h se parece a:

#if defined( WINNT )
   #define USE_FEATURE_X
#endif

Entonces USE_FEATURE_X sólo se define si WINNT está definido, por lo que la herramienta necesitaría saber qué directivas genera el propio compilador y cuáles se especifican en el comando de compilación en lugar de en un archivo de encabezado.

Al igual que Timmermans, no conozco ninguna herramienta para ello.Pero conozco programadores que escribieron un script en Perl (o Python) para intentar comentar cada línea de inclusión una a la vez y luego compilar cada archivo.


Parece que ahora Eric Raymond tiene una herramienta para esto.

de google cpplint.py tiene una regla de "incluir lo que usas" (entre muchas otras), pero hasta donde yo sé, no hay "incluir" solo lo que usas." Aún así, puede ser útil.

Si está interesado en este tema en general, es posible que desee consultar el artículo de Lakos. Diseño de software C++ a gran escala.Está un poco anticuado, pero aborda muchas cuestiones de "diseño físico", como encontrar el mínimo absoluto de encabezados que deben incluirse.Realmente no he visto este tipo de cosas discutidas en ningún otro lugar.

Dar Incluir administrador un intento.Se integra fácilmente en Visual Studio y visualiza las rutas de inclusión, lo que le ayuda a encontrar cosas innecesarias.Internamente utiliza Graphviz, pero hay muchas más funciones interesantes.Y aunque es un producto comercial tiene un precio muy reducido.

Puedes construir un gráfico de inclusión usando C/C++ incluye vigilante de dependencias de archivos, y encuentre inclusiones innecesarias visualmente.

Si sus archivos de encabezado generalmente comienzan con

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#endif

(en lugar de usar #pragma una vez), puedes cambiarlo a:

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#else 
#pragma message("Someheader.h superfluously included")
#endif

Y dado que el compilador genera el nombre del archivo cpp que se está compilando, eso le permitiría saber al menos qué archivo cpp está provocando que el encabezado aparezca varias veces.

De hecho, PC-Lint puede hacer esto.Una forma sencilla de hacerlo es configurarlo para que detecte sólo los archivos de inclusión no utilizados e ignore todos los demás problemas.Esto es bastante sencillo: para habilitar solo el mensaje 766 ("El archivo de encabezado no se usa en el módulo"), simplemente incluya las opciones -w0 +e766 en la línea de comando.

El mismo enfoque también se puede utilizar con mensajes relacionados como 964 ("Archivo de encabezado no utilizado directamente en el módulo") y 966 ("Archivo de encabezado incluido indirectamente no utilizado en el módulo").

FWIW, escribí sobre esto con más detalle en una publicación de blog la semana pasada en http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318.

Si está buscando eliminar innecesarios #include archivos para reducir los tiempos de compilación, es mejor invertir su tiempo y dinero en paralelizar su proceso de compilación utilizando cl.exe/MP, hacer -j, Xoreax IncrediConstrucción, distrito/helado, etc.

Por supuesto, si ya tiene un proceso de compilación paralelo y todavía está intentando acelerarlo, entonces limpie su #include directivas y eliminar esas dependencias innecesarias.

Comience con cada archivo de inclusión y asegúrese de que cada archivo de inclusión solo incluya lo necesario para compilarse.Cualquier archivo de inclusión que falte en los archivos C++ se puede agregar a los propios archivos C++.

Para cada archivo de inclusión y fuente, comente cada archivo de inclusión uno a la vez y vea si se compila.

También es una buena idea ordenar los archivos incluidos alfabéticamente y, cuando esto no sea posible, agregar un comentario.

Agregar uno o ambos de los siguientes #defines excluirá a menudo archivos de encabezado innecesarios y puede mejorar sustancialmente los tiempos de compilación, especialmente si el código que no usa funciones de la API de Windows.

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN

Ver http://support.microsoft.com/kb/166474

Si aún no lo ha hecho, usar un encabezado precompilado para incluir todo lo que no va a cambiar (encabezados de plataforma, encabezados de SDK externos o partes estáticas ya completadas de su proyecto) marcará una gran diferencia en los tiempos de compilación.

http://msdn.microsoft.com/en-us/library/szfdksca(VS.71).aspx

Además, aunque puede que sea demasiado tarde para su proyecto, organizarlo en secciones y no agrupar todos los encabezados locales en un encabezado principal grande es una buena práctica, aunque requiere un poco de trabajo adicional.

Si quisieras trabajar con Eclipse CDT, podrías probar http://incluyetor.com para optimizar su estructura de inclusión.Sin embargo, es posible que Includator no sepa lo suficiente sobre las inclusiones predefinidas de VC++ y la configuración de CDT para usar VC++ con inclusiones correctas aún no está integrada en CDT.

El último IDE de Jetbrains, CLion, muestra automáticamente (en gris) las inclusiones que no se utilizan en el archivo actual.

También es posible tener la lista de todas las inclusiones no utilizadas (y también funciones, métodos, etc...) desde el IDE.

Algunas de las respuestas existentes afirman que es difícil.De hecho, eso es cierto, porque necesita un compilador completo para detectar los casos en los que una declaración directa sería apropiada.No se puede analizar C++ sin saber qué significan los símbolos;la gramática es simplemente demasiado ambigua para eso.Debe saber si un determinado nombre nombra una clase (podría declararse hacia adelante) o una variable (no puede).Además, debe tener en cuenta el espacio de nombres.

Quizás un poco tarde, pero una vez encontré un script en Perl de WebKit que hacía exactamente lo que querías.Creo que necesitará algo de adaptación (no conozco bien Perl), pero debería funcionar:

http://trac.webkit.org/browser/branches/old/safari-3-2-branch/WebKitTools/Scripts/find-extra-includes

(esta es una rama antigua porque el tronco ya no tiene el archivo)

Si hay un encabezado en particular que cree que ya no es necesario (digamos String.h), puede comentar que incluya luego poner esto a continuación todas las incluido:

#ifdef _STRING_H_
#  error string.h is included indirectly
#endif

Por supuesto, los encabezados de su interfaz pueden usar una convención #Define diferente para registrar su inclusión en la memoria CPP.O no hay convención, en cuyo caso este enfoque no funcionará.

Luego reconstruir.Hay tres posibilidades:

  • Se construye bien.String.H no fue compilar crítico, y la inclusión para ello se puede eliminar.

  • El #error tropieza.String.g se incluyó indirectamente de alguna manera, todavía no sabe si String.h es necesario.Si es necesario, debe #incluirlo directamente (ver más abajo).

  • Obtiene algún otro error de compilación.String.h era necesario y no se incluye indirectamente, por lo que la inclusión era correcta para empezar.

Tenga en cuenta que dependiendo de la inclusión indirecta cuando su .h o .C usa directamente otro .h es casi seguro que es un error:En efecto, promete que su código solo requerirá ese encabezado siempre que algún otro encabezado que esté utilizando lo requiera, lo que probablemente no sea lo que quiera decir.

Las advertencias mencionadas en otras respuestas sobre encabezados que modifican el comportamiento en lugar de que declarar cosas que causan fallas de construcción se aplican aquí también.

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