C++
C++ 모듈 (Module, C++20)
joonior
2025. 3. 25. 01:12
헤더 파일 방식의 문제점
C++에서는 전통적으로 #include를 사용하여 헤더 파일을 포함하는 방식을 사용한다. 하지만 이 방식에는 여러 가지 문제가 있다.
1) 중복된 파싱 비용 (Compile-time Overhead)
- 헤더 파일은 여러 번 포함될 수 있으며, 각 번역 단위(Translation Unit)마다 같은 헤더 파일이 반복적으로 파싱됨.
- 프로젝트가 커질수록 빌드 시간이 기하급수적으로 증가함.
2) 전처리기의 한계 (Preprocessor Limitations)
- #include는 단순한 텍스트 복사-붙여넣기 방식으로 동작함.
- #define, #ifdef 등의 전처리 매크로로 인해 의도하지 않은 코드 변화가 발생할 수 있음.
- 네임스페이스와 달리 전역 네임스페이스 오염(Global Namespace Pollution) 문제가 있음.
3) 불필요한 의존성 문제
- 불필요한 헤더 파일까지 포함될 경우, 필요 없는 코드까지 컴파일하게 됨.
- #include는 파일 단위로 작동하므로, 실제로 사용하지 않는 선언도 포함될 가능성이 높음.
4) 빌드 캐싱(Incremental Build) 불가능
- #include 방식에서는 작은 변경 사항(예: 헤더 파일 수정)만 있어도 전체 파일을 다시 컴파일해야 함.
- 재사용할 수 있는 단위가 없어서 빌드 캐싱을 활용하기 어려움.
C++ 모듈의 도입
C++ 모듈은 위와 같은 문제를 해결하기 위해 C++20에서 정식 도입되었습니다. 주요 특징은 다음과 같습니다.
1) 컴파일 단위로 모듈화 (No Text Inclusion)
- #include 대신 import 키워드를 사용하여 모듈을 가져옴.
- import는 텍스트가 아니라 사전 컴파일된 바이너리 형태로 불러오기 때문에 중복 파싱이 발생하지 않음.
2) 전역 네임스페이스 오염 방지
- 모듈을 통해 선언된 심볼(Symbol)은 명확하게 정의된 인터페이스를 통해서만 접근 가능.
- #define 같은 전처리기가 개입할 수 없으므로 매크로 오염 문제 해결.
3) 의존성 감소 및 빌드 성능 향상
- 모듈 단위로 컴파일 결과를 캐싱할 수 있어, 변경이 없으면 재사용 가능.
- 큰 프로젝트에서 빌드 시간이 크게 단축됨.
4) 명확한 인터페이스/구현 분리
- 모듈 파일에서 공개(Export)할 심볼을 명확히 정의 가능.
- export 키워드를 사용하여 외부에 노출할 API만 공개함.
사용 예제
1) 모듈 정의 (파일: math_module.ixx)
C++ 모듈은 .ixx 확장자를 가진 인터페이스 파일로 정의할 수 있다.
// math_module.ixx
export module math_module; // 모듈 선언
export int add(int a, int b) {
return a + b;
}
export int multiply(int a, int b) {
return a * b;
}
2) 모듈을 사용하는 코드 (파일: main.cpp)
모듈을 import하여 사용한다.
import math_module; // 모듈 가져오기
#include <iostream>
int main() {
std::cout << "3 + 5 = " << add(3, 5) << std::endl;
std::cout << "3 * 5 = " << multiply(3, 5) << std::endl;
return 0;
}
3) 빌드 및 실행
기본적으로 C++ 모듈을 지원하는 컴파일러에서 다음과 같이 빌드할 수 있습니다.
ex) gcc (g++)
g++ -std=c++20 -fmodules-ts -c math_module.ixx -o math_module.o
g++ -std=c++20 main.cpp math_module.o -o main
./main