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


96

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

답변:


50

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

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


1
/ showincludes는 훌륭합니다. 이것을 수동으로하는 것은 그것 없이는 벅찬 일이었습니다.
shambolic

30

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


3
pc-lint로이를 수행하는 방법에 대한 몇 가지 지침은 riverblade.co.uk/
David Sykes


26

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

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

1) 과부하 세트 :

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

2) 템플릿 전문화 :

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

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


@RichardCorden : 소프트웨어 (QA C ++)가 너무 비쌉니다.
Xander Tulip

13
@XanderTulip : 영업 홍보 없이는 이에 응답하기가 어렵 기 때문에 미리 사과드립니다. IMHO, 당신이 고려해야 할 것은 좋은 엔지니어가 합리적인 크기의 프로젝트에서 이와 같은 것을 찾는 데 걸리는 시간입니다 (다른 많은 언어 / 제어 흐름 버그 포함). 소프트웨어가 변경됨에 따라 동일한 작업을 반복해서 반복해야합니다. 따라서 절약 된 시간을 계산할 때 도구 비용은 아마도 중요하지 않을 것입니다.
Richard Corden

10

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

소스 파일에 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되므로 도구는 컴파일러 자체에 의해 생성 된 지시문과 헤더 파일이 아닌 컴파일 명령에 지정된 지시문을 알아야합니다.


9

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


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

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


이 글을 읽으면서 웃어야 했어요. 저의 상사는 지난달에 저희 프로젝트 중 하나에서이 일을했습니다. 축소 된 헤더에는 몇 가지 요소가 포함됩니다.
Don Wakefield

2
Mac의 codewarrior는이 작업을 수행하고, 주석 처리하고, 컴파일하고, 오류 주석 해제시 #includes의 끝까지 계속하기 위해 스크립트를 내장했습니다. 파일 상단의 #includes에서만 작동했지만 일반적으로 그 위치에 있습니다. 완벽하지는 않지만 합리적으로 정상을 유지합니다.
slycrel 2010

5

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


4

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



3

헤더 파일이 일반적으로

#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 파일이 헤더를 여러 번 가져 오는지 알 수 있습니다.


12
헤더를 여러 번 포함하는 것이 좋습니다. 사용하는 것을 포함하는 것이 좋으며 포함 파일에 의존하지 않는 것이 좋습니다. OP가 원하는 것은 실제로 사용되지 않는 #includes를 찾는 것입니다.
Ryan Ginstrom 2010

12
IMO는 적극적으로 잘못된 일을합니다. 헤더가 없으면 작동하지 않는 다른 헤더를 포함해야합니다. 그리고 당신은 할 때 A.hB.h모두에 달려 있다고 C.h당신은 포함 A.h하고 B.h당신이 모두 필요하기 때문에, 당신은 C.h두 번,하지만 컴파일러는 그것을 두 번째 시간을 건너 뛸 수 있기 때문에 즉, 괜찮아요 당신이하지 않은 경우, 기억해야 할 것 항상 C.h이전에 포함 A.h하거나 B.h훨씬 더 쓸모없는 포함으로 끝납니다.
Jan Hudec 2011

5
콘텐츠가 정확하므로 여러 번 포함 된 헤더를 찾는 데 좋은 솔루션입니다. 그러나 원래의 질문은 이것에 의해 대답되지 않으며 이것이 언제 좋은 생각이 될지 상상할 수 없습니다. Cpp 파일은 헤더가 다른 곳에 포함되어 있더라도 종속 된 모든 헤더를 포함해야합니다. 프로젝트가 컴파일 순서에 따라 다르거 나 필요한 헤더가 다른 헤더에 포함되는 것을 원하지 않습니다.
jaypb

3

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

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

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


2

#include빌드 시간을 줄이기 위해 불필요한 파일 을 제거하려는 경우 cl.exe / MP , make -j , Xoreax IncrediBuild , distcc / icecream 등을 사용하여 빌드 프로세스를 병렬화하는 데 시간과 돈을 쓰는 것이 좋습니다 .

물론, 이미 병렬 빌드 프로세스가 있고 여전히 속도를 높이려는 경우, 반드시 #include지시문을 정리하고 불필요한 종속성을 제거하십시오.


2

각 포함 파일로 시작하고 각 포함 파일에 자체 컴파일에 필요한 것만 포함하는지 확인하십시오. C ++ 파일에 대해 누락 된 포함 파일은 C ++ 파일 자체에 추가 할 수 있습니다.

각 포함 및 소스 파일에 대해 각 포함 파일을 한 번에 하나씩 주석 처리하고 컴파일되는지 확인하십시오.

포함 파일을 알파벳순으로 정렬하는 것도 좋은 생각이며 이것이 불가능한 경우 주석을 추가하십시오.


2
매우 많은 수의 구현 파일이 관련되어 있다면이 주석이 얼마나 실용적인지 잘 모르겠습니다.
Sonny

1

다음 #defines 중 하나 또는 둘 다를 추가하면 종종 불필요한 헤더 파일이 제외되고 특히 Windows API 함수를 사용하지 않는 코드의 경우 컴파일 시간이 크게 향상 될 수 있습니다.

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN

보기 http://support.microsoft.com/kb/166474를


1
둘 필요가 - VC_EXTRALEAN는 WIN32_LEAN_AND_MEAN 정의하지 않는다
에이단 라이언

1

아직 변경하지 않은 경우 미리 컴파일 된 헤더를 사용하여 변경하지 않을 모든 항목 (플랫폼 헤더, 외부 SDK 헤더 또는 프로젝트의 이미 완료된 정적 부분)을 포함하면 빌드 시간이 크게 달라집니다.

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

또한 프로젝트에 너무 늦을 수 있지만 프로젝트를 섹션으로 구성하고 모든 로컬 헤더를 하나의 큰 메인 헤더로 묶지 않는 것이 좋습니다. 그러나 약간의 추가 작업이 필요합니다.


미리 컴파일 된 헤더에 대한 훌륭한 설명 : cygnus-software.com/papers/precompiledheaders.html (최신 버전의 VisualStudio에서 미리 컴파일 된 헤더 자동 생성이 손상되었는지 확실하지 않지만 확인할 가치가 있습니다.)
idbrii

1

Eclipse CDT로 작업 할 경우 http://includator.com 을 사용하여 포함 구조를 최적화 할 수 있습니다. 그러나 Includator는 VC ++의 사전 정의 포함에 대해 충분히 알지 못할 수 있으며 올바른 포함으로 VC ++를 사용하도록 CDT를 설정하는 것은 아직 CDT에 내장되어 있지 않습니다.


1

최신 Jetbrains IDE 인 CLion은 현재 파일에서 사용되지 않는 포함 항목을 회색으로 자동 표시합니다.

IDE에서 사용하지 않는 모든 포함 (함수, 메소드 등)의 목록을 가질 수도 있습니다.


0

기존 답변 중 일부는 어렵다고 말합니다. 순방향 선언이 적절한 경우를 감지하려면 완전한 컴파일러가 필요하기 때문에 사실입니다. 기호의 의미를 모르면 C ++를 구문 분석 할 수 없습니다. 문법은 너무 모호합니다. 특정 이름이 클래스 (앞으로 선언 될 수 있음) 또는 변수 (불가능)를 명명하는지 알아야합니다. 또한 네임 스페이스를 인식해야합니다.


"어떤 #include가 필요한지 결정하는 것은 중지 문제를 해결하는 것과 동일합니다. 행운을 빕니다. :)"라고 말할 수 있습니다. 물론 휴리스틱을 사용할 수 있지만이 작업을 수행하는 무료 소프트웨어는 없습니다.
porges


0

더 이상 필요하지 않다고 생각하는 특정 헤더 (예 : string.h)가있는 경우 포함을 주석 처리 한 다음 모든 포함 아래에 넣을 수 있습니다.

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

물론 인터페이스 헤더는 다른 #define 규칙을 사용하여 CPP 메모리에 포함 된 내용을 기록 할 수 있습니다. 또는 규칙이없는 경우이 방법은 작동하지 않습니다.

그런 다음 재건하십시오. 세 가지 가능성이 있습니다.

  • 괜찮습니다. string.h는 컴파일에 중요하지 않았으며 포함을 제거 할 수 있습니다.

  • #error 트립. string.g가 어떻게 든 간접적으로 포함되었습니다. string.h가 필요한지 여전히 알 수 없습니다. 필요한 경우 직접 #include해야합니다 (아래 참조).

  • 다른 컴파일 오류가 발생합니다. string.h가 필요했고 간접적으로 포함되지 않았으므로 포함은 처음부터 정확했습니다.

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.