템플릿 메타프로그래밍(template metaprogramming)은 템플릿을 사용하는 프로그래밍 기법으로, 컴파일러에게 프로그램 코드를 생성하도록 하는 방식이다. 이러한 기법은 컴파일 시점에 많은 것을 결정하도록 하여, 실행 시점의 계산을 줄여준다. 이 기법은 C++ 프로그래밍 언어에서 주로 사용된다1.
TMP는 버그를 찾기 쉽지도 않고 구현도 어렵지만, 사용하는 이유는 많은 C++ 라이브러리들이 TMP 를 이용해서 구현되었고, TMP 를 통해서 컴파일 타임에 여러 오류들을 잡아낼 수도 있으며 속도가 매우 중요한 프로그램의 경우 TMP 를 통해서 런타임 속도도 향상2 시킬 수 있기 때문이다.
TMP에 대한 살펴볼 만한 실제 사례는 C++Builder의 template 버그 문제로 볼랜드포럼의 글 <아래 컴파일 에러는 엠바에서 템플릿코드를 엉터리로 구현해 놨기 때문>3에서 볼 수 있다.
아래의 예제는 콜라츠 추측(Collatz)을 C#과 C++ 언어로 각각 구현한 예제4를 보여주고 TMP를 사용한 C++의 소스가 컴파일 타임에서 C#과 어떠한 차이가 있는지 보여준다.
CS 예제
using System.Linq;
using System.Collections.Generic;
namespace CollatzCS
{
class Program
{
public static IEnumerable<(uint count, uint value)> Collatz(uint seed)
{
uint count = 1;
yield return (count, seed);
while (seed > 1)
{
if (seed % 2 == 0)
seed /= 2;
else
seed = 3 * seed + 1;
yield return (++count, seed);
}
}
static void Main(string[] args)
{
// Collatz : 런타임에 실행
var col = Collatz(100);
foreach (var val in col)
{
System.Console.WriteLine($"{val.count}: {val.value}");
}
System.Console.WriteLine("------------------------");
System.Console.WriteLine($"Collatz of 100 = {col.Count()}");
}
}
}
CPP 예제
#include <iostream>
using namespace std;
template <unsigned n>
struct Factorial
{
static const unsigned value = n * Factorial<n - 1>::value;
};
template <>
struct Factorial<1>
{
static const unsigned value = 1;
};
template <unsigned depth, unsigned seed, bool odd>
struct CollatzBase
{
};
template <unsigned depth, unsigned seed>
struct CollatzBase<depth, seed, true> : public CollatzBase<depth + 1, seed * 3 + 1, (seed * 3 + 1) % 2>
{
};
template <unsigned depth, unsigned seed>
struct CollatzBase<depth, seed, false> : public CollatzBase<depth + 1, seed / 2, (seed / 2) % 2>
{
};
template <unsigned depth>
struct CollatzBase<depth, 1, true> // false <-> true
{
static const unsigned count = depth;
};
template <unsigned seed>
struct Collatz : public CollatzBase<1, seed, seed % 2>
{
};
int main(void)
{
// Factorial 예제
constexpr unsigned fact5 = Factorial<5>::value;
cout << "Factorial 5 = " << fact5 << endl;
cout << "Factorial 10 = " << Factorial<10>::value << endl;
cout << "-------------------------------" << endl;
// Collatz : 컴파일 타임에 실행
cout << "Collatz of 100 = " << Collatz<100>::count << endl;
}