z 23. 메타프로그래밍 :: C++, 그래픽 기술 블로그

메타 프로그래밍이란 프로그램을 프로그래밍하는 것으로 실제로 원하는 기능을 구현하는 ㅅ내로운 코드를 만들어내는 프로그래밍 시스템을 작성하는 것입니다. 이는 적은 노력으로 더 많은 기능을 구현하여 컴파일 타임에 계산을 하여 성능을 높이거나, 인터페이스를 단순하게 할 수 있습니다.

23.1 현대 C++ 메타프로그래밍의 현 상태

메타 프로그래밍은 C++14에서 도입된 constexpr 함수가 도입되어 상당히 편하게 다양한 생성 방식을 지원하게 되었습니다. 재귀적인 템플릿 인스턴스화를 사용하면 상당히 복잡한 형식 계산까지도 수행할 수 있습니다.

// primary template: in general we yield the given type:
template<typename T>
struct RemoveAllExtentsT {
  using Type = T;
};

// partial specializations for array types (with and without bounds):
template<typename T, std::size_t SZ>
struct RemoveAllExtentsT<T[SZ]> {
  using Type = typename RemoveAllExtentsT<T>::Type;
};
template<typename T>
struct RemoveAllExtentsT<T[]> {
  using Type = typename RemoveAllExtentsT<T>::Type;
};

template<typename T>
using RemoveAllExtents = typename RemoveAllExtentsT<T>::Type;

RemoveAllExtents<int[5][10]> // int

이는 배열 계층이 몇 개가 쌓여있든 간에 제거하여 형식을 도출해낼 수 있습니다.

 값 및 형식 메타프로그래밍을 갖추면 컴파일 과정에 값과 형식을 계산할 수 있는데, 이로 인해 컴파일 과정에 실행 시간 효과를 내는 코드 조각을 프로그래밍을 통해 조합해낼 수 있는데 이를 혼합 메타 프로그래밍이라고 합니다. 혼합 계산은 다양한 다위형 값들의 결과를 계산할 수 있는 라이브러리가 존재하는데, 값은 실행 시간에 계산되지만 결과 단위형은 컴파일 과정에 계산해야 합니다.

template<unsigned N, unsigned D = 1>
struct Ratio {
  static constexpr unsigned num = N;  // numerator
  static constexpr unsigned den = D;  // denominator
  using Type = Ratio<num, den>;
};

// implementation of adding two ratios:
template<typename R1, typename R2>
struct RatioAddImpl
{
 private:
  static constexpr unsigned den = R1::den * R2::den;
  static constexpr unsigned num = R1::num * R2::den + R2::num * R1::den;
 public:
  typedef Ratio<num, den> Type;
};

// using declaration for convenient usage:
template<typename R1, typename R2>
using RatioAdd = typename RatioAddImpl<R1, R2>::Type;

다음을 통해 컴파일 과정에 두 비율의 합을 계산할 수 있습니다.

23.2 반영 메타프로그래밍의 중요성

이제까지는 constexpr 평가와 재귀적 템플릿 인스턴스화에 기반을 둔 형식 메타프로그래밍을 사용해 값 메타프로그래밍을 구현해보았습니다. 이는 현대 C++에서 가능한 것으로 다양한 메소드를 활용하였습니다. 이 이전에는 재귀 템플릿 인스턴스화를 통해서 구현하였습니다. C++를 위한 메타프로그래밍 해결책이라면 계산, 반영, 생성, 이 세 가지 차원에 대해 선택을 해야 합니다. 반영은 프로그래밍 방식으로 조사할 수 있는 능력을 의미하며, 생성은 프로그램을 위해 추가 코드를 생성하는 능력을 말합니다. 계산은 재귀 인스턴스화와 constexpr 평가를 통해 구현 가능합니다. 반영은 형식 특질을 통해 해결할 수 있습니다. 생성은 템플릿을 인스턴스화를 통해 이루어지고 있습니다.

23.3 재귀 인스턴스화의 비용

템플릿 인스턴스화는 비용이 싸지 않습니다. 상대적으로 비용이 적게 드는 클래스 템플릿이라도 인스턴스 만들 때마다 1킬로바이트가 넘어가며, 이 공간은 재활용될 수 없습니다. 게다가 재귀 도중 삼항 연산자에서는 연산자를 통해 접근하기에 모든 클래스 형 내의 멤버를 인스턴스화 시킵니다.

23.4 계산 완전성

템플릿 인스턴스화에는 상당한 컴파일러 자원이 들어가기에 재귀 인스턴스화를 지나치게 사용하면 컴파일러가 느려지거나 가용한 자원을 모두 소모할 수도 있습니다. C++ 표준에서는 적어도 1024개 수준의 재귀 인스턴스화를 권고하며, 대부분의 템플릿 메타 프로그래밍 작업에서는 충분합니다. 그러나 이는 꼭 필요한 자리에만 사용되야합니다.

23.5 재귀 인스턴스화와 재귀 템플릿 인자

재귀적 인스턴스화를 할때 템플릿 인자는 재귀적으로 중첩되지 않게 하는 쪽이 좋습니다.

'C++공부 > C++ Templates' 카테고리의 다른 글

25. 튜플  (0) 2022.07.07
24. 형식 목록  (0) 2022.07.06
22. 정적과 동적 다형성 사이 잇기  (0) 2022.07.06
21. 템플릿과 상속  (0) 2022.07.06
20. 형식 속성 오버로딩  (0) 2022.07.05

+ Recent posts