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