[C++] 템플릿을 이용해서 읽기 쉬운 타입 이름을 얻어보자

Posted by 적분 ∫2tdt=t²+c
2019.09.29 19:10 프로그래밍/테크닉

C++에는 런타임 타입 정보(RTTI)를 알려주는 typeid 시스템이 들어가 있습니다. 따라서 컴파일 언어이면서도 생각보다는 유연하게 실행 시간에 특정 변수의 타입을 조회하고 타입 간의 연산을 수행할 수가 있습니다. typeid로 얻어지는 type_info 인스턴스는 name()이라는 멤버 함수를 가지는데, 이 멤버 함수는 해당 타입의 이름을 알려줍니다. 이 값을 통해 현재 변수의 타입이 무엇인지 유저에게 문자열로 출력해줄 수가 있는 것이죠.

문제는 이 name()이 사실 사람이 읽기에 적합한 문자열을 주지 않는다는 것입니다. (게다가 컴파일 환경에 따라 출력값이 달라질 수도 있구요.) 예로 gcc 8.3에서 typeid(std::vector<size_t>).name()는 St6vectorImSaImEE라는 값을 반환합니다. 보시다시피 mangling이 되어서 알아보기도 어렵고 표준화가 되어 있지 않아 어떤 값이 나올지를 예측하기 어렵습니다.

이를 해결하기 위해 gcc에서만 지원하는 demangle함수(https://stackoverflow.com/questions/281818/unmangling-the-result-of-stdtype-infoname)를 사용할 수도 있고, boost::typeindex의 pretty_name이라는 함수(https://stackoverflow.com/questions/33386672/boostcoredemangle-on-visual-studio-typeid-name)를 쓸 수도 있습니다. 

타입 이름을 프로그래머가 컨트롤할 수 있는 범위 내에서 컴파일 타임에 얻고 싶다면, 위의 두 옵션을 사용하지 않고 직접 타입의 이름을 구하는 템플릿 함수를 작성할 수도 있습니다. 구현해야할게 조금 많지만, 컴파일러를 타지 않으며 프로그래머가 통제할 수 있는 방법이라고 할 수 있죠. 서론은 그만 끊고 바로 코드로 들어가보도록 하겠습니다.


우리는 위와 같은 readable_name이라는 템플릿 함수를 구현해볼 겁니다. 템플릿 인자로 타입을 넣으면 그 타입의 이름을 std::string으로 되돌려 주는 함수죠. 먼저 가장 쉬운 기본 타입들을 구현해봅시다.


readable_name_impl이라는 함수를 오버로딩하고 있는것을 주목해보세요. 파라미터로는 해당 타입의 포인터를 받고 있습니다. 이렇게 한 이유는 해당 타입의 null 포인터를 하나 만들어서 파라미터로 넘기면 컴파일러가 알아서 타입이 맞는 함수를 선택하여 호출해주기 때문입니다. 포인터가 아니라 그냥 타입 그 자체를 파라미터로 받아도 되겠지만, 그 경우 생성자가 없거나 금지된 타입의 경우 문제가 발생할 수 있죠. 이제 기본 타입을 조합하여 복합 타입들의 이름을 생성하는 함수를 구현해봅시다.


엄청 간단합니다. 컨테이너의 파라미터를 템플릿 파라미터로 가지는 readable_name_impl 함수들을 구현했습니다. 템플릿 내부의 타입들은 다시 readable_name을 호출함으로써 재귀적으로 타입명을 생성합니다. 이로써 std::map<std::vector<size_t>, std::pair<size_t, double>>과 같이 복잡한 타입들도 컴파일러가 알아서 위 함수들을 재귀적으로 호출해서 해결할 수 있게 됩니다!


그리고 최종적으로 readable_name 함수의 구현은 위와 같습니다. 그냥 nullptr를 _Type*로 캐스팅해서 readable_name_impl에 던져준게 전부입니다. 그러면 이제 다음과 같은게 가능해지는것이죠.



템플릿 가지고 놀기 참 쉽죠~?

Tags
이 댓글을 비밀 댓글로