Programing

C ++ 템플릿에서 유형의 이름 가져 오기

crosscheck 2020. 11. 10. 07:51
반응형

C ++ 템플릿에서 유형의 이름 가져 오기


일부 텍스트 데이터 파일을 구문 분석하기위한 템플릿 클래스를 작성 중입니다. 따라서 대부분의 구문 분석 오류는 대부분 프로그래머가 작성하지 않은 데이터 파일의 오류로 인해 발생합니다. 앱이로드에 실패한 이유에 대한 좋은 메시지, 예 :

example.txt를 구문 분석하는 중에 오류가 발생했습니다. [MySectiom] Key의 값 ( "notaninteger")이 유효한 정수가 아닙니다.

클래스의 템플릿 함수 및 멤버 변수에 전달 된 인수에서 파일, 섹션 및 키 이름을 알아낼 수 있지만 템플릿 함수가 변환하려는 형식의 이름을 얻는 방법을 모르겠습니다.

내 현재 코드는 일반 문자열 등에 대한 전문화와 같습니다.

template<typename T> T GetValue(const std::wstring &section, const std::wstring &key)
{
    std::map<std::wstring, std::wstring>::iterator it = map[section].find(key);
    if(it == map[section].end())
        throw ItemDoesNotExist(file, section, key)
    else
    {
        try{return boost::lexical_cast<T>(it->second);}
        //needs to get the name from T somehow
        catch(...)throw ParseError(file, section, key, it->second, TypeName(T));
    }
}

Id는 데이터 파일이 사용할 수있는 모든 유형에 대해 특정 오버로드를 만들 필요가 없습니다.

또한 예외가 발생하지 않는 한 런타임 오버 헤드가 발생하지 않는 솔루션이 필요합니다. 즉,이 코드가 수없이 많이 호출되고로드 시간이 이미 다소 길어지고 있기 때문에 완전한 컴파일 타임 솔루션이 제가 원하는 것입니다.

편집 : 좋아 이것은 내가 생각해 낸 해결책입니다.

나는 types.h가 다음을 포함합니다

#pragma once
template<typename T> const wchar_t *GetTypeName();

#define DEFINE_TYPE_NAME(type, name) \
    template<>const wchar_t *GetTypeName<type>(){return name;}

그런 다음 DEFINE_TYPE_NAME 매크로를 사용하여 처리해야하는 각 유형에 대한 cpp 파일 (예 : 시작할 유형을 정의한 cpp 파일)에서 사용할 수 있습니다.

그러면 링커는 어딘가에 정의되어있는 한 적절한 템플릿 전문화를 찾거나 그렇지 않으면 링커 오류를 발생시켜 유형을 추가 할 수 있습니다.


Jesse Beder의 솔루션이 가장 좋을 수 있지만 typeid가 제공하는 이름이 마음에 들지 않으면 (예를 들어 gcc가 망가진 이름을 제공한다고 생각합니다) 다음과 같이 할 수 있습니다.

template<typename T>
struct TypeParseTraits;

#define REGISTER_PARSE_TYPE(X) template <> struct TypeParseTraits<X> \
    { static const char* name; } ; const char* TypeParseTraits<X>::name = #X


REGISTER_PARSE_TYPE(int);
REGISTER_PARSE_TYPE(double);
REGISTER_PARSE_TYPE(FooClass);
// etc...

그리고 다음과 같이 사용하십시오.

throw ParseError(TypeParseTraits<T>::name);

편집하다:

두 가지를 결합하여 name기본적으로 호출하는 함수로 변경 typeid(T).name()한 다음 허용되지 않는 경우에만 전문화 할 수 있습니다.


해결책은

typeid(T).name()

std :: type_info 반환합니다 .


typeid(T).name() 구현 정의이며 사람이 읽을 수있는 문자열을 보장하지 않습니다.

cppreference.com 읽기 :

유형의 이름을 포함하는 구현 정의 널 종료 문자열을 리턴합니다. 특히 반환 된 문자열은 여러 유형에 대해 동일 할 수 있으며 동일한 프로그램의 호출간에 변경 될 수 있다는 보장은 없습니다.

...

gcc 및 clang과 같은 컴파일러를 사용하면 반환 된 문자열을 c ++ filt -t를 통해 파이프하여 사람이 읽을 수있는 형식으로 변환 할 수 있습니다.

그러나 어떤 경우에는 gcc가 올바른 문자열을 반환하지 않습니다. 내 컴퓨터에 예를 들어 나는 GCC 파크가 -std=c++11내부 템플릿 함수의 typeid(T).name()반환 "j"를 들어 "unsigned int". 그것은 소위 망가진 이름입니다. 실제 유형 이름을 얻으려면 abi :: __ cxa_demangle () 함수 (gcc 전용)를 사용하십시오.

#include <string>
#include <cstdlib>
#include <cxxabi.h>

template<typename T>
std::string type_name()
{
    int status;
    std::string tname = typeid(T).name();
    char *demangled_name = abi::__cxa_demangle(tname.c_str(), NULL, NULL, &status);
    if(status == 0) {
        tname = demangled_name;
        std::free(demangled_name);
    }   
    return tname;
}

Bunkar에서 언급했듯이 typeid (T) .name은 구현 정의입니다.

이 문제를 방지하려면 Boost.TypeIndex 라이브러리를 사용할 수 있습니다 .

예를 들면 :

boost::typeindex::type_id<T>().pretty_name() // human readable

Logan Capaldo의 대답은 정확하지만 매번 클래스를 전문화 할 필요가 없기 때문에 약간 단순화 할 수 있습니다. 다음과 같이 쓸 수 있습니다.

// in header
template<typename T>
struct TypeParseTraits
{ static const char* name; };

// in c-file
#define REGISTER_PARSE_TYPE(X) \
    template <> const char* TypeParseTraits<X>::name = #X

REGISTER_PARSE_TYPE(int);
REGISTER_PARSE_TYPE(double);
REGISTER_PARSE_TYPE(FooClass);
// etc...

이렇게하면 REGISTER_PARSE_TYPE 명령어를 C ++ 파일에 넣을 수도 있습니다.


As a rephrasing of Andrey's answer:

The Boost TypeIndex library can be used to print names of types.

Inside a template, this might read as follows

#include <boost/type_index.hpp>
#include <iostream>

template<typename T>
void printNameOfType() {
    std::cout << "Type of T: " 
              << boost::typeindex::type_id<T>().pretty_name() 
              << std::endl;
}

I just leave it there. If someone will still need it, then you can use this:

template <class T>
bool isString(T* t) { return false;  } // normal case returns false

template <>
bool isString(char* t) { return true; }  // but for char* or String.c_str() returns true
.
.
.

This will only CHECK type not GET it and only for 1 type or 2.


If you'd like a pretty_name, Logan Capaldo's solution can't deal with complex data structure: REGISTER_PARSE_TYPE(map<int,int>) and typeid(map<int,int>).name() gives me a result of St3mapIiiSt4lessIiESaISt4pairIKiiEEE

There is another interesting answer using unordered_map or map comes from https://en.cppreference.com/w/cpp/types/type_index.

#include <iostream>
#include <unordered_map>
#include <map>
#include <typeindex>
using namespace std;
unordered_map<type_index,string> types_map_;

int main(){
    types_map_[typeid(int)]="int";
    types_map_[typeid(float)]="float";
    types_map_[typeid(map<int,int>)]="map<int,int>";

    map<int,int> mp;
    cout<<types_map_[typeid(map<int,int>)]<<endl;
    cout<<types_map_[typeid(mp)]<<endl;
    return 0;
}

참고URL : https://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template

반응형