10. STL tuple
C++ Stl 중에 튜플이 있다. 사용하는데 편한데 어떻게 만들어져있는지 궁금할정도로 신기하다
void main()
{
std::tuple<float, int> t;
std::get<0>(t) = 1.2f;
std::get<1>(t) = 1;
printf("%g\r\n", std::get<0>(t));
printf("%i\r\n", std::get<1>(t));
}
출력
1.2
1
이 튜플을 비슷하게 만들어보자
template<int...> struct seq{};
template<int max, int... s>
struct make_seq : make_seq<max - 1, max - 1, s...>
{
};
template<int... s>
struct make_seq<0, s...>
{
typedef seq<s...> type;
};
template<int max>
using MakeSeq = typename make_seq<max>::type;
template<int x, typename Arg>
struct foo_storage
{
Arg data;
};
template<typename Seq, typename... Args>
struct foo_helper{};
template<int s0, int... s, typename A0, typename... Args>
struct foo_helper<seq<s0, s...>, A0, Args...>
: foo_storage<s0, A0>
, foo_helper<seq<s...>, Args...>
{};
template<typename... Args>
struct foo : foo_helper<MakeSeq<sizeof...(Args)>, Args...>
{
};
template<int N, typename T>
T& get(foo_storage<N, T>& f)
{
return f.data;
}
위에는 전체 소스이고 하나씩 살펴보자
template<int...>
struct seq{};
template<int max, int... s>
struct make_seq : make_seq<max - 1, max - 1, s...>
{
};
template<int... s>
struct make_seq<0, s...>
{
typedef seq<s...> type;
};
template<int max>
using MakeSeq = typename make_seq<max>::type;
여기에는 가변길이 템플릿을 사용한다.
make_seq는 어떤 max 값을 주면 그 max 값에 해당하는 int의 seq에 해당하는 type을 정의한다. (내가 적어도 뭔말인지 모르겠다 허헣)

이런 식으로 3을 넣으면 0~2까지의 seq를 정의한다.
따라서

이렇게 결과가 나온다.
template<int...>
struct seq{};
int형의 가변길이 템플릿을 받는 seq를 정의한다.
이 seq는
template<int... s>
struct make_seq<0, s...>
{
typedef seq<s...> type;
};
make_seq의 type을 정의한다.
struct make_seq : make_seq<max - 1, max - 1, s...>
{
};
그리고 make_seq는 상속 받는 make_seq보다 1 작은 make_seq를 상속받는다.
그리고 종료조건으로
template<int... s>
struct make_seq<0, s...>
{
typedef seq<s...> type;
};
0일경우 마지막 베이스 클래스를 만든다.
헷갈린다.

이런 식으로 상속을 받게 된다.
따라서

MakeSeq<2>의 타입은 seq<0,1>이 된다.
template<int x, typename Arg>
struct foo_storage
{
Arg data;
};
여기서 foo_storage를 보면 int형에 대응하는 타입을 갖고 이 타입을 내부에서 타입에 해당하는 멤버변수 데이터를 선언한다. 각 인티저의 시퀀스마다 타입을 갖는다.
template<typename Seq, typename... Args>
struct foo_helper{};
가변길이 템플릿 Args를 갖는다. 그리고 특화버전을 작성한다.
template<int s0, int... s, typename A0, typename... Args>
struct foo_helper<seq<s0, s...>, A0, Args...>
: foo_storage<s0, A0>
, foo_helper<seq<s...>, Args...>
{};
특화버전은 int형의 리스트가 n개 오고, 그리고 타입 리스트가 n개 옵니다. 따라서 int형의 리스트가 n개면 타입도 n개가 와야 한다.
이 작성의 의도는 0,1,2가 연속이 되고, 타입을 float, int, long을 작성 한다면 0을 float 1을 int 2를 long에 대응하도록 구성하려는 의도다.
foo_helper의 특화버전이 seq를 파라미터로 받는다면
struct foo_helper<seq<s0, s...>
이 seq의 s0부터 n-1까지의 시퀀스가 foo_helper의 int형의 seq에 매핑이 되고,
, A0, Args...>
나머지 타입 인자가 타입에 매핑이 된다.
: foo_storage<s0, A0>
그리고 상속받기를 foo_helper의 첫번째 int형의 seq를 foo_storage의 첫번째에 넣고 첫번째 자료형을 첫번째 자료형에 넣는다. 이렇게 하면, 그 seq에 해당하는 데이터를 내부에서 선언하는 base클래스를 갖게 된다.
, foo_helper<seq<s...>, Args...>
{};
그 다음에 int형의 시퀀스를 계속 받기 위해 재귀적으로 상속을 받는다.
이렇게 하면

foo_helper<seq<0,1>,float,int> 를 선언한다면 이런 구조록 만들어진다.
template<typename... Args>
struct foo : foo_helper<MakeSeq<sizeof...(Args)>, Args...>
{
};
여기서 보면 MakeSeq<sizeof...(Args)>, Args...>에서 sizeof...(Args)는 가변길이 템플릿에서 템플릿에서 전달된 타입의 갯수를 구하는 연산자이다. 따라서 Args의 갯수를 의미한다.
위에는 궁극적으로는 MakeSeq에 sizeof에 Args를 전달하여 foo<seq<s0. s...> 부분을 자동적으로 생성하게 된다.
template<int N, typename T>
T& get(foo_storage<N, T>& f)
{
return f.data;
}
마지막으로 위에 그림 구조로 만들어지니까 N과 T를 reference로 넘겨주면 접근 하게 된다.
void main()
{
foo<float, int> t;
get<0>(t) = 1.2f;
get<1>(t) = 2;
printf("%g\r\n", get<0>(t));
printf("%i\r\n", get<1>(t));
}
이제 이것들을 실행시키면

출력이 된다....
마참내 std에서 제공하는 튜플과 비슷하게 작동하게 만들었다.