λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
C++ μ‘μš©

ν…œν”Œλ¦Ώ 메타 ν”„λ‘œκ·Έλž˜λ°μ˜ λͺ¨λ“  것(Template Meta Programming)

by μ„œμ•„λž‘πŸ˜ 2024. 8. 28.

 

 

ν…œν”Œλ¦Ώ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ° (Template Metaprogramming)

ν…œν”Œλ¦Ώ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°(Template Metaprogramming)은 C++의 κ°•λ ₯ν•œ ν…œν”Œλ¦Ώ μ‹œμŠ€ν…œμ„ μ΄μš©ν•˜μ—¬ 컴파일 νƒ€μž„μ— μ½”λ“œμ˜ 일뢀λ₯Ό μƒμ„±ν•˜κ±°λ‚˜ κ³„μ‚°ν•˜λŠ” ν”„λ‘œκ·Έλž˜λ° κΈ°λ²•μž…λ‹ˆλ‹€. 이 기법은 C++의 ν…œν”Œλ¦Ώμ„ λ‹¨μˆœν•œ μ½”λ“œ μž¬μ‚¬μš© λ„κ΅¬λ‘œ μ‚¬μš©ν•˜λŠ” κ²ƒμ—μ„œ 더 λ‚˜μ•„κ°€, λ³΅μž‘ν•œ κ³„μ‚°μ΄λ‚˜ 쑰건 처리λ₯Ό 컴파일 μ‹œμ μ— 미리 μˆ˜ν–‰ν•˜μ—¬, μ‹€ν–‰ μ‹œμ μ˜ μ„±λŠ₯을 κ·ΉλŒ€ν™”ν•  수 있게 ν•©λ‹ˆλ‹€.

1. κΈ°λ³Έ κ°œλ…

ν…œν”Œλ¦Ώ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°μ€ 일반적인 ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄μ—μ„œμ˜ μ‹€ν–‰ μ‹œμ (runtime) ν”„λ‘œκ·Έλž˜λ°κ³ΌλŠ” λ‹€λ₯΄κ²Œ, ν”„λ‘œκ·Έλž¨μ΄ 컴파일될 λ•Œ μˆ˜ν–‰λ©λ‹ˆλ‹€. μ΄λŠ” ν”„λ‘œκ·Έλž¨μ΄ μ‹€ν–‰λ˜κΈ° 전에 λ‹€μ–‘ν•œ μž‘μ—…μ„ 미리 μˆ˜ν–‰ν•˜μ—¬, μ‹€ν–‰ μ‹œμ μ—μ„œλŠ” μ΅œλŒ€ν•œ 효율적인 μ½”λ“œλ§Œμ„ μ‹€ν–‰ν•  수 μžˆλ„λ‘ ν•˜λŠ” 것을 λͺ©ν‘œλ‘œ ν•©λ‹ˆλ‹€.

μ΄λŸ¬ν•œ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°μ„ 톡해, μˆ˜ν•™μ  계산, 쑰건 처리, μ½”λ“œ 생성 등을 컴파일 μ‹œμ μ— μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • μ£Όμš” νŠΉμ§•:
    • 컴파일 νƒ€μž„ 계산: λ³΅μž‘ν•œ κ³„μ‚°μ΄λ‚˜ 논리λ₯Ό 컴파일 νƒ€μž„μ— μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • μ½”λ“œ 생성: μ‹€ν–‰ μ‹œμ μ— λ™μ μœΌλ‘œ 생성할 ν•„μš” 없이, 컴파일 νƒ€μž„μ— ν•„μš”ν•œ μ½”λ“œλ‚˜ 데이터λ₯Ό 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.
    • μ„±λŠ₯ μ΅œμ ν™”: μ‹€ν–‰ μ‹œμ μ˜ 뢀담을 쀄여 ν”„λ‘œκ·Έλž¨μ˜ μ„±λŠ₯을 ν–₯μƒμ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.
    • μž¬μ‚¬μš©μ„±: ν…œν”Œλ¦Ώμ„ μ΄μš©ν•œ μΌλ°˜ν™”λœ μ½”λ“œλ₯Ό μž‘μ„±ν•¨μœΌλ‘œμ¨, μž¬μ‚¬μš©μ„±κ³Ό ν™•μž₯성을 높일 수 μžˆμŠ΅λ‹ˆλ‹€.


2. 예제 μ½”λ“œμ™€ μ„€λͺ…

예제 1: νŒ©ν† λ¦¬μ–Ό 계산

ν…œν”Œλ¦Ώ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°μ„ μ‚¬μš©ν•˜μ—¬ νŒ©ν† λ¦¬μ–Όμ„ 컴파일 νƒ€μž„μ— κ³„μ‚°ν•˜λŠ” μ˜ˆμ œμž…λ‹ˆλ‹€.

#include <iostream>

// κΈ°λ³Έ ν…œν”Œλ¦Ώ (μž¬κ·€ 호좜)
template<int N>
struct Factorial {
    static constexpr int value = N * Factorial<N - 1>::value;
};

// μž¬κ·€ μ’…λ£Œ 쑰건 (N = 0)
template<>
struct Factorial<0> {
    static constexpr int value = 1;
};

int main() {
    std::cout << "Factorial of 5: " << Factorial<5>::value << std::endl;  // Output: 120
    return 0;
}

μ„€λͺ…:

  • 이 μ˜ˆμ œμ—μ„œ Factorial ꡬ쑰체 ν…œν”Œλ¦Ώμ€ μž¬κ·€μ μœΌλ‘œ νŒ©ν† λ¦¬μ–Όμ„ κ³„μ‚°ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, Factorial<5>λŠ” 5 * Factorial<4>둜 ν™•μž₯λ©λ‹ˆλ‹€.
  • Factorial<0>은 κΈ°λ³Έ 쑰건으둜, μž¬κ·€ ν˜ΈμΆœμ„ μ’…λ£Œν•˜λŠ” 역할을 ν•©λ‹ˆλ‹€.
  • μ΅œμ’…μ μœΌλ‘œ, 컴파일 νƒ€μž„μ— Factorial<5>::valueλŠ” 120으둜 κ³„μ‚°λ©λ‹ˆλ‹€.

예제 2: 컴파일 νƒ€μž„μ—μ„œ 쑰건뢀 νƒ€μž… 선택

ν…œν”Œλ¦Ώ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°μ„ μ‚¬μš©ν•˜μ—¬ 컴파일 νƒ€μž„μ— 쑰건에 따라 νƒ€μž…μ„ μ„ νƒν•˜λŠ” μ˜ˆμ œμž…λ‹ˆλ‹€.

#include <iostream>
#include <type_traits>

// ν…œν”Œλ¦Ώ 쑰건에 따라 νƒ€μž…μ„ μ„ νƒν•˜λŠ” ꡬ쑰체
template<bool Condition, typename TrueType, typename FalseType>
struct Conditional {
    using type = TrueType;
};

// νŠΉμˆ˜ν™”: 쑰건이 false일 λ•Œ
template<typename TrueType, typename FalseType>
struct Conditional<false, TrueType, FalseType> {
    using type = FalseType;
};

int main() {
    using SelectedType = Conditional<(sizeof(int) > 4), double, int>::type;

    if constexpr (std::is_same_v<SelectedType, double>) {
        std::cout << "Selected type is double." << std::endl;
    } else {
        std::cout << "Selected type is int." << std::endl;
    }

    return 0;
}

μ„€λͺ…:

  • Conditional κ΅¬μ‘°μ²΄λŠ” 주어진 쑰건(컴파일 νƒ€μž„ 쑰건)에 따라 TrueType λ˜λŠ” FalseType 쀑 ν•˜λ‚˜λ₯Ό μ„ νƒν•©λ‹ˆλ‹€.
  • 이 μ˜ˆμ œμ—μ„œλŠ” sizeof(int) > 4λΌλŠ” 쑰건을 톡해 SelectedType이 double인지 int인지λ₯Ό κ²°μ •ν•©λ‹ˆλ‹€.
  • if constexpr을 μ‚¬μš©ν•˜μ—¬ μ„ νƒλœ νƒ€μž…μ— 따라 λ‹€λ₯Έ μ½”λ“œλ₯Ό μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

예제 3: 컴파일 νƒ€μž„μ—μ„œ 제곱 계산

ν…œν”Œλ¦Ώ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°μ„ μ‚¬μš©ν•˜μ—¬ 숫자의 μ œκ³±μ„ 컴파일 νƒ€μž„μ— κ³„μ‚°ν•˜λŠ” μ˜ˆμ œμž…λ‹ˆλ‹€.

#include <iostream>

// κΈ°λ³Έ ν…œν”Œλ¦Ώ (μž¬κ·€ 호좜)
template<int N, int Power>
struct PowerOf {
    static constexpr int value = N * PowerOf<N, Power - 1>::value;
};

// μž¬κ·€ μ’…λ£Œ 쑰건 (Power = 0)
template<int N>
struct PowerOf<N, 0> {
    static constexpr int value = 1;
};

int main() {
    std::cout << "2^4: " << PowerOf<2, 4>::value << std::endl;  // Output: 16
    return 0;
}

μ„€λͺ…:

  • PowerOf ꡬ쑰체 ν…œν”Œλ¦Ώμ€ μž¬κ·€μ μœΌλ‘œ 숫자의 μ œκ³±μ„ κ³„μ‚°ν•©λ‹ˆλ‹€.
  • PowerOf<N, 0>은 κΈ°λ³Έ 쑰건으둜, μž¬κ·€ ν˜ΈμΆœμ„ μ’…λ£Œν•˜λŠ” 역할을 ν•©λ‹ˆλ‹€.
  • 컴파일 νƒ€μž„μ— PowerOf<2, 4>::valueλŠ” 16으둜 κ³„μ‚°λ©λ‹ˆλ‹€.


3. ν…œν”Œλ¦Ώ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°μ˜ μž₯단점

μž₯점:

  • 컴파일 νƒ€μž„ μ΅œμ ν™”: λ³΅μž‘ν•œ 계산을 μ‹€ν–‰ μ‹œμ μ΄ μ•„λ‹Œ 컴파일 μ‹œμ μ— μˆ˜ν–‰ν•  수 μžˆμ–΄ μ„±λŠ₯이 ν–₯μƒλ©λ‹ˆλ‹€.
  • νƒ€μž… μ•ˆμ „μ„±: ν…œν”Œλ¦Ώ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°μ€ κ°•λ ₯ν•œ νƒ€μž… 검사λ₯Ό μ œκ³΅ν•˜μ—¬, μ½”λ“œμ˜ μ•ˆμ „μ„±μ„ λ†’μž…λ‹ˆλ‹€.
  • μ½”λ“œ μž¬μ‚¬μš©μ„±: μΌλ°˜ν™”λœ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆμ–΄, λ‹€μ–‘ν•œ 상황에 맞게 μž¬μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

단점:

  • λ³΅μž‘μ„±: ν…œν”Œλ¦Ώ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°μ€ 맀우 λ³΅μž‘ν•  수 있으며, μ΄ν•΄ν•˜κΈ° μ–΄λ €μš΄ μ—λŸ¬ λ©”μ‹œμ§€λ₯Ό μ΄ˆλž˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 컴파일 μ‹œκ°„ 증가: λ³΅μž‘ν•œ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°μ€ 컴파일 μ‹œκ°„μ„ μ¦κ°€μ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • 디버깅 어렀움: 컴파일 νƒ€μž„μ— μ‹€ν–‰λ˜λŠ” μ½”λ“œμ΄κΈ° λ•Œλ¬Έμ—, 디버깅이 맀우 μ–΄λ ΅μŠ΅λ‹ˆλ‹€.


4. ν™œμš© 사둀

  • κ³ κΈ‰ 라이브러리 개발: ν…œν”Œλ¦Ώ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°μ€ κ³ κΈ‰ C++ 라이브러리(예: Boost, Eigen, MPL)μ—μ„œ 자주 μ‚¬μš©λ©λ‹ˆλ‹€.
  • 컴파일 νƒ€μž„ 계산: μˆ˜ν•™μ  κ³„μ‚°μ΄λ‚˜ μƒμˆ˜ 생성에 μ‚¬μš©λ©λ‹ˆλ‹€.
  • 정적 λ‹€ν˜•μ„± κ΅¬ν˜„: λŸ°νƒ€μž„ λ‹€ν˜•μ„± λŒ€μ‹  컴파일 νƒ€μž„μ— λ‹€ν˜•μ„±μ„ κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 정적 검사: 컴파일 νƒ€μž„μ— νƒ€μž…μ΄λ‚˜ 쑰건을 κ²€μ¦ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.

ν…œν”Œλ¦Ώ λ©”νƒ€ν”„λ‘œκ·Έλž˜λ°μ€ C++의 κ°•λ ₯ν•œ κΈ°λŠ₯ 쀑 ν•˜λ‚˜λ‘œ, κ³ κΈ‰ C++ ν”„λ‘œκ·Έλž˜λ°μ—μ„œ ν•„μˆ˜μ μΈ λ„κ΅¬μž…λ‹ˆλ‹€. 이λ₯Ό 톡해 컴파일 νƒ€μž„μ— μ½”λ“œμ˜ μ΅œμ ν™”μ™€ μ•ˆμ „μ„±μ„ 보μž₯ν•  수 있으며, λ³΅μž‘ν•œ 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 데 맀우 μœ μš©ν•©λ‹ˆλ‹€.

λŒ“κΈ€