Programing

대규모 C ++ 프로젝트에서 불필요한 #include 파일을 어떻게 감지해야합니까?

lottogame 2020. 8. 28. 07:49
반응형

대규모 C ++ 프로젝트에서 불필요한 #include 파일을 어떻게 감지해야합니까?


저는 Visual Studio 2008에서 대규모 C ++ 프로젝트를 작업 중이며 불필요한 #include지시문 이있는 파일이 많이 있습니다 . 때로는 #includes는 아티팩트 일 뿐이며 모든 것이 제거되면 잘 컴파일되며 다른 경우에는 클래스를 앞으로 선언하고 #include를 .cpp파일 로 이동할 수 있습니다 . 이 두 경우를 모두 감지 할 수있는 좋은 도구가 있습니까?


불필요한 포함 파일을 표시하지는 않지만 Visual Studio에는 컴파일 타임에 포함 된 모든 파일의 트리를 출력하는 설정 /showIncludes( .cpp파일을 마우스 오른쪽 버튼으로 클릭 Properties->C/C++->Advanced)이 있습니다. 이는 포함 할 필요가없는 파일을 식별하는 데 도움이 될 수 있습니다.

또한 pimpl 관용구를 살펴보면 더 적은 헤더 파일 종속성을 제거하여 제거 할 수있는 내용을 쉽게 확인할 수 있습니다.


PC Lint 는 이것에 대해 아주 잘 작동하며 다른 모든 종류의 구피 문제도 찾습니다. Visual Studio에서 외부 도구를 만드는 데 사용할 수있는 명령 줄 옵션이 있지만 Visual Lint 추가 기능을 사용하는 것이 더 쉽다 는 것을 알았 습니다. Visual Lint의 무료 버전도 도움이됩니다. 그러나 PC-Lint를 시도해보십시오. 너무 많은 경고가 표시되지 않도록 구성하려면 약간의 시간이 걸리지 만 표시되는 내용에 놀랄 것입니다.


이를위한 새로운 Clang 기반 도구 인 include-what-you-use가 있습니다 .


!!부인 성명!! 저는 상용 정적 분석 도구 (PC Lint가 아님)로 작업합니다. !!부인 성명!!

간단한 비 파싱 접근 방식에는 몇 가지 문제가 있습니다.

1) 과부하 세트 :

오버로드 된 함수에 다른 파일에서 온 선언이있을 수 있습니다. 하나의 헤더 파일을 제거하면 컴파일 오류가 아닌 다른 오버로드가 선택 될 수 있습니다! 그 결과 나중에 추적하기가 매우 어려울 수있는 의미 체계의 조용한 변경이 발생합니다.

2) 템플릿 전문화 :

오버로드 예제와 유사하게, 템플릿에 대한 부분적 또는 명시 적 전문화가있는 경우 템플릿이 사용될 때 모두 표시되기를 원합니다. 기본 템플릿의 전문화가 다른 헤더 파일에있을 수 있습니다. 전문화가있는 헤더를 제거해도 컴파일 오류가 발생하지 않지만 해당 전문화가 선택되었을 경우 정의되지 않은 동작이 발생할 수 있습니다. (참조 : C ++ 함수의 템플릿 전문화 가시성 )

'msalters'가 지적했듯이 코드를 전체적으로 분석하면 클래스 사용도 분석 할 수 있습니다. 파일의 특정 경로를 통해 클래스가 사용되는 방식을 확인하면 클래스의 정의 (및 모든 종속 항목)를 완전히 제거하거나 적어도 포함의 기본 소스에 더 가까운 수준으로 이동할 수 있습니다. 나무.


그런 도구에 대해 잘 모르고, 과거에 하나를 쓸 생각을했지만 해결하기 어려운 문제라는 것이 밝혀졌습니다.

소스 파일에 ah 및 bh가 포함되어 있다고 가정합니다. 아 포함 #define USE_FEATURE_X하고 bh 사용합니다 #ifdef USE_FEATURE_X. #include "a.h"주석 처리 된 경우 파일이 여전히 컴파일 될 수 있지만 예상 한대로 수행되지 않을 수 있습니다. 이를 프로그래밍 방식 으로 감지하는 것은 중요하지 않습니다.

어떤 도구를 사용하든 빌드 환경도 알아야합니다. 아의 경우 :

#if defined( WINNT )
   #define USE_FEATURE_X
#endif

그런 다음 USE_FEATURE_X이 정의 된 경우에만 정의 WINNT되므로 도구는 컴파일러 자체에 의해 생성 된 지시문과 헤더 파일이 아닌 컴파일 명령에 지정된 지시문을 알아야합니다.


Timmermans와 마찬가지로 나는 이것에 대한 도구에 익숙하지 않습니다. 하지만 필자는 Perl (또는 Python) 스크립트를 작성하여 한 번에 하나씩 각 포함 줄을 주석 처리 한 다음 각 파일을 컴파일하는 프로그래머를 알고 있습니다.


이제 Eric Raymond 가이를위한 도구를 가지고있는 것으로 보입니다 .

Google의 cpplint.py 에는 "사용하는 항목 만 포함"규칙이 있지만, 내가 말할 수있는 한 " 사용하는 항목 포함"하지 않습니다 . 그래도 유용 할 수 있습니다.


일반적으로이 주제에 관심이있는 경우 Lakos의 대규모 C ++ 소프트웨어 설계 를 확인하십시오 . 약간 구식이지만 포함되어야하는 헤더의 절대 최소값을 찾는 것과 같은 많은 "물리적 설계"문제에 직면합니다. 나는 다른 곳에서 이런 종류의 논의를 실제로 본 적이 없습니다.


부여 관리자를 포함 시도. Visual Studio에서 쉽게 통합되고 불필요한 항목을 찾는 데 도움이되는 포함 경로를 시각화합니다. 내부적으로는 Graphviz를 사용하지만 더 많은 멋진 기능이 있습니다. 그리고 상용 제품이지만 가격이 매우 저렴합니다.


C / C ++ Include File Dependencies Watcher를 사용하여 포함 그래프를 빌드하고 불필요한 포함을 시각적으로 찾을 수 있습니다.


헤더 파일이 일반적으로

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

(#pragma를 한 번 사용하는 것과는 반대로) 다음과 같이 변경할 수 있습니다.

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

그리고 컴파일러는 컴파일되는 cpp 파일의 이름을 출력하기 때문에 적어도 어떤 cpp 파일이 헤더를 여러 번 가져 오는지 알 수 있습니다.


PC-Lint는 실제로 이것을 할 수 있습니다. 이를 수행하는 한 가지 쉬운 방법은 사용하지 않는 포함 파일 만 감지하고 다른 모든 문제를 무시하도록 구성하는 것입니다. 이것은 매우 간단합니다. 메시지 766 ( "모듈에서 사용되지 않는 헤더 파일") 만 활성화하려면 명령 줄에 -w0 + e766 옵션을 포함하면됩니다.

964 ( "모듈에서 직접 사용되지 않는 헤더 파일") 및 966 ( "모듈에서 사용되지 않는 간접적으로 포함 된 헤더 파일")과 같은 관련 메시지에도 동일한 접근 방식을 사용할 수 있습니다.

FWIW 지난주 http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318 의 블로그 게시물에서 이에 대해 자세히 썼습니다 .


If you are looking to remove unnecessary #include files in order to decrease build times, your time and money might be better spent parallelizing your build process using cl.exe /MP, make -j, Xoreax IncrediBuild, distcc/icecream, etc.

Of course, if you already have a parallel build process and you're still trying to speed it up, then by all means clean up your #include directives and remove those unnecessary dependencies.


Start with each include file, and ensure that each include file only includes what is necessary to compile itself. Any include files that are then missing for the C++ files, can be added to the C++ files themselves.

For each include and source file, comment out each include file one at a time and see if it compiles.

It is also a good idea to sort the include files alphabetically, and where this is not possible, add a comment.


Adding one or both of the following #defines will exclude often unnecessary header files and may substantially improve compile times especially if the code that is not using Windows API functions.

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN

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


If you aren't already, using a precompiled header to include everything that you're not going to change (platform headers, external SDK headers, or static already completed pieces of your project) will make a huge difference in build times.

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

Also, although it may be too late for your project, organizing your project into sections and not lumping all local headers to one big main header is a good practice, although it takes a little extra work.


If you would work with Eclipse CDT you could try out http://includator.com to optimize your include structure. However, Includator might not know enough about VC++'s predefined includes and setting up CDT to use VC++ with correct includes is not built into CDT yet.


The latest Jetbrains IDE, CLion, automatically shows (in gray) the includes that are not used in the current file.

It is also possible to have the list of all the unused includes (and also functions, methods, etc...) from the IDE.


Some of the existing answers state that it's hard. That's indeed true, because you need a full compiler to detect the cases in which a forward declaration would be appropriate. You cant parse C++ without knowing what the symbols mean; the grammar is simply too ambiguous for that. You must know whether a certain name names a class (could be forward-declared) or a variable (can't). Also, you need to be namespace-aware.


Maybe a little late, but I once found a WebKit perl script that did just what you wanted. It'll need some adapting I believe (I'm not well versed in perl), but it should do the trick:

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

(this is an old branch because trunk doesn't have the file anymore)


If there's a particular header that you think isn't needed anymore (say string.h), you can comment out that include then put this below all the includes:

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

Of course your interface headers might use a different #define convention to record their inclusion in CPP memory. Or no convention, in which case this approach won't work.

Then rebuild. There are three possibilities:

  • It builds ok. string.h wasn't compile-critical, and the include for it can be removed.

  • The #error trips. string.g was included indirectly somehow You still don't know if string.h is required. If it is required, you should directly #include it (see below).

  • You get some other compilation error. string.h was needed and isn't being included indirectly, so the include was correct to begin with.

.h 또는 .c가 다른 .h를 직접 사용할 때 간접 포함에 따라 거의 확실하게 버그입니다. 실제로 사용중인 다른 헤더가 필요로하는 한 코드가 해당 헤더 만 필요로 할 것이라고 약속하는 것입니다. 아마도 당신이 의미 한 것이 아닐 것입니다.

빌드 실패를 일으키는 사항을 선언하는 것보다 동작을 수정하는 헤더에 대한 다른 답변에서 언급 한 경고도 여기에도 적용됩니다.

참고 URL : https://stackoverflow.com/questions/74326/how-should-i-detect-unnecessary-include-files-in-a-large-c-project

반응형