سؤال

أعمل حاليًا على بعض أكواد التسجيل التي من المفترض - من بين أشياء أخرى - طباعة معلومات حول وظيفة الاتصال.يجب أن يكون هذا سهلاً نسبيًا، فالمعيار C++ يحتوي على type_info فصل.يحتوي هذا على اسم الفئة/الوظيفة/النوع الذي تم كتابته وما إلى ذلك.لكنها مشوهة.انها ليست مفيدة جدا.أي. typeid(std::vector<int>).name() عائدات St6vectorIiSaIiEE.

هل هناك طريقة لإنتاج شيء مفيد من هذا؟يحب std::vector<int> للمثال أعلاه.إذا كان يعمل فقط مع الفئات غير القالبية، فلا بأس بذلك أيضًا.

يجب أن يعمل الحل مع دول مجلس التعاون الخليجي، ولكن سيكون من الأفضل أن أتمكن من نقله.إنه مخصص للتسجيل، لذا فهو ليس مهمًا جدًا بحيث لا يمكن إيقاف تشغيله، ولكن يجب أن يكون مفيدًا لتصحيح الأخطاء.

هل كانت مفيدة؟

المحلول

نظرًا للاهتمام الذي يتلقاه هذا السؤال/الإجابة، والتعليقات القيمة منه جي ماننيك جي, لقد قمت بتنظيف الكود قليلاً.يتم إعطاء نسختين:أحدهما بميزات 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

تم اختباره باستخدام g++ 4.7.2، وg++ 4.9.0 20140302 (تجريبي)، و clang++ 3.4 (الجذع 184647)، و clang 3.5 (الجذع 202594) على Linux 64 بت و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


(تحديث بتاريخ 8 سبتمبر 2013)

الإجابة المقبولة (اعتبارًا من 7 سبتمبر 2013), ، عند الاتصال بـ abi::__cxa_demangle() ناجح، يقوم بإرجاع مؤشر إلى مصفوفة محلية مخصصة للمكدس...أوتش!
لاحظ أيضًا أنه إذا قمت بتوفير مخزن مؤقت، abi::__cxa_demangle() يفترض أنه سيتم تخصيصه على الكومة.يعد تخصيص المخزن المؤقت على المكدس خطأً (من مستند gnu): "لو output_buffer ليست طويلة بما فيه الكفاية، يتم توسيعها باستخدام realloc." الاتصال realloc() على مؤشر إلى المكدس...أوتش!(أنظر أيضا ايجور سكوتشينسكيتعليقه اللطيف.)

يمكنك بسهولة التحقق من كلا الخطأين:ما عليك سوى تقليل حجم المخزن المؤقت في الإجابة المقبولة (اعتبارًا من 7 سبتمبر 2013) من 1024 إلى شيء أصغر، على سبيل المثال 16، وإعطائه شيئًا يحمل اسمًا لا أطول من 15 (هكذا realloc() يكون لا مُسَمًّى).ومع ذلك، اعتمادًا على نظامك وتحسينات المترجم، سيكون الإخراج كما يلي:القمامة / لا شيء / تعطل البرنامج.
للتحقق من الخطأ الثاني:اضبط حجم المخزن المؤقت على 1 وقم بتسميته بشيء اسمه أطول من حرف واحد.عند تشغيله، يتعطل البرنامج بشكل شبه مؤكد أثناء محاولته الاتصال realloc() مع مؤشر إلى المكدس.


(الإجابة القديمة من 27 كانون الأول (ديسمبر) 2010)

تغييرات مهمة تم إجراؤها على كود 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;
}

نصائح أخرى

والدافع الأساسي يحتوي على demangler. الأساسية / 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، كما اقترح سابقا.

وهذا هو ما نستخدمه. ومن المقرر HAVE_CXA_DEMANGLE فقط إذا كانت متوفرة (الإصدارات الأخيرة من دول مجلس التعاون الخليجي فقط).

#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، الذي يأتي مع بينوتيلس. ويمكن أن demangle أسماء رمز C ++ و Java.

وليس حلا كاملا، ولكنك قد ترغب في النظر في ما تناقلته بعض ومعيار (أو بتأييد واسع) الماكرو تحديد. ومن الشائع في التعليمات البرمجية تسجيل لرؤية استخدام وحدات الماكرو:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

وانها تنفيذ محددة، حتى انها ليست شيئا يحدث لتكون محمولة. في MSVC ++، اسم () هو اسم غير مزخرف، وكان لديك للنظر في raw_name () للحصول على واحدة مزينة.
مجرد طعنة في الظلام هنا، ولكن في ظل دول مجلس التعاون الخليجي، قد ترغب في النظر في demangle.h

وكما أنني وجدت ماكرو تسمى __PRETTY_FUNCTION__، الذي لا حيلة. أنه يعطي اسم وظيفة جميلة (أرقام :)). هذا هو ما احتاجه.

وأي بمعنى. انه يعطيني ما يلي:

virtual bool mutex::do_unlock()

ولكن لا أعتقد أنه يعمل على المجمعين أخرى.

اختلاف طفيف في حل علي.إذا كنت تريد أن يظل الرمز مشابهًا جدًا لـ

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 يبقى كما هو في حل علي

ونلقي نظرة على __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، ولكن أنا متأكد من أن النتيجة من الاسم () دالة عضو هو غير القياسية ولن بالضرورة العودة أي شيء يمكن أن يتم تحويلها إلى نتيجة ذات مغزى.
إذا كنت متمسكة مترجم واحد، هناك ربما وظيفة مترجم محددة من شأنها أن تفعل ما تريد. تحقق من الوثائق.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top