17. 메타 프로그래밍

Post on 21-Jan-2016

42 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

17. 메타 프로그래밍. - PowerPoint PPT Presentation

TRANSCRIPT

17. 메타 프로그래밍

''Metaprogramming is the writing of computer programs that write or manipulate other programs (or themselves) as their data, or that do part of the work at compile time that would otherwise be done at runtime. In many cases, this al-lows programmers to get more done in the same amount of time as they would take to write all the code manually, or it gives programs greater flexibility to efficiently handle new sit-uations without recompilation."

메타 프로그래밍 이란 ?: 프로그램을 프로그래밍 하는 것 .

왜 쓰나 ?: 적은 노력으로 더 많은 기능을 구현하기 위해 . 성능상의 이득도 얻는다 .

그리고 왠지 자신의 코드가 예술로 승화되는 것 같은 착각 .

첫 번째 예제 코드 : 3^N

template < int N >class Pow3{public:

enum { result = 3 * Pow3<N-1>::result };};template <>class Pow3<0>{public:

enum { result = 1 };};

재귀를 끝내기 위한전체 특수화

Pow3<7>::result

3 * Pow3<6>::result

3 * Pow3<5>::result

3 * Pow3<4>::result

3 * Pow3<3>::result

3 * Pow3<2>::result

3 * Pow3<1>::result

3 * Pow3<0>::result

1

3*3*3*3*3*3*3*1

enum ? const ?

struct TrueConstant{

enum { Three = 3 };static int const Four = 4;

};

Three, Four 둘다 상수다 .C++ 표준화를 거치며 정적 상수 초기화 개념이 도입되었다 .

template < int N >class Pow3{public:

enum { result = 3 * Pow3<N-1>::result };};

template < int N >class Pow3{Public:

static int const result = 3 * Pow3<N-1>::result;};

lvalue 가 아님 , 주소를 갖지 않으며 참조로 전달되어도 정적 메모리를 잡아먹지 않음 .

lvalue 실질적인 메모리 할당이 이루어져야 하며 이것은 런타임에 발생함 .

두번째 예제 : 제곱근

template < int N, int LO = 1, int HI = N >class Sqrt{public:

enum { mid = (LO + HI + 1) / 2 };enum { result = (N < mid*mid) ? Sqrt<N, LO, mid – 1>::result

: Sqrt<N, mid, HI>::result };};

template < int N, int M >class Sqrt<N, M, M>{public:

enum { result = M };};

N < mid * mid

yes no

LO = 1 mid HI

LO mid - 1 mid HI

HI LO

Sqrt<16>::result mid = (1 + 16 + 1) / 2 = 9

16 < 9*9

Sqrt<16, 1, 8>::result Sqrt<16, 9, 16>::result

mid = (1 + 8 + 1) / 2 = 5 mid = (9 + 16 + 1) / 2 = 13

Sqrt<16, 1, 4>::result Sqrt<16, 5, 8>::result

16 < 5*5

Sqrt<16, 9, 12>::result Sqrt<16, 13, 16>::result

16 < 13*13

…….…

양쪽의 케이스 모두를 인스턴스화 .‘ :: ’ 연산자 사용 접근으로 인해 클래스형 내의 모든 멤버도 인스턴스화 .

삼항 연산자 대신 특수화를 이용 하자 .

template < bool C, typename T1, typename T2 >class IfThenElse;

template < typename T1, typename T2 >class IfThenElse<true, T1, T2>{public:

typedef T1 Result;};

template < typename T1, typename T2 >class IfThenElse<false, T1, T2>{public:

typedef T2 Result;};

template < int N, int LO = 1, int HI = N >class Sqrt{public:

enum { mid = (LO + HI + 1) / 2 };typedef typename IfThenElse<(N < mid*mid), Sqrt<N, LO, mid-1>,

Sqrt<N, mid, HI> >::Result SubT;enum { result = SubT::result };

};

template < int N, int M >class Sqrt<N, M, M>{public:

enum { result = M };};

N < mid*mid 에 대한 값 (true, false) 의 방향에 대해서만 인스턴스화가 진행된다 .

유도 변수

for ( int i = 1 ; i*i < N ; ++i ){

// do something.}

전형적인 C/C++ 에서의 제곱근 추적 루프

template < int N, int I = 1 >class Sqrt{public:

enum { result = (I * I < N) ? Sqrt<N, I + 1>::result : I };};

template < int N >class Sqrt<N, N>{public:

enum { result = N };};

I 를 유도변수라고 한다 .Induction variable

Sqrt<4>::result

(1 * 1 < 4) ? Sqrt<4, 2>::result : 1

(2 * 2 < 4) ? Sqrt<4, 3>::result : 2

(3 * 3 < 4) ? Sqrt<4, 4>::result : 3

4

(1*1 < 4) ? ( (2*2 < 4) ? ( (3*3 < 4) ? 3 ) : 2 ) : 1

답은 2 번의 단계만에 도달하지만 인스턴스화는 최종적으로 부분 특수화에 도달하기 전까지 계속된다 .

IfThenElse 템플릿 도입

template < int N >struct Value{public:

enum { result = N };};

template < int N, int I = 1 >class Sqrt{public:

typedef typename IfThenElse \<(I*I < N), Sqrt<N, I+1>, Value<I> >::ResultT SubT;

enum { result = SubT::result };};

Sqrt<16>::result

IfThenElse<1*1 < 16, Sqrt<16, 2>, Value<1> >

IfThenElse<2*2 < 16, Sqrt<16, 3>, Value<2> >

IfThenElse<3*3 < 16, Sqrt<16, 4>, Value<3> >

IfThenElse<4*4 < 16, Sqrt<16, 5>, Value<4> >

Sqrt<16>::result = 4

계산 완벽성

- 인스턴스화 횟수에 제한이 없다면 계산 가능한 모든 것을 메타프로그래밍을 통해 처리할 수 있을 것이다 .

- 하지만 , 현실적으로 컴파일러의 자원은 한정되어 있고 인스턴스의 재귀 횟수 또한 제한된다 .

- 또한 , 템플릿의 인스턴스화는 상당한 컴파일러 자원을 잡아먹는다 .

- 템플릿은 만능이 아니다 .

재귀적 인스턴스화 , 재귀적 템플릿 인자

template < typename T, typename U >struct Doublify {};

template < int N >struct Trouble{

typedef Doublify<typename Trouble<N-1>::LongType,typename Trouble<N-1>::LongType> LogType;

};

template <>struct Trouble<0>{

typedef double LongType;};

Trouble<10>::LongType ouch;

Doublify<Trouble<9>::LongType, Trouble<9>::LongType>

Doublify<Trouble<8>::LongType, Trouble<8>::LongType>

Doublify<Trouble<7>::LongType, Trouble<7>::LongType>

………………………..

Doublify<Trouble<0>::LongType, Trouble<0>::LongType>

dou-ble

Trouble<N>::LongType 의 복잡도는 N 의 지수승으로 증가한다 .재귀적 인스턴스화에 템플릿 인자는 재귀적인 형태를 피하는게 좋다 .

재귀적 템플릿 인자를 포함하는 재귀적 인스턴스화는 포함하지 않는 경우의 재귀적 인스턴스화 보다 컴파일러에 가해지는 부하가 더 심하다 .

루프를 풀기 위한 메타프로그래밍

template < typename T >inline T dot_product (int dim, T* a, T* b){

T result = T();for(int i = 0; i < dim; ++i){

result += a[i] * b[i];}return result;

}

내적을 계산하는 템플릿 코드

int a[3] = { 1, 2, 3 };int b[3] = { 5, 6, 7 };int dot = dot_product(3, a, b);

int a[3] = { 1, 2, 3 };int b[3] = { 5, 6, 7 };int dot = dot_product(3, a, b);

for(int i = 0 ; i < 3 ; ++i){

result += a[i] * b[i];}

루프 보다는a[0] * b[0] + a[1] * b[1] + a[2] * b[2]

같은 직접적인 계산이 더 빠르다 .

- 지역 스택의 생성 / 소거- int i- 대입연산 , 임시 객체의 생성

….

template < int DIM, typename T >class DotProduct{public:

static T result (T* a, T* b){

return (*a) * (*b) + DotProduct<DIM – 1, T>::result (a+1, b+1);}

};

template < typename T >class DotProduct<1, T>{public:

static T result (T* a, T* b){

return (*a) * (*b);}

};

template < int DIM, typename T >inline T dot_product (T* a, T* b){

return DotProduct<DIM,T>::result(a,b);}

int a[3] = { 1, 2, 3 };int b[3] = { 3, 5, 7 };int dot = dot_product<3>(a, b);

static int result (int* a, int* b){

return (*a) * (*b) + DotProduct<3 – 1, T>::result (a+1, b+1);}

return (*a) * (*b) + DotProduct<2 – 1, T>::result (a+1, b+1);

a[0]

a[1]

b[0]

b[1]

return (*a) * (*b)a[2] b[2]

return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]

• 실질적으로 루프를 줄이는 것만으로는 성능 향상을 기대하기 힘들다 .Blitz++, MTL, POOMA 같은 라이브러리들은 이런 식의 메타프로그래밍 뿐 아니라 다양한 요소들을 고려하여 만들어진다 .

template <int p, int i>class is_prime{public:enum { prim = (p == 2) || (p%i) && is_prime<(i>2 ? p : 0), i-1>::prim };};

template<>class is_prime<0,0>{public:enum { prim = 1 };};

template<>class is_prime<0,1>{public:enum { prim = 1 };};

template < int i >class D{public:D(void*);};

template < int i >class Prime_print{public:Prime_print<i-1> a;enum { prim = is_prime<i, i-1>::prim };

void f(){D<i> d = prim ? 1 : 0;a.f();}};

template<>class Prime_print<1>{public:enum { prim = 0 };

void f(){D<1> d = prim ? 1 : 0;}};

#define LAST 18

int main(){Prime_print<LAST> a;a.f();}

어윈 운러의 소수 계산 템플릿

감사합니다

top related