템플릿 메타프로그래밍
위키백과 ― 우리 모두의 백과사전.
템플릿 메타프로그래밍(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
템플릿 메타프로그래밍 기법으로는 이렇게 구현될 수 있다.
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<4>::value 을 컴파일 시간에 처리한다. 그러므로 Factorial<x>::value 에서 x의 값이 상수 혹은 sizeof ()로 얻은 값처럼 컴파일 시간에 전달되어야 처리될 수 있다.
템플릿 메타프로그래밍은 알기 힘든 문법 구조를 가지고 있으나 쓸모있는 용도가 있다. 어떤 값 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++과는 다른 함수형 프로그래밍 형태로 나타난다. 이유인즉 메타프로그램에서의 순서 제어가 위에서 보았듯이 재귀를 통해 이뤄지기 때문이다.
[편집] 템플릿 메타프로그래밍의 장점과 단점
- 컴파일 시간과 실행 시간과의 교환: 모든 템플릿 코드가 컴파일 시간에 처리, 평가, 확장되므로 컴파일 시간은 더 걸린다. 그러나 실행 코드는 더 효율적이다. 오버헤드는 보통 무시될 정도지만, 대형 프로젝트나 템플릿에 많이 의존하는 프로젝트는 이 점이 중요할 수 있다.
- 일반화 프로그래밍: 템플릿 메타프로그래밍은 프로그래머로 하여금 구조에 집중하게 하고 클라이언트 코드가 필요한 구현을 컴파일러로 하여금 하도록 한다. 따라서 템플릿 메타프로그래밍은 진정한 일반화 코드가 된다. 그리고 필요 코드가 최소화되고 유지보수도 쉬워진다.
- 가독성: 템플릿 메타프로그래밍의 문법과 형태는 일반적인 C++ 프로그래밍에 비해 난해하다. C++ 프로그램 쪽이 더 고급기법을 썼거나, 매우 심오하게 짜여졌다 하더라도 템플릿 메타프로그래밍이 더 난해할 수 있다. 그래서 템플릿 메타프로그래밍으로 짜여진 프로그램을 유지보수해야하는 비숙련 프로그래머는 곤란함을 겪는다.
- 이식성: 컴파일러마다 템플릿을 사용하는 방법이 다르다. 따라서 템플릿 메타프로그래밍은 컴파일러에 크게 의존하며 이식성 문제 - 특히 최근의 메타프로그래밍에서 - 를 갖고 있다.