C++/C++ 특수 강좌 & 모던 C++

[C++심화 강좌] C++20 모듈 사용하기

카루-R 2021. 5. 15. 10:00
반응형

환영합니다, Rolling Ress의 카루입니다.

처음에는 무척 기다렸고 신기한 기능이었는데, 막상 추가가 되니까 생각보다 별 감흥이 없네요. 아니면 요즘 제가 UWP 앱 개발 중이라 C#에 너무 에너지를 쏟고 있는 탓인지도 모릅니다. 비주얼스튜디오에서 C#을 사용할 땐 정말 막강한 인텔리센스를 제공하죠.

그래서 말인데, 오늘 할 얘기도 인텔리센스에 관해 먼저 이야기하겠습니다. 우선 몇 년 전과는 달리 최근에는 모듈 지원이 대폭 강화되면서 ixx 파일에 대한 인텔리센스도 제공이 됩니다. 심지어 모듈을 import 한 후에도 인텔리센스가 되긴 되는데, 클래스 내부 멤버들까지는 모르는 것 같아요. 컴파일은 잘 되는데 소스 편집기에서 빨간 줄이 사라지지 않는 요상한 상황이 발생합니다.

아무튼, 아실 분들은 아시겠지만 C++20에서 모듈 기능이 추가되었죠. 이제 비주얼스튜디오에서 제대로 지원을 합니다. 이 기능을 사용하려면 Visual Studio Installer에서 C++ 모듈 기능을 선택해야 합니다. 없어도 사용은 가능한데, 표준 라이브러리는 이 옵션을 선택해야만 설치가 됩니다.

 

 

C++ 프로젝트를 하나 만들고 C++ 언어 표준은 Preview로, 그리고 마지막에 모듈 사용은 "예"를 선택해주세요. 그럼 이제 준비가 끝납니다.

 

 

소스 파일 디렉터리를 우클릭하고 추가를 눌러주세요. 여기서 맨 아래에 있는 모듈 인터페이스 유닛을 클릭합니다. 이름은 자유롭게 정해주시고, 일단 표준 라이브러리 모듈부터 보겠습니다.

 

import std.core; 

int main() 
{
    std::cout << "Imported std.core" << std::endl; 
}

[출력결과] Imported std.core

 

여전히...인텔리센스가 엉망이긴 하네요. 분명 아직까지는 빨간 밑줄이 뜰 겁니다. 그래도 컴파일은 되니 크게 신경 쓰진 마세요. 아무리 C#이 리플렉션이 잘 된다고는 해도 C#에 비하면 C++은 찬밥 신세가 아닌가...살짝 아쉬운 부분입니다.

import std.core;

이 부분은 std.core이라는 모듈을 사용하겠다는 뜻입니다. C#의 using, Python의 import와 비슷해요. 참고로 C++에서 제공하는 표준 라이브러리 모듈이 있는데, 다음과 같습니다. 기존 헤더파일을 대체한다고 볼

수도 있어요.

std.core - 아래 것들을 제외한 대부분의 라이브러리

std.regex - <regex> 헤더의 라이브러리

std.filesystem - <filesystem> 헤더의 라이브러리

std.memory - <memory> 헤더의 라이브러리

std.threading - <atomic>, <condition_variable>, <future>, <mutex>, <shared_mutex>, <thread> 라이브러리

아마 시간이 흐르면 #include 구문은 완전히 자취를 감추지 않을까 싶네요. 물론 한 10~20년 정도 지나야겠지만요. 모듈이 자리잡으면 C++98에서 C++11로 넘어온 것보다 더 큰 변화가 일어나지 않을까 싶습니다.

자, 그런 의미에서 다른 모듈도 사용해볼까요.

 

import std.core; 
import std.memory;
import std.threading;

int main() 
{
    std::thread t([] { std::cout << "func() called." << std::endl; });
    std::cout << "Main function" << std::endl;
    t.join(); 
    
    auto ptr = std::make_unique<int>(5); 
    (*ptr)++; 
    std::cout << "*ptr: " << *ptr << std::endl; 
}

[출력결과]

Main function

func() called. 

*ptr: 6

 

네...여전히 인텔리센스가 엉망입니다. 아직까지는 이걸로 뭐 의미 있는 코딩을 한다거나 하는 건 불가능해요. 일단 이렇게 쓸 수 있다는 것만 알아두시면 될 것 같습니다. 헤더만 #include <...>에서 import ...;으로 바뀌었다 뿐이지, 코드 자체는 크게 달라지지 않습니다. 전처리기가 빠진 게 제일 마음에 들어요. #define은 constexpr로, #include는 import로... 사실 이런 전처리기가 C로부터 받은 것들이라 C++다운 새로운 구문이 추가되는 것에 굉장히 만족하고 있습니다.

이제 아까 만든 파일을 봅시다. 아래 구문을 입력해주세요.

 

// 파일: Module.ixx
module; 

#include <iostream> 

export module Module; 
export class Test
{
public: 
    template<typename ...Args> 
    static void print(Args&&... args) 
    {
        (std::cout << ... << args) << std::endl; 
    } 
};

 

module; 구문을 통해 이 파일이 모듈임을 컴파일러에게 알립니다. 표준 C++에서는 필요 없는 걸로 아는데..비주얼스튜디오에서는 이게 있어야 하더라고요. 그리고 아랫줄에는 export module (모듈이름);을 통해 모듈 이름을 선언하고, export합니다. module M; export M; 해도 되는 것 같기는 한데 저렇게 하는 게 더 깔끔하긴 하네요.

그리고 레거시 헤더 파일을 쓰는 방법. module; 구문과 export module Module; 사이에 추가할 헤더파일을 모두 넣어주세요. 그럼 헤더가 같이 컴파일되어 하나의 모듈이 만들어집니다. 어차피 module 파일은 #include가 아니라 import 할 뿐이니까, 모듈 파일 자체에는 헤더파일이 많아도 큰 영향을 미치지 않겠죠. module의 궁극적인 목적은 #include 전처리기가 헤더파일을 복붙하는 과정에서 발생하는 비효율을 막고자 하는 것이니까요.

아무튼, 이렇게 클래스를 정의하고 export하면 외부에서 사용할 수 있게 됩니다.

 

import Module;
import std.core; 

int main() 
{
    Test::print(std::string("Modules "), std::string("of "), std::string("C++"), '!'); 
}

[출력결과] Modules of C++!

 

자, 그런데 우리가 Module을 정의하고 import했는데 또 추가로 std.core를 import하는 게 귀찮죠. 네. 그래서 모듈을 정의하면서 아래와 같이 쓰는 것도 가능합니다.

 

export module Module; 
export import std.core; 

export class Test 
{
public: 
    template<typename ...Args> 
    static void print(Args&&... args) 
    {
        (std::cout << ... << args) << std::endl; 
    } 
};

 

이번에는 헤더파일을 포함하지 않았기 때문에 module; 문장을 삭제했습니다. 바로 export module Module을 통해서 Module이라는 모듈을 정의했어요. 그리고 아랫문장이 중요한데,

export import std.core;

이게 뭔가 싶으시겠지만 간단합니다. export (import std.core);로 해석하시면 됩니다. std.core을 불러와서 이 모듈 안에서 사용하고, 또 불러온 이 모듈을 그대로 외부로 export한다는 뜻입니다. 즉, 외부에서는 import Module;만 해도 자동으로 std.core까지 import가 된다는 거죠.

 

import Module;

int main() 
{
    Test::print(std::string("Modules "), std::string("of "), std::string("C++"), '!');
    std::cout << "Feel free" << std::endl; 
}

[출력결과] Modules of C++! Feel free

 

간단합니다. 컴파일 속도가 확실히 빨라진 게 느껴지네요. 모듈은 한 번 컴파일하면 변경사항이 발생하지 않을 경우 기존에 컴파일한 내용을 바탕으로 '참고'만 합니다. 기존 C++의 컴파일 속도가 느렸던 게 헤더파일을 통으로 긁어오는 전처리기 특성상 매번 새로 컴파일을 해야 해서 속도가 느렸던 거죠.

모듈에 대한 지원이 더 확대되었으면 하는 바람입니다.

반응형