3.4는 typelist에 대한 길이 계산에 대해서 설명하는 항목인데, 3.3의 다음으로 보기엔 좀 난해하다. 길이에 대한 설명은 접근성 다음이 되야 좋지 않을까 한다. 그러므로 접근성에 대해서 먼저 정리한 뒤, 길이에 대해서 정리하겠다.

아직 손쉬운 접근방법이 없으므로, 손쉬운 접근방법에 대해서 생각해보자. int 타입을 갖는 값이 나와라. 라고 사용법을 정하면 다음과 같이 정의 할 수 있을 것이다.

참으로 간단하면서 사용하기 편하다. 하지만 첫번째 int의 값인지 세번째 int의 값인지 알 길이 없다. 그러므로 이 방법은 사용 할 수 없다.

다시 원점으로 돌아와서 생각을 해보니, 숫자로 지정하면 모호해질 일이 없을 것이라는 생각이 든다.(실제로 숫자로 하는게 좋다! 까지 생각이 도달하는데 많은 시간이 필요하다. 어찌할 방도가 없기 때문에 고정적인 자릿수가 번뜩! 밖에 없겠군! 까지 생각하는데 나는 한참 생각했다. ) 다음 예제코드를 봐보자.

이제 사용법에 따른 모호성 문제는 해결을 했다. 이제 실제 구현을 생각해야 한다. get<0>( d3 ) 처럼, 함수를 정의함에 있어서  리턴 타입과, 첫번째 int에 맞는 값을 리턴해주는 기술이 들어가야 한다. 이제부터 이 기술들에 대해서 정리해보자.


1. 인덱스에 맞는 타입을 반환하는 기술
음! 막상 이런 기술을 생각하라고 하면 참 많이 고민하게 된다. "어떻게 개발" 하는지 모르기 때문인데, 나 같은 경우에는 다음의 알고리즘으로 개발을 했다.
첫째, 제일 쉬운형태를 먼저 만든다.
둘째, 첫째보다 한단계 어려운 형태를 만든다.
셋째, 둘째를 반복하면서 하드 코딩 한다.
넷째, 규칙성을 파악하고 중복되는 개념이나 코드가 있는지 찾는다.
다섯째, 첫째부터 넷째까지 얻어온 정보를 토대로 코드를 처음부터 다시작성 한다.
여섯째, 첫째부터 다섯째를 반복하며, 만족할 만한 수준까지 끌어 올린다.
나는 이 알고리즘에 이런 이름을 붙였다. "맛난거 부터 먹어 보는 알고리즘" ...


자 이제 개발 알고리즘도 있으니, "인덱스에 맞는 타입을 반환하는 기술"을 개발해 보자.
일단 제일 쉬운 형태는 0번 인덱스의 타입을 반환하게 만드는 것일 것이다. 아참, 타입도 리턴할 수 있어? 라고 의구심이 들 수 있겠다. 먼저 이러한 문제를 개발하기 전에 "메타함수" 라는 개념을 이해하고 있어야 한다. ... 그러므로 메타함수란 개념을 안다는 전제하에 계속 진행하겠다.

타입을 반환할 수 있도록 typelist를 수정한다. typelist 는 두개의 타입을 받으므로, 두개의 타입에 대해서 리턴할 수 있게 정의해 두어야 한다. 첫번째 타입을 Head, 두번째 타입을 Tail 이라고 정의해 두자. 또한 NullType이 마지막인 경우도 Tail 이 NullType 이라고 별명을 붙이면 된다. 다음 코드를 봐보자.

이것으로 타입을 typelist<T0, T1>::Head 처럼 리턴 할 수 있게 되었다.

이제 0번 인덱스를 리턴하게 하려면 typelist<T0, T1> 타입을 받으면서 typelist<T0, T1>::Head를 typedef ..... type; 이라고 정의만 해주면 된다. 일단 이 메타함수는 인덱스와 typelist<T0,T1>이라는 타입을 받아야 하므로, 두개의 템플릿 파라미터가 필요하다.(경우엔 따라선 더 많은 템플릿 파라미터가 필요하나, 나는 두개로 했다.)

미리 정의한 이유는 특수화를 하기 위해선 기초가 되는 클래스 템플릿군이 필요하기 때문이다. 자 이제 0번 인덱스일 때만 우선 만들어 보겠으므로, 클래스 템플릿 부분특수화를 이용하여 0번 인덱스 일 때만 typelist<T0, T1>::Head를 리턴하게 하자.

이제 한번 0번 인덱스의 타입을 빼볼까!?

오오! 잘 작동한다. 1번일 때를 이제 생각해 보자. 0번 인덱스를 그대로 사용 한다면, gettype<0, typelist3::Tail>::type 이 바로 전체의 1번 인덱스의 타입일 것이다. 그러고 보니 2번일 때도, tyelist1 ~ 6의 Tail::Tail의 Head 이다! 오!! 재귀적 호출이라고라고라!!!

그러므로
tyelist4의 1번의 경우는 gettype<0, T>에서 T를 typelist4::Tail 로 넣어 주면 되고,
tyelist4의 2번의 경우는 gettype<0, T>에서 T를 typelist4::Tail::Tail 로 넣어 주면 되고,
tyelist4의 3번의 경우는 gettype<0, T>에서 T를 typelist4::Tail::Tail::Tail 로 넣어 주면 되고,

아무리 내가 경험적은 프로그래머라 할지라도 이건 재귀적인데? 라고 말 할 수 있겠다. 하하!

결국 이렇게 된다는 건데
gettype<1, typelist4>::type == gettype<0, typelist4::Tail>::type
gettype<2, typelist4>::type == gettype<0, typelist4::Tail::Tail>::type
gettype<3, typelist4>::type == gettype<0, typelist4::Tail::Tail::Tail>::type

이렇게 보니까 index 숫자에 따라 Tail의 갯수가 정해 진다. 결국 index에 맞게 ::Tail을 물고 늘어지는 방법이 필요하다.

index에 맞게 ::Tail을 물고 늘어지는 방법
우선 하드 코딩으로 일단 자리수가 T0, T1, T3, T4 갯수만큼 만들어 보자.

하지만 우리는 프로그래머가 아닌가? 규칙성을 파악해보면, index 가 1 이상일 때, 현재의 index를 -1 한 index와 T::Tail이 붙는 다는 것을 알 수 있다. 그러므로 코드는 다음과 같다.

우와! 어떻게 이런 생각을 했지! 이게 다 "맛난거 부터 먹어보는 알고리즘" 때문이라고 생각한다. 이제 전체 코드를 봐보자.

갑자기 머리속에 나는 "프로그래머의 길도 갈수 있겠군" 이라는 뇌리가 스쳤다.(고작 이거 가지고!? ㅋ). 이제 인덱스에 따라 타입을 얻어 낼 수 있으니, get<index>(T) 함수의 리턴 타입을 알 수가 있다!


그 인덱스 멤버에 접근 하는 기술
그 멤버의 타입이 무엇인지는 알겠으나, 그 변수까지는 아직 접근을 못한다.  다시 "맛난거 부터 먹어 보는 알고리즘"을 이용하여 하나씩 해보자. 처음 맛난건 아무래도 0번 인덱스의 변수에 접근하는 것이다. 리턴 타입은 gettype<index, T>::type으로 정의하자.

음 잘 짯네! 이제 컴파일 해보자. .. 이런! 잊어먹고 있었다! 함수 템플릿은 부분 특수화가 되지 않는 것이다!! 결국 나는 부분 특수화를 써야만 하기에, 클래스 템플릿을 써야만 한다. 곰곰히 생각해 보면, 클래스 템플릿은 부분 특화만 하고, 그 부분특화의 템플릿 파라미터로 동일한 이름을 함수를 만들면, ... 함수도 특화시킬 수 있다는 것이 뇌리에 스친다. 그러므로 다음처럼 만들 것이다.

자 이제 0번 인덱스의 변수를 빼보자.

잘된다. 이제 인덱스에 맞게 하나씩 빼보자. 이제 재귀에대해서 어느정도 익숙해 졌으니 .. 이건 알아서 생각해 보도록 하고, 다음 코드를 추가하면 된다.

이제 완성형 코드를 봐보자!!

휴~ 이제 잘 되는 것을 확인 할 수 있다. 여기에 const 일 때도 처리 해야 하는데, 이건 일단 나중으로 미룬다.


이제 3.4의 본래 이야기를 할 차례이다.
어떻게 하면 typelist의 길이를 알 수 있을까?
현재 typelist에서 주어진 정보를 토대로 생각해 본다면,
1. typelist의 시작타입이 무엇인지는 모르지만 끝 타입은 알고 있다.
2. ...

더 적을려고 했으나, 이것 밖에 없다. 그러므로 이것으로 길이를 알아야 한다. 끝 타입은 NullType 인것이 확실하므로, 다음과 같은 의사코드를 만들 수 있을 것같다.

의사코드 흐름
1. typelist의 끝 타입이 NullType 이라면 0을 리턴한다.
2. typelist의 끝 타입이 typelist 일 경우, 1을 리턴하며, Tail 타입의 길이를 더해준다.
3. 2번을 NullType 이 올 때까지 반복한다.

그러므로 코드는 다음과 같은 것이다.


자 코드를 다 만들었으니, 이제 전체 코드로 테스트 한번 해보자.

휴 이것으로 길이 계산을 끝내고, 3.5 중간 첨언 : 왜 재귀로만 구현하는가? 에 대해서 이야기 하겠다.

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기