성공할 게임개발자

[Effective c++] [7. 템플릿과 제네릭 프로그래밍] 48. 템플릿 메타프로그래밍, 하지 않겠는가? 본문

C++

[Effective c++] [7. 템플릿과 제네릭 프로그래밍] 48. 템플릿 메타프로그래밍, 하지 않겠는가?

fn000 2025. 6. 30. 10:22

템플릿 메타 프로그래밍(TMP)은 컴파일 도중에 실행되는 템플릿 기반의 프로그램을 작성하는 일을 말한다.

TMP의 강점 두가지

1. TMP를 쓰면 다른 방법으로는 까다롭거나 불가능한 일을 굉장히 쉽게 할 수 있다

2. TMP는 C++컴파일이 진행되는 동안에 실행되기 때문에, 기존 작업을 런타임영역에서 컴파일 타임 영역으로 전환할 수 있다

2의 이득 : 일반적으로 프로그램 실행 도중에 잡혀 왔던 몇몇 에러들을 컴파일 도중에 찾을 수 있다

2의 이득 : TMP를 써서 만든 C++프로그램이 확실히 모든 면에서 효율적일 여지가 많다

컴파일 타임에 동작을 다 해 가지고 오기 때문에 실행 코드가 작아지고 실행 시간도 짧아지며 메모리도 적게 잡아먹는다.

 

항목 47 의 advance 코드

template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
    // 임의 접근 반복자라면
	if(typeid(typename iterator_traits<IterT>::iterator_category) ==
    	typeid(random_acces_iterator_tag))
        {
        	iter += d;
        }
    else
    {
    	if(d >= 0){ while(d--) ++iter; }
        else{ while(d++) --iter; }
    }
}

항목 47 에서 설명했듯, typeid를 쓰는 방법은 특성정보를 쓰는 방법보다 효율이 떨어진다. 

이유는 다음과 같다

1. 타입 점검 동작이 런타임에 일어나기 때문이다

2. 런타임 타입 점검을 수행하는 코드는 어쩔 수 없이 실행 파일에 들어가야 하기 때문이다

 

TMP는 그 자체가 튜링 완전성을 갖고 있는 것으로도 알려져 왔다. 범용 프로그래밍 언어처럼 어떤 것이든 계산할 수 있는 능력을 갖고 있다는 뜻이다. 이 말은 TMP가 변수선언도 되고 루프도 실행되며 함수를 작성하고 호출하는 것도 된다.  하지만 알아두어야 할 것이 TMP에서 if-else조건문을 나타내는 데는 통상의 if-a 문이 아닌 템플릿 특수화를 사용한 것 처럼 C++에서 쓰이는 구문요소들과 다른 모습을 갖고 있다.

 

TMP의 동작원리 루프

TMP의 루프는 재귀 함수 호출을 만들지 않고, 재귀식 템플릿 인스턴스화 한다.                                                                               예를 들어,

.

template<unsigned n>
struct Factorial
{
    enum { value = n * Factorial<n-1>::value };
};

template<>
struct Factorial<0>
{
    enum { value = 1 };
};

int main()
{
    cout << Factorial<5>::value;		// 120을 런타임 계산 없이 출력
    cout << Factorial<10>::value;		// 3628800을 런타임 계산 없이 출력
}

 

 

C++에서 TMP가 이득을 보는 경우

1. 치수 단위의 정확성 확인

 

예를 들면, 속도를 나타내는 변수에 질량을 나타내는 변수를 대입하면 에러인 것 처럼 TMP를 사용하면 프로그램 안에서 쓰이는 치수단위의 조합이 제대로 맞춰졌는디 컴파일 타임에 볼 수 있다

 

 

 

2. 행렬 연산의 최적화

typedef SquareMatrix<double, 10000> BigMatrix;
BigMatrix m1, m2, m3, m4, m5;
...
BigMatrix result = m1 * m2 * m3 * m4 * m5;

이 경우 일반적인 방법으론 네개의 임시 행렬이 생겨야 한다. operator*를 한번씩 호출할 때마다 반환되는 결과로 생기는 것이다. 또한, 행렬 원소들 사이에 곱셈을 해야하므로 네개의 루프가 순차적으로 만들어질 수 밖에없다. 여기서 나오는 리스크를 TMP를 응용한 표현식 템플릿을 사용하면 덩치 큰 임시 객체를 없애는 것은 물론이고 루프까지 합쳐 버릴 수 있다.

 

 

 

3. 맞춤식 디자인 패턴 구현의 생성

TMP를 사용한 정책 기반 설계를 사용하면 따로 만들어진 설계상의 선택을 나타내는 템플릿을 만들어 낼 수 있다. 예를 들면, 몇개의 스마트 포인터 동작 정책을 구현한 각각의 템플릿을 만들고 사용자가 조합하여 수백가지의 스마트 포인터 타입을 컴파일 타임에 생성할 수 있게 하는 것이다. 이걸 생성식 프로그래밍의 기초라고 부른다.

 

 

 

중요포인트!

템플릿 메타 프로그래밍은 기존 작업을 런타임에서 컴파일 타임으로 전환하는 효과를 낸다. 따라서 TMP를 쓰면 선행 에러탐지와 높은 런타임 효율을 얻는다.

 

TMP는 정책 선택의 조합에 기반하여 사용자 정의 코드를 생성하는데 쓸 수 있으며, 또한 특정 타입에 대해 부적절한 코드가 만들어지는 것을 막는데도 쓸 수 있다.