std :: type_info :: name 결과 관리 해제


93

저는 현재 호출 함수에 대한 정보를 인쇄하는 로깅 코드를 작업 중입니다. 이것은 비교적 쉬울 것입니다. 표준 C ++에는 type_info클래스가 있습니다. 여기에는 typeid의 클래스 / 함수 / 등의 이름이 포함됩니다. 그러나 그것은 엉망입니다. 별로 유용하지 않습니다. 즉 typeid(std::vector<int>).name()돌아갑니다 St6vectorIiSaIiEE.

이것에서 유용한 것을 생산하는 방법이 있습니까? 마찬가지로 std::vector<int>위의 예를 들어. 템플릿이 아닌 클래스에서만 작동한다면 괜찮습니다.

솔루션은 gcc에서 작동하지만 포팅 할 수 있다면 더 좋을 것입니다. 로깅 용이므로 끌 수 없을 정도로 중요하지는 않지만 디버깅에 도움이됩니다.

답변:


117

이 질문 / 답변에 대한 관심과 GManNickG 의 귀중한 피드백을 감안할 때 코드를 약간 정리했습니다. 두 가지 버전이 제공됩니다. 하나는 C ++ 11 기능이 있고 다른 하나는 C ++ 98 기능 만 있습니다.

파일 type.hpp

#ifndef TYPE_HPP
#define TYPE_HPP

#include <string>
#include <typeinfo>

std::string demangle(const char* name);

template <class T>
std::string type(const T& t) {

    return demangle(typeid(t).name());
}

#endif

파일 type.cpp (C ++ 11 필요)

#include "type.hpp"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

std::string demangle(const char* name) {

    int status = -4; // some arbitrary value to eliminate the compiler warning

    // enable c++11 by passing the flag -std=c++11 to g++
    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };

    return (status==0) ? res.get() : name ;
}

#else

// does nothing if not g++
std::string demangle(const char* name) {
    return name;
}

#endif

용법:

#include <iostream>
#include "type.hpp"

struct Base { virtual ~Base() {} };

struct Derived : public Base { };

int main() {

    Base* ptr_base = new Derived(); // Please use smart pointers in YOUR code!

    std::cout << "Type of ptr_base: " << type(ptr_base) << std::endl;

    std::cout << "Type of pointee: " << type(*ptr_base) << std::endl;

    delete ptr_base;
}

다음을 인쇄합니다.

ptr_base Base*
유형 : pointee 유형 :Derived

Linux 64 비트에서 g ++ 4.7.2, g ++ 4.9.0 20140302 (시험용), clang ++ 3.4 (트렁크 184647), clang 3.5 (트렁크 202594) 및 g ++ 4.7.2 (Mingw32, Win32 XP SP2)로 테스트되었습니다.

C ++ 11 기능을 사용할 수없는 경우 C ++ 98에서 수행 할 수있는 방법 은 다음과 같습니다. 파일 type.cpp 는 다음과 같습니다.

#include "type.hpp"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

struct handle {
    char* p;
    handle(char* ptr) : p(ptr) { }
    ~handle() { std::free(p); }
};

std::string demangle(const char* name) {

    int status = -4; // some arbitrary value to eliminate the compiler warning

    handle result( abi::__cxa_demangle(name, NULL, NULL, &status) );

    return (status==0) ? result.p : name ;
}

#else

// does nothing if not g++
std::string demangle(const char* name) {
    return name;
}

#endif


(2013 년 9 월 8 일 업데이트)

허용 된 답변 (2013 년 9 월 7 일 현재) ,에 대한 호출 abi::__cxa_demangle()이 성공 하면 로컬 스택 할당 배열에 대한 포인터를 반환합니다 . 아야!
또한 버퍼를 제공 abi::__cxa_demangle()하면 힙에 할당 된 것으로 간주합니다. 스택에 버퍼를 할당하는 것은 버그입니다 (gnu 문서에서) : " output_buffer길이가 충분하지 않으면를 사용하여 확장됩니다 realloc." 스택에 대한 포인터를 호출 realloc()하는 중 ... 아야! ( Igor Skochinsky 의 친절한 의견 도 참조하십시오 .)

단지 예 16, 작은 일 1024에서 SEP ((7), 2013) 허용 대답에 버퍼 크기를 줄이고, 이름으로 뭔가를 줄 : 당신은 쉽게 이러한 버그를 모두 확인할 수 없습니다 때문에 (이상 15 일 realloc()입니다 호출 되지 않음 ). 그래도 시스템과 컴파일러 최적화에 따라 출력은 쓰레기 / 없음 / 프로그램 충돌입니다.
두 번째 버그를 확인하려면 버퍼 크기를 1로 설정하고 이름이 1 자보다 긴 것으로 호출합니다. 실행 realloc()하면 스택에 대한 포인터로 호출 을 시도 할 때 프로그램이 거의 확실하게 충돌 합니다.


(2010 년 12 월 27 일의 이전 답변)

KeithB의 코드에 대한 중요한 변경 사항 : 버퍼는 malloc에 ​​의해 할당되거나 NULL로 지정되어야합니다. 스택에 할당하지 마십시오.

그 상태도 확인하는 것이 좋습니다.

나는 찾지 못했습니다 HAVE_CXA_DEMANGLE. __GNUG__코드가 컴파일된다는 보장은 없지만 확인 합니다. 누구든지 더 좋은 아이디어가 있습니까?

#include <cxxabi.h>

const string demangle(const char* name) {

    int status = -4;

    char* res = abi::__cxa_demangle(name, NULL, NULL, &status);

    const char* const demangled_name = (status==0)?res:name;

    string ret_val(demangled_name);

    free(res);

    return ret_val;
}

여기에는 #include <cxxabi.h>. 그렇지 않으면 잘 작동했습니다. 감사합니다.
jterrace

2
로부터 문서 : output_buffer메모리의 영역의 분해 해제 이름이 저장되는 * 바이트 길이의 malloc에, 할당. output_buffer가 충분히 길지 않으면 realloc을 사용하여 확장됩니다. output_buffer는 대신 NULL 일 수 있습니다. 이 경우 demangled 이름은 malloc으로 할당 된 메모리 영역에 배치됩니다.
Igor Skochinsky 2012-08-29

2
@IgorSkochinsky 예, 이전 댓글에 오타가 있지만 편집 할 수 없습니다. 내가 쓰고 싶었던 것 : "마지막으로 확인 abi::__cxa_demangle했을 때 힙에 할당 될 것으로 예상했습니다 . "문서를 찾아 주셔서 감사합니다!
Ali

1
기술적으로 이것은 ret_val건설 중에 던지면 누출 될 수 있습니다 . 이를 방지하기 위해 스코프 가드를 사용할 수 있습니다.
GManNickG 2013-08-11

3
std::unique_ptr<char, decltype(&std::free)>포인터의 서명 으로 사용하는 것이 더 분명 할 것입니다 .
mindvirus

27

부스트 코어에는 디맨 글러가 포함되어 있습니다. core / demangle.hpp 체크 아웃 :

#include <boost/core/demangle.hpp>
#include <typeinfo>
#include <iostream>

template<class T> struct X
{
};

int main()
{
    char const * name = typeid( X<int> ).name();

    std::cout << name << std::endl; // prints 1XIiE
    std::cout << boost::core::demangle( name ) << std::endl; // prints X<int>
}

기본적으로 abi::__cxa_demangle이전에 제안 된대로를 위한 래퍼입니다 .


1
부스트가 옵션이라면 이것이 최선의 방법입니다!
hbobenicio 2016

13

이것이 우리가 사용하는 것입니다. HAVE_CXA_DEMANGLE은 사용 가능한 경우에만 설정됩니다 (최신 버전의 GCC 만 해당).

#ifdef HAVE_CXA_DEMANGLE
const char* demangle(const char* name)
{
   char buf[1024];
    unsigned int size=1024;
    int status;
    char* res = abi::__cxa_demangle (name,
                                 buf,
                                 &size,
                                 &status);
    return res;
  }
#else
const char* demangle(const char* name)
{
  return name;
}
#endif  

6
을 포함해야합니다 #include <cxxabi.h>.
fuenfundachtzig

흥미 롭군. HAVE_CXA_DEMANGLE이 정의되지 않은 __cxa_demangle이 있습니다
mkb

@Matt 내가 말하고자하는 것은 autoconf를 기반으로하는 우리의 빌드 시스템은 사용 가능한 경우에만 HAVE_CXA_DEMANGLE을 설정한다는 것입니다.
KeithB 2010

19
경고! 위의 코드는 프로그램 충돌을 일으킬 가능성이 있습니다. 버퍼는 malloc에 ​​의해 할당되거나 NULL로 지정되어야합니다. 스택에 할당하지 마십시오. 아래 내 코드를 참조하십시오.
Ali

조심해, res는 NULL을 반환 할 수 있습니다. :)
Zibri

8

여기에서 원하는 것을 수행하는 함수가 포함 된 type_strings.hpp를 살펴보십시오 .

예를 들어 로그 파일에 표시된 내용을 조작하는 데 사용할 수있는 demangling 도구를 찾는 경우 c++filtbinutils와 함께 제공되는를 살펴보십시오 . C ++ 및 Java 기호 이름을 demangle 할 수 있습니다.


참고로 cxa_demange () (링크 된 코드에서 사용)와 cx ++ filt는 모두 gcc에 따라 다릅니다. 이를 수행하는 휴대용 방법은 없습니다.
KeithB

c ++ filt는 그것을 자르지 않습니다. 컴파일 타임에이 물건 (또는 대부분)이 필요합니다. 대부분은 매크로로 수행됩니다.
종착역

4
type_strings.cpp에 대한 링크가 깨진 것 같습니다.
StackedCrooked

1
안녕하세요 @GregoryPakosz 위의 의견에있는 github 링크도 깨진 것 같습니다 :( Cheers
olibre

중요한 FYI : abi::__cxa_demangle()그리고 그것의 ilk <cxxabi.h> 는 GCC에 국한되지 않습니다 . 먼 과거에는 GCC 전용 이었을지 모르지만이 게시물의 작성 시점 <cxxabi.h>에는 확고한 임시 표준이었습니다. 따라서 답변의 코드 링크는 DOI 였지만 Clang이이 경우에 최고 수준의 지원을 제공한다고 보증 할 수 있습니다. qv, Clang의 libcxxabi소스에서 : 각각의 decl, impl, huge test : git.io/vRTBo , git.io/vRTBh , git.io/vRTRf – 테스트 코드의 주석은 Clang 구현이 GCC에 비해 어떻게 든 더 많은 디맨 글링이 가능하다는 것을 나타 냅니다.
fish2000

4

구현이 정의되어 있으므로 이식 할 수있는 것이 아닙니다. MSVC ++에서 name ()은 데코 레이팅되지 않은 이름이며, 데코 레이팅 된 이름을 얻으려면 raw_name ()을 확인해야합니다.
여기 어둠 속에서 찌르고 있지만 gcc 아래에서 demangle.h 를보고 싶을 수도 있습니다.


3

완전한 솔루션은 아니지만 표준 (또는 널리 지원되는) 매크로의 정의가 무엇인지 살펴볼 수 있습니다. 매크로 사용을 확인하는 것은 로깅 코드에서 일반적입니다.

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

4
PRETTY_FUNCTION 은 말할 것도 없습니다 .
CesarB

1
코드에서 현재 위치에 대한 정보를 제공합니다. 질문은 std :: vector와 같은 유형의 예쁜 이름이었습니다.
KeithB

그는 그것이 디버깅을위한 것이라고 언급했고 나는 이것이 완전한 해결책이 아니라고 말했습니다. FUNCDNAME 과 같은 다른 매크로 는 데코 레이팅 된 이름을 반환합니다.
luke

실제로 질문을 다시 읽어 보면 "나는 현재 호출 함수에 대한 정보를 인쇄해야하는 로깅 코드를 작업 중입니다."였습니다. 작동합니다.
Max Lybbert

네임 스페이스를 모르기 때문에 완전하지 않습니다. 이것은 내 코드에서 모두 준비되었습니다. 어쨌든 감사합니다.
종착역

3

나는 또한 __PRETTY_FUNCTION__트릭을 수행하는 라는 매크로를 발견했습니다 . 예쁜 함수 이름을 제공합니다 (그림 :)). 이것이 내가 필요한 것입니다.

즉, 다음을 제공합니다.

virtual bool mutex::do_unlock()

그러나 다른 컴파일러에서는 작동하지 않는다고 생각합니다.


예, PRETTY_FUNCTION 은 gcc에 따라 다릅니다.
Greg Rogers

2

Ali의 솔루션에 약간의 변형이 있습니다. 코드가 여전히 다음과 매우 유사하도록하려면

typeid(bla).name(),

대신 이것을 작성

Typeid(bla).name() (대문자 첫 글자 만 다름)

그런 다음 관심이있을 수 있습니다.

파일 type.hpp

#ifndef TYPE_HPP
#define TYPE_HPP

#include <string>
#include <typeinfo>

std::string demangle(const char* name);

/*
template <class T>
std::string type(const T& t) {

  return demangle(typeid(t).name());
}
*/

class Typeid {
 public:

  template <class T>
    Typeid(const T& t) : typ(typeid(t)) {}

  std::string name() { return demangle(typ.name()); }

 private:
  const std::type_info& typ;
};


#endif

type.cpp 는 Ali의 솔루션과 동일하게 유지됩니다.


1

에서 __cxa_demangle찾을 수 있는 항목을 살펴보십시오 cxxabi.h.


내가받은 메시지에 따라 사용하지 않았습니다.
종착역

그 메시지를 어디서 찾았습니까? 방금 봤는데 지원되는 것 같고 더 이상 사용되지 않는다는 증거는 없습니다.
Ali

:: 네임 스페이스에서 더 이상 사용되지 않을 수 있습니다. abi :: __ cxa_demangle을 사용하면 경고가 표시되지 않습니다. 어떤 gcc를 사용하고 있습니까?
onitake

1
// KeithB's solution is good, but has one serious flaw in that unless buf is static
// it'll get trashed from the stack before it is returned in res - and will point who-knows-where
// Here's that problem fixed, but the code is still non-re-entrant and not thread-safe.
// Anyone care to improve it?

#include <cxxabi.h>

// todo: javadoc this properly
const char* demangle(const char* name)
{
    static char buf[1024];
    size_t size = sizeof(buf);
    int status;
    // todo:
    char* res = abi::__cxa_demangle (name,
                                 buf,
                                 &size,
                                 &status);
    buf[sizeof(buf) - 1] = 0; // I'd hope __cxa_demangle does this when the name is huge, but just in case.
    return res;
  }

11
경고! 버퍼는 malloc에 ​​의해 할당되거나 NULL로 지정되어야합니다. 스택에 할당하지 마십시오. 아래 내 코드를 참조하십시오.
Ali

1

허용 솔루션 [1] 대부분 잘 작동합니다. 나는 내가 기대했던 것을보고하지 않는 적어도 하나의 사례를 발견했다 (그리고 나는 그것을 코너 사례라고 부르지 않을 것이다).

이 경우 하단에 게시 된 다른 해결책을 찾았습니다.

문제가있는 경우 ( type[1]에 정의 된대로 사용 ) :

int i = 1;
cout << "Type of " << "i" << " is " << type(i) << endl;
int & ri = i;
cout << "Type of " << "ri" << " is " << type(ri) << endl;

생산하다

Type of i is int
Type of ri is int

솔루션 (사용 type_name<decltype(obj)>(), 아래 코드 참조) :

cout << "Type of " << "i" << " is " << type_name<decltype(i)>() << endl;
cout << "Type of " << "ri" << " is " << type_name<decltype(ri)>() << endl;

생산하다

Type of i is int
Type of ri is int&

원하는대로 (적어도 나에 의해)

코드 . 특수화 문제로 인해 별도로 컴파일 된 소스가 아닌 포함 된 헤더에 있어야합니다. 예를 들어 템플릿 함수에 대한 정의되지 않은 참조를 참조하십시오 .

#ifndef _MSC_VER
#   include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>

template <class T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::unique_ptr<char, void(*)(void*)> own
           (
#ifndef _MSC_VER
                abi::__cxa_demangle(typeid(TR).name(), nullptr,
                                           nullptr, nullptr),
#else
                nullptr,
#endif
                std::free
           );
    std::string r = own != nullptr ? own.get() : typeid(TR).name();
    if (std::is_const<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}

0

나는 항상 type_info를 사용하고 싶었지만 name () 멤버 함수의 결과가 비표준이고 의미있는 결과로 변환 될 수있는 어떤 것도 반환하지 않을 것이라고 확신합니다.
하나의 컴파일러를 고수하는 경우 원하는 작업을 수행하는 컴파일러 특정 함수가있을 수 있습니다. 문서를 확인하십시오.


0

Ali의 솔루션에 따라 다음 은 내 용도에 가장 적합한 C ++ 11 템플릿 대안입니다.

// type.h
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

template <typename T>
std::string demangle() {
  int status = -4;

  std::unique_ptr<char, void (*)(void*)> res{
      abi::__cxa_demangle(typeid(T).name(), NULL, NULL, &status), std::free};
  return (status == 0) ? res.get() : typeid(T).name();
}

용법:

// main.cpp
#include <iostream>

namespace test {
    struct SomeStruct {};
}

int main()
{
    std::cout << demangle<double>() << std::endl;
    std::cout << demangle<const int&>() << std::endl;
    std::cout << demangle<test::SomeStruct>() << std::endl;

    return 0;
}

다음을 인쇄합니다.

double                                                                        
int                                                                           
test::SomeStruct
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.