템플릿 메타프로그래밍

위키백과, 우리 모두의 백과사전.
이동: 둘러보기, 검색

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

바깥 고리[편집]