템플릿 메타프로그래밍

위키백과, 우리 모두의 백과사전.
둘러보기로 가기 검색하러 가기

템플릿 메타프로그래밍(template metaprogramming)은 템플릿을 사용하는 프로그래밍 기법으로, 컴파일러에게 프로그램 코드를 생성하도록 하는 방식이다. 이러한 기법은 컴파일 시점에 많은 것을 결정하도록 하여, 실행 시점의 계산을 줄여준다. 이 기법은 C++ 프로그래밍 언어에서 주로 사용된다.

예제[편집]

다음의 예는 C++ 언어로 기술하였다.

메타프로그래밍 기법 없이, 계승 함수는 다음과 같이 재귀적으로 구현할 수 있다.

int factorial(int n) {
  if (n == 0)
    return 1;
  return n * factorial(n - 1);
}

// factorial(4) == (4 * 3 * 2 * 1) == 24
// factorial(0) == 0! == 1

이 경우 factorial(4)의 값은 실행 시점에 계산된다.

반면 템플릿 메타프로그래밍과 재귀의 탈출 조건을 제공하는 템플릿 특수화를 사용하면, 프로그램에서 사용되지 않는 펙토리얼들은 구할 것도 없이, 사용되는 펙토리얼의 값만을 컴파일할 때 구할 수 있다.

template <int N>
struct Factorial {
  enum { value = N * Factorial<N - 1>::value };
};

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

// Factorial<4>::value == 24
// Factorial<0>::value == 1

위 두 예제는 기능적 측면에서는 유사해 보이지만, 첫 번째 예제는 프로그램이 실행될 때 계산하고 두 번째 예제는 컴파일할 때 값을 구하여 사용한다. 두 번째 예제와 같이 사용하기 위해서 컴파일러는 컴파일할 때 템플릿의 인자를 정확히 알고 있어야 하므로, Factorial<X>::value가 컴파일 시점에 계산되기 위해서, X는 컴파일 시점에 알 수 있는 상수 값이어야 한다.

템플릿 메타프로그래밍은 알기 힘든 문법 구조를 가지고 있으나 쓸모있는 용도가 있다. 어떤 값 n이 있고 이 값이 컴파일 시간에 전달된다고 하면, n 차원 벡터 클래스를 만들 수 있다. 고전적인 방법으로 n 차원 벡터를 만드는 것보다 반복문이 필요 없고, 상당히 최적화된 코드를 얻을 수 있어 좋다.

아래의 덧셈 연산자 코드를 살펴보자. n 차원 벡터를 추가하기 위해 아래처럼 짜여질 수도 있다.

template<int dimension>
Vector<dimension>& Vector<dimension>::operator+=(const Vector<dimension>& rhs) {
  for (int i = 0; i < dimension; ++i) {
    value[i] += rhs.value[i];
  }
  return *this;
}

컴파일러는 위에서 정의한 템플릿 함수를 초기화할 때, 아래와 같은 코드를 만든다.

template<>
Vector<2>& Vector<2>::operator+=(const Vector<2>& rhs) {
  value[0] += rhs.value[0];
  value[1] += rhs.value[1];
  return *this;
}

컴파일러 최적화 도구가 반복문인 'for'를 제외하는 까닭은, 템플릿 인자인 'dimension'이 컴파일 시간에 전달되는 상수이어서이다. 이 기법의 실제 구현을 보려면 filpcode.com에 올려진 글을 참고하라.

C++에서, 템플릿 메타프로그래밍은 튜링 완전성을 갖고 있는데, 이게 뜻하는 바는 다음과 같다. 컴퓨터는 표현 가능한 어떤 계산도 처리할 수 있는데, 어떤 형태에서는 템플릿 메타프로그램에 의해서도 가능하다는 뜻이다.

템플릿 메타프로그램으로 된 어떠한 형태든지 프로그램된 표현으로 처리하기 때문이다.

이 기법으로 작성된 프로그램은 변수 값을 바꿀 수 없다. 이 의미는 일단 초기화된 값은 바꿀 수 없다는 뜻이다. 이런 결과로, 템플릿 메타프로그래밍은 C++과는 다른 함수형 프로그래밍 형태로 나타난다. 이것은 메타프로그램에서의 순서 제어는 재귀를 통해 이루어지기 때문이다.

장점[편집]

  • 늘어나는 컴파일 시간과 좀 더 빨라지는 실행 시간
    • 템플릿 코드는 모두 컴파일 시점에 처리되므로 컴파일 시간이 오래 걸릴 수 있다. 반면, 실행 시점의 계산 중 일부를 컴파일 시점에 처리한 것이므로 실행 코드는 더 효율적이다. 컴파일 속도는 보통 크게 문제가 되지는 않지만, 대형 프로젝트나 템플릿에 많이 의존하는 프로젝트는 이 점이 중요할 수 있다.
  • 일반화 프로그래밍(Generic Programming)
    • 템플릿 메타프로그래밍은 프로그래머로 하여금 구조에 집중하게 하고 클라이언트 코드가 필요한 구현을 컴파일러로 하여금 하도록 한다. 따라서 템플릿 메타프로그래밍은 진정한 일반화 코드가 된다. 그리고 필요 코드가 최소화되고 유지보수도 쉬워진다.

단점[편집]

  • 가독성
    • 템플릿 메타프로그래밍의 문법과 형태는 일반적인 C++ 프로그래밍에 비해 난해하다. C++ 프로그램 쪽이 더 고급기법을 썼거나, 매우 심오하게 짜여졌다 하더라도 템플릿 메타프로그래밍이 더 난해할 수 있다. 그래서 템플릿 메타프로그래밍으로 짜여진 프로그램을 유지보수해야하는 비숙련 프로그래머는 곤란함을 겪는다.
  • 컴파일러에 의존적로 인한 표준 보다는 낮은 이식성
    • 컴파일러마다 템플릿을 사용하는 방법이 다르다. 따라서 템플릿 메타프로그래밍은 컴파일러에 크게 의존하며 이식성 문제 - 특히 최근의 메타프로그래밍에서 - 를 갖고 있다.

외부 링크[편집]