이전에 템플릿 특화(template specialization)와 특화를 이용한 튜플(Tuple)클래스를 만들어 본적이 있다. 오늘은 비슷한 원리를 이용해 N개의 키를 가질 수 있는 멀티 키 맵을 만들어 보겠다.
멀티키 맵이라고 해서 특별이 다를 것은 없다. 다만 단일 키만을 제공하는 std::map을 좀 더 확장하여 N개의 키를 타입 리스트를 통해 넘겨주는 것 뿐이다. 물론 이런 방법 말고도 구조체를 키로 쓰는 std::map을 활용하거나, 바이너리 메모리로 모든 키들을 복사해서 쓰는등 구현에는 다향한 취향과 트레이드 오프가 존재한다. 오늘 이 포스트에서 소개하는 것은 그 방법들 중 한 가지 방법일 뿐이다.
멀티키 맵에 관련된 설명에 앞서 이전에 다뤘던 Typelist라던지 템플릿 특화의 개념은 이미 이전 포스트에서 익혔다고 가정하고 간단하게 설명하거나 다루지 않고 넘어 가도록 하겠다.
1. 기본 개념은 '상속' - 상속을 통한 코드의 자동 생성
이전의 튜플(Tuple)클래스와 마찬가지로 멀티키 맵도 기본 뼈대는 상속을 통해 만들어 진다. 멀티키 맵의 기본 구조를 보자면 각 키 마다 하위 계층 맵을가지고 있고, 가장 하위 키는 따로 맵을 가지지 않고 바로 값을 가지고 있는 형태다. 한 마디로 계층 구조를 가지는 std::map들을 템플릿을 이용해 자동으로 생성하는 방식이다.
위 코드 라인 2번을 보면 여러 개의 키중 한개 키만을 떼어 자식 클래스 내부 std::map의 키 값으로 쓰고 나머지 타입 리스트는 부모 클래스로 올려 버린다. Value는 최상위 클래스의 값으로 저장되어야 하므로 모든 계층구조를 통해 끝까지 올려 보낸다.
위와 같은 상속을 통해 각 키에 해당하는 std::map을 자동으로 생성할 수 있다(이렇게 하지 않았다면 직접 각 계층 구조 마다 std::map을 생성 해 줘야 하는 불편함이 있었을 것이다.)
코드 라인 5번은 멀티키 맵 내부에 값을 저장 할 std::map을 생성한다. 위 코드를 보면 어떻게 계층 구조를 이루는 맵을 생성하는지 알 수 있다.
2. 멀티 키 맵에서의 데이터 접근
데이터의 접근은 위의 개념보다 좀 더 쉽다. 단순히 std::map의 계층 구조를 따라 다 맨 마지막 실제 값에 다다르면 값을 리턴하는 방식이다.
3. 멀티 키 맵의 사용
멀티 키 맵의 템플릿 인자를 다양화 하기 위해서 Modern C++ Design에서 소개 되고 있는 타입리스트(type list) 를 이용한다. 그리고 타입 리스트에 따라 실제 인자도 다양화하기 위해 PARAMLIST 를 사용하고 있다. 타입리스트와 파라메터 리스트의 개념은 특화를 이용한 튜플(Tuple)클래스 를 참고하면 되겠다.
나머지 상세 코드는 첨부 파일을 참조 바란다.
참조 :
- Modern C++ Design - 안드레 알렉산드레쿠스
- [진리는어디에] - 템플릿 특화를 이용한 튜플 클래스
- [진리는어디에] - 템플릿 특화(Template specialize)
멀티키 맵이라고 해서 특별이 다를 것은 없다. 다만 단일 키만을 제공하는 std::map을 좀 더 확장하여 N개의 키를 타입 리스트를 통해 넘겨주는 것 뿐이다. 물론 이런 방법 말고도 구조체를 키로 쓰는 std::map을 활용하거나, 바이너리 메모리로 모든 키들을 복사해서 쓰는등 구현에는 다향한 취향과 트레이드 오프가 존재한다. 오늘 이 포스트에서 소개하는 것은 그 방법들 중 한 가지 방법일 뿐이다.
멀티키 맵에 관련된 설명에 앞서 이전에 다뤘던 Typelist라던지 템플릿 특화의 개념은 이미 이전 포스트에서 익혔다고 가정하고 간단하게 설명하거나 다루지 않고 넘어 가도록 하겠다.
1. 기본 개념은 '상속' - 상속을 통한 코드의 자동 생성
이전의 튜플(Tuple)클래스와 마찬가지로 멀티키 맵도 기본 뼈대는 상속을 통해 만들어 진다. 멀티키 맵의 기본 구조를 보자면 각 키 마다 하위 계층 맵을가지고 있고, 가장 하위 키는 따로 맵을 가지지 않고 바로 값을 가지고 있는 형태다. 한 마디로 계층 구조를 가지는 std::map들을 템플릿을 이용해 자동으로 생성하는 방식이다.
1 : template <class T, class U, class Value>
2 : struct MultiKeyMap<Typelist<T, U>, Value> : private MultiKeyMap<U, Value>
3 : {
4 : typedef Typelist<T, U> TYPELIST;
5 : typedef std::map<T, MultiKeyMap<U, Value> > CONTAINER;
6 : CONTAINER m_mapContainer;
... .....
2 : struct MultiKeyMap<Typelist<T, U>, Value> : private MultiKeyMap<U, Value>
3 : {
4 : typedef Typelist<T, U> TYPELIST;
5 : typedef std::map<T, MultiKeyMap<U, Value> > CONTAINER;
6 : CONTAINER m_mapContainer;
... .....
위 코드 라인 2번을 보면 여러 개의 키중 한개 키만을 떼어 자식 클래스 내부 std::map의 키 값으로 쓰고 나머지 타입 리스트는 부모 클래스로 올려 버린다. Value는 최상위 클래스의 값으로 저장되어야 하므로 모든 계층구조를 통해 끝까지 올려 보낸다.
위와 같은 상속을 통해 각 키에 해당하는 std::map을 자동으로 생성할 수 있다(이렇게 하지 않았다면 직접 각 계층 구조 마다 std::map을 생성 해 줘야 하는 불편함이 있었을 것이다.)
코드 라인 5번은 멀티키 맵 내부에 값을 저장 할 std::map을 생성한다. 위 코드를 보면 어떻게 계층 구조를 이루는 맵을 생성하는지 알 수 있다.
2. 멀티 키 맵에서의 데이터 접근
데이터의 접근은 위의 개념보다 좀 더 쉽다. 단순히 std::map의 계층 구조를 따라 다 맨 마지막 실제 값에 다다르면 값을 리턴하는 방식이다.
template <class Value>
struct MultiKeyMap<NullType, Value>
{
Value m_value;
Value& get(NullType key)
{
return m_value;
}
};
template <class T, class U, class Value>
struct MultiKeyMap<Typelist<T, U>, Value> : private MultiKeyMap<U, Value>
{
....
template <class PList>
Value& get(PList key)
{
MultiKeyMap<U, Value>& mkm = m_mapContainer[key.first];
return mkm.get(key.second);
}
....
};
struct MultiKeyMap<NullType, Value>
{
Value m_value;
Value& get(NullType key)
{
return m_value;
}
};
template <class T, class U, class Value>
struct MultiKeyMap<Typelist<T, U>, Value> : private MultiKeyMap<U, Value>
{
....
template <class PList>
Value& get(PList key)
{
MultiKeyMap<U, Value>& mkm = m_mapContainer[key.first];
return mkm.get(key.second);
}
....
};
3. 멀티 키 맵의 사용
int main() {
MultiKeyMap<TYPELIST_3(std::string, std::string, int), int> multiKeyMap;
multiKeyMap[PARAMLIST_3("key1", "key2", 0)] = 1000;;
std::cout << multiKeyMap[PARAMLIST_3("key1", "key2", 0)] << std::endl;
multiKeyMap.erase(PARAMLIST_3("key1", "key2", 0));
return 0;
}
MultiKeyMap<TYPELIST_3(std::string, std::string, int), int> multiKeyMap;
multiKeyMap[PARAMLIST_3("key1", "key2", 0)] = 1000;;
std::cout << multiKeyMap[PARAMLIST_3("key1", "key2", 0)] << std::endl;
multiKeyMap.erase(PARAMLIST_3("key1", "key2", 0));
return 0;
}
멀티 키 맵의 템플릿 인자를 다양화 하기 위해서 Modern C++ Design에서 소개 되고 있는 타입리스트(type list) 를 이용한다. 그리고 타입 리스트에 따라 실제 인자도 다양화하기 위해 PARAMLIST 를 사용하고 있다. 타입리스트와 파라메터 리스트의 개념은 특화를 이용한 튜플(Tuple)클래스 를 참고하면 되겠다.
나머지 상세 코드는 첨부 파일을 참조 바란다.
- Modern C++ Design - 안드레 알렉산드레쿠스
- [진리는어디에] - 템플릿 특화를 이용한 튜플 클래스
- [진리는어디에] - 템플릿 특화(Template specialize)