std::type_info::name 결과 풀기
-
07-07-2019 - |
문제
저는 현재 무엇보다도 호출 함수에 대한 정보를 인쇄하는 일부 로깅 코드를 작업 중입니다.이는 상대적으로 쉬울 것입니다. 표준 C++에는 type_info
수업.여기에는 유형 ID가 지정된 클래스/함수/등의 이름이 포함됩니다.하지만 망가졌어.별로 유용하지 않습니다.즉. typeid(std::vector<int>).name()
보고 St6vectorIiSaIiEE
.
이것으로부터 유용한 것을 생산할 수 있는 방법이 있나요?좋다 std::vector<int>
위의 예에서는.템플릿이 아닌 클래스에서만 작동한다면 그것도 괜찮습니다.
솔루션은 gcc에서 작동해야 하지만 이식할 수 있다면 더 좋을 것입니다.로깅을 위한 것이므로 끌 수 없을 만큼 중요하지는 않지만 디버깅에는 도움이 될 것입니다.
해결책
이 질문/답변이 주목을 받고 있으며, 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*
포인트 유형: Derived
Linux 64비트 및 g++ 4.7.2(Mingw32, Win32 XP SP2)에서 g++ 4.7.2, g++ 4.9.0 20140302(실험), clang++ 3.4(트렁크 184647), clang 3.5(트렁크 202594)로 테스트되었습니다.
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()
스택에 대한 포인터에서...아야!(또한보십시오 이고르 스코친스키친절한 댓글입니다.)
다음 두 가지 버그를 모두 쉽게 확인할 수 있습니다.허용된 답변(2013년 9월 7일 기준)의 버퍼 크기를 1024에서 더 작은 크기(예: 16)로 줄이고 이름을 지정하세요. ~ 아니다 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;
}
다른 팁
Boost Core에는 Demangler가 포함되어 있습니다. 점검 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
, 이전에 제안 된 바와 같이.
이것이 우리가 사용하는 것입니다. HAS_CXA_DEMANGL은 사용 가능한 경우에만 설정됩니다 (최근 버전의 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
여기서 살펴보십시오 type_strings.hpp 원하는 것을 수행하는 함수가 포함되어 있습니다.
로그 파일에 표시된 물건을 맹글하는 데 사용할 수있는 Demangling 도구를 찾으십시오. c++filt
, Binutils와 함께 제공됩니다. C ++ 및 Java 기호 이름을 demangle 할 수 있습니다.
완전한 해결책은 아니지만 표준 (또는 널리 지원되는) 매크로의 정의를보고 싶을 수도 있습니다. 로깅 코드에서는 매크로의 사용을 보는 것이 일반적입니다.
__FUNCTION__
__FILE__
__LINE__
e.g.:
log(__FILE__, __LINE__, __FUNCTION__, mymessage);
구현이 정의되어 있으므로 휴대용이 아닙니다. MSVC ++에서는 이름 ()이 미색 이름이며, 장식 된 이름을 얻으려면 raw_name ()을 살펴 봐야합니다.
여기 어둠 속에서 찌르지 만 GCC 아래에서보고 싶을 수도 있습니다. demangle.h
나는 또한 매크로라고 불렀다 __PRETTY_FUNCTION__
, 그것은 트릭을합니다. 그것은 예쁜 기능 이름을 제공합니다 (그림 :)). 이것이 내가 필요한 것입니다.
즉, 그것은 다음을 제공합니다.
virtual bool mutex::do_unlock()
그러나 나는 그것이 다른 컴파일러에서 작동한다고 생각하지 않습니다.
Ali의 솔루션에 약간의 변화가 있습니다. 코드가 여전히 매우 유사하기를 원한다면
typeid(bla).name()
,
대신 이것을 씁니다
Typeid(bla).name()
(자본 첫 번째 편지에서만 다름)
그러면 이것에 관심이있을 수 있습니다.
파일로 타입 .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의 솔루션과 동일하게 유지됩니다
보세요 __cxa_demangle
당신이 찾을 수있는 것 cxxabi.h
.
// 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;
}
그만큼 허용 된 솔루션 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;
}
나는 항상 type_info를 사용하고 싶었지만 () 멤버 함수의 결과는 비표준이 아니며 반드시 의미있는 결과로 변환 할 수있는 것을 반드시 반환 할 필요는 없습니다.
하나의 컴파일러를 고수하는 경우 원하는 작업을 수행하는 컴파일러 별 기능이있을 수 있습니다. 문서를 확인하십시오.