#define _SOME_HEADER_
// 헤더 내용들이 많이 있다.
#endif
OtherHeader.h
두 가지 방식의 차이점이라든지 비교우위 등을 알고 싶어하는 이들이 많다.
짤막하게 짚고 넘어가자면 전자(SomeHeader)는 언어 차원의 표준이며 컴파일러가 항상 지원해야하는 믿을 수 있는 기능이고
후자(OtherHeader)는 일부 성능 좋은(?) 컴파일러만 지원하는 변종 기능이다.
변종인 만큼 후자의 방식은 전처리기가 아예 파일도 열어보지 않고 컴파일을 건너뛰고,
전자는 일단 파일을 열어보고 건너뛴다는 차이점을 가지며, 변종이 조금 더 속도에서 우세하다고 한다.
맞는 얘기들이었지만 사실 이젠 좀 철 지난 이야기들이다.
실제로 #ifndef 든 #pragma 든 둘 다 전처리기(Preprocessor) 지시자이며 컴파일을 하기 전에 먼저 처리되는 것들이다.
#ifndef 와 #endif (#else 라든지 #elif 도 좋겠다) 사이에 수백만줄 정도가 있다면 #pragma once 와 차이가 좀 벌어질 수 있으나
일반적인 기준에서 잘 짜여진 헤더파일(그 자체로 문서로 여겨질 수 있는 요약본 형식의)의 크기 안에서는
그럭저럭 무난하게 무시해줄 수 있다.
(물론 그렇다고 해서 그 헤더 파일이 수천번 include 되어도 무난하다는 소리는 절대 아니다)
중요한 것은 정말로 파일 전체가(꼭 헤더에 한정되지 않는다) 동일 obj 에서 한 번만 로딩된다면 #pragma once 도 괜찮겠지만
다른 조건들이 포함되어 있다면 #ifndef 를 활용하는 것이다. #ifndef 의 원래 용도를 잘 생각해보자.
(혹자는 #pragma once 가 표준이 아님을 들어 #ifndef 를 권하고 있으나 사실 가장 널리 사용되는 상위 컴파일러들은 모두 지원하고 있으므로 딱히 문제가 되진 않는다)
만약 헤더 파일이 수천줄 쯤 된다면, 그리고 그 헤더 파일을 많은 곳에서 include 한다면 #ifndef 보다는 #pragma once 를
쓰는 것이 확실히 전처리 속도에 도움이 된다.
감이 잘 안 오는 사람들을 위해 아래와 같은 스샷을 마련했다
대충 파일 구조는 위의 것을 보면 쉽게 알 수 있을 것이고
컴파일 결과를 그냥 IDE에서 컴파일해버리면 알기 어려우니 직접 명령어를 조작하여 컴파일을 시도해보는 것이 좋다.
원래 영어 IDE 를 사용하지만 스샷을 위해 한글을 마련했다. 왕친절한 것 같다.
result.cpp 의 내용을 보자
드문드문 보이는 빈 공백들은 바로 전처리기가 #ifndef ~ #endif 같은 조건에 걸려 삭제해버린 줄들이다.
(물론 쓸모 없는 전처리기 구문 자체도 지우기 때문에 원래 3번 라인 밑에 나와야 하는 #ifndef _IFNDEF_ 같은 구문이 모두 빈줄로 대체되어 있다)
other.cpp 의 경우를 보아야 #ifndef 와 #pragma once 의 차이점을 확실히 알 수 있다.
other.cpp 는 22줄에서 보다시피 처음으로 ifndef.h 파일을 포함하였는데 이후 middle.h 에서 다시 한 번 포함한다(36줄)
그러나 앞서 이미 포함되어 있었으므로 모두 공백으로 대체되었다.
그러나 30줄에서 보다시피 other.cpp 는 pragmaonce.h 파일도 먼저 포함하였는데 이후 44번째 줄에서 보다시피
middle.h 가 선언한 #include "pragmaonce.h" 명령은 공백으로 대체되어 아예 포함조차 시도하지 않았다.
이것을 통해 #ifndef 와 #pragma once 의 실제 움직임을 조금은 따라가 볼 수 있는가?
#line 지시문에 대해서는 관심있는 사람들에 한해 찾아보도록 남겨두겠다.
그런데 주의할 점이 있다. 여기서 1회 포함 및 생성이란 얘기는 "파일 단위"에서의 이야기다.
그리고 이것이야말로 Unity Build 라는 기법이 왜 필요한가의 이유가 된다.
내가 심심해서 main.cpp 와 other.cpp 두 개를 만들지는 않았을 것이다.
우리는 보통 #ifndef (또는 #pragma once) 를 사용하면 헤더 파일을 전체 프로젝트에서 한 번만 include 할 것이라고
착각할 수 있는데(또는 별 신경 안 쓰고 넘어갈 수 있는데) 위에서 보다시피, 사실이 아니다.
한 번만 include 하는 것은 모두 오브젝트 단위에서의 이야기다.
오브젝트가 다르면 제 아무리 이전에 8만줄짜리 파일을 포함하여 컴파일을 이미 진행했다 하더라도
컴파일러는 다시 컴파일을 진행해버린다는 이야기다.
이런 짓을 하지 않기 위해 Unity Build 라는 스킬이 등장한 것이고 이와 관련해서는 서툰 내 글솜씨보다
훨씬 잘 된 자료가 있으므로 그것을 공유하며 이 포스팅을 마칠까 한다
http://www.slideshare.net/devcatpublications/ndc2010-unity-build