มีมาโคร __CLASS__ ใน C ++ หรือไม่


102

มี__CLASS__มาโครใน C ++ หรือไม่ที่ให้ชื่อคลาสคล้ายกับ__FUNCTION__มาโครที่ให้ชื่อฟังก์ชัน

คำตอบ:


68

สิ่งที่ใกล้เคียงที่สุดคือการโทรtypeid(your_class).name()- แต่สิ่งนี้สร้างชื่อที่แตกต่างกันของคอมไพเลอร์

เพื่อใช้ในชั้นเรียนเพียง typeid(*this).name()


2
typeid (* this) .name () สามารถใช้ในฟังก์ชันคลาส
Aleksei Potov

2
มันดีกว่า. สำหรับการรู้จักคลาสการกำหนดอาร์เรย์ char ฟังดูดีกว่าการเลื่อนออกไปจนถึงรันไทม์
Michael Krelin - แฮ็กเกอร์

5
น่าเสียดายที่มันไม่ได้กำหนดไว้เช่น __ CLASS __ มันสามารถใช้งานได้ในขั้นตอนก่อนตัวประมวลผล! :(
k3a

2
@ แม็กซ์ไม่ได้ แต่ทำได้ วิธีที่คล้ายกันที่มันรู้เกี่ยวกับฟังก์ชัน :-P
k3a

5
@kexik: ตัวประมวลผลล่วงหน้าไม่ทราบเกี่ยวกับฟังก์ชันเช่นกันมาตรฐาน__func__และไม่เป็นมาตรฐาน__FUNCTION__ไม่ใช่มาโคร เอกสารของ Microsoft __FUNCTION__เป็นมาโคร แต่ของแถมที่ไม่ได้เป็นจริงก็คือมันไม่ได้ถูกขยายโดยตัวประมวลผลล่วงหน้าเมื่อคุณรวบรวม/Pไฟล์.
Steve Jessop

77

ปัญหาในการใช้typeid(*this).name()คือไม่มีthisตัวชี้ในการเรียกเมธอดแบบคงที่ แมโคร__PRETTY_FUNCTION__รายงานชื่อคลาสในฟังก์ชันคงที่เช่นเดียวกับการเรียกใช้เมธอด อย่างไรก็ตามสิ่งนี้ใช้ได้กับ gcc เท่านั้น

นี่คือตัวอย่างของการดึงข้อมูลผ่านอินเทอร์เฟซสไตล์มาโคร

inline std::string methodName(const std::string& prettyFunction)
{
    size_t colons = prettyFunction.find("::");
    size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1;
    size_t end = prettyFunction.rfind("(") - begin;

    return prettyFunction.substr(begin,end) + "()";
}

#define __METHOD_NAME__ methodName(__PRETTY_FUNCTION__)

มาโคร__METHOD_NAME__จะส่งคืนสตริงของฟอร์ม<class>::<method>()ตัดทอนประเภทการส่งคืนตัวปรับแต่งและอาร์กิวเมนต์จากสิ่งที่__PRETTY_FUNCTION__ให้คุณ

สำหรับบางสิ่งที่แยกเฉพาะชื่อคลาสต้องใช้ความระมัดระวังเพื่อดักจับสถานการณ์ที่ไม่มีคลาส:

inline std::string className(const std::string& prettyFunction)
{
    size_t colons = prettyFunction.find("::");
    if (colons == std::string::npos)
        return "::";
    size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1;
    size_t end = colons - begin;

    return prettyFunction.substr(begin,end);
}

#define __CLASS_NAME__ className(__PRETTY_FUNCTION__)

5
คุณไม่ควรล้อมรอบนี้ด้วย#ifdef __GNU_C__หรือ?
einpoklum

1
แทนที่จะsubstr(0,colons).rfind(" ")ใช้rfind(' ', colons)เพื่อสำรองการสร้างสตริงพิเศษ
mariusm

1
ฉันอยากจะใช้ find_last_of ("::") ไม่เช่นนั้นฟังก์ชันจะส่งคืนเนมสเปซก็ต่อเมื่อมีอย่างใดอย่างหนึ่ง
ต่ำ

ฉันเขียน__METHOD_NAME__มาโครเวอร์ชันขอบเขตที่กว้างขึ้น ตรวจสอบที่นี่
Antonio

ใน C ++ 11 คุณสามารถลองทำให้constexprฟังก์ชันนี้ประเมินได้ในเวลาคอมไพล์
Andre Holzner

11

ฉันขอแนะนำboost :: typeindexซึ่งฉันได้เรียนรู้จาก "Effective Modern C ++" ของ Scott Meyer นี่เป็นตัวอย่างพื้นฐาน:

ตัวอย่าง

#include <boost/type_index.hpp>

class foo_bar
{
    int whatever;
};

namespace bti =  boost::typeindex;

template <typename T>
void from_type(T t)
{
    std::cout << "\tT = " << bti::type_id_with_cvr<T>().pretty_name() << "\n";
}

int main()
{
    std::cout << "If you want to print a template type, that's easy.\n";
    from_type(1.0);
    std::cout << "To get it from an object instance, just use decltype:\n";
    foo_bar fb;
    std::cout << "\tfb's type is : "
              << bti::type_id_with_cvr<decltype(fb)>().pretty_name() << "\n";
}

คอมไพล์ด้วย "g ++ --std = c ++ 14" สิ่งนี้ก่อให้เกิดสิ่งต่อไปนี้

เอาต์พุต

หากคุณต้องการพิมพ์ประเภทแม่แบบนั้นง่ายมาก

T = สองเท่า

ในการรับมันจากอินสแตนซ์วัตถุเพียงใช้ปฏิเสธประเภท:

ประเภทของ fb คือ: foo_bar


เป็นไปได้ไหมที่จะได้ชื่อคลาสโดยไม่มีเนมสเปซด้วยสิ่งนี้? aka coliru.stacked-crooked.com/a/cf1b1a865bb7ecc7
tower120


7

ฉันคิดว่าการใช้__PRETTY_FUNCTION__นั้นดีพอแม้ว่าจะมีเนมสเปซด้วยเช่นกันเช่นnamespace::classname::functionnameจนกว่า__CLASS__จะพร้อมใช้งาน


4

หากคอมไพเลอร์ของคุณเกิดขึ้นg++และคุณกำลังขอ__CLASS__เนื่องจากคุณต้องการวิธีรับชื่อเมธอดปัจจุบันรวมถึงคลาส__PRETTY_FUNCTION__ควรช่วย (ตามinfo gccส่วน5.43 ชื่อฟังก์ชันเป็นสตริง )


3

หากคุณต้องการบางสิ่งที่จะสร้างชื่อคลาสในเวลาคอมไพล์คุณสามารถใช้ C ++ 11 เพื่อทำสิ่งนี้:

#define __CLASS__ std::remove_reference<decltype(classMacroImpl(this))>::type

template<class T> T& classMacroImpl(const T* t);

ฉันรู้ว่านี่ไม่ใช่สิ่งเดียวกับ__FUNCTION__แต่ฉันพบโพสต์นี้ในขณะที่หาคำตอบเช่นนี้ : ง



2

คุณจะได้รับชื่อฟังก์ชันรวมถึงชื่อคลาส สิ่งนี้สามารถประมวลผล funcitons ประเภท C

static std::string methodName(const std::string& prettyFunction)
{
    size_t begin,end;
    end = prettyFunction.find("(");
    begin = prettyFunction.substr(0,end).rfind(" ") + 1;
    end -= begin;
    return prettyFunction.substr(begin,end) + "()";
}

1

วิธีแก้ปัญหาของฉัน:

std::string getClassName(const char* fullFuncName)
{
    std::string fullFuncNameStr(fullFuncName);
    size_t pos = fullFuncNameStr.find_last_of("::");
    if (pos == std::string::npos)
    {
        return "";
    }
    return fullFuncNameStr.substr(0, pos-1);
}

#define __CLASS__ getClassName(__FUNCTION__)

ฉันทำงานกับ Visual C ++ 12


1

นี่คือวิธีแก้ปัญหาที่ใช้__FUNCTION__เทมเพลตมาโครและ C ++:

template <class T>
class ClassName
{
public:
  static std::string Get()
  {
    // Get function name, which is "ClassName<class T>::Get"
    // The template parameter 'T' is the class name we're looking for
    std::string name = __FUNCTION__;
    // Remove "ClassName<class " ("<class " is 7 characters long)
    size_t pos = name.find_first_of('<');
    if (pos != std::string::npos)
      name = name.substr(pos + 7);
    // Remove ">::Get"
    pos = name.find_last_of('>');
    if (pos != std::string::npos)
      name = name.substr(0, pos);
    return name;
  }
};

template <class T>
std::string GetClassName(const T* _this = NULL)
{
  return ClassName<T>::Get();
}

นี่คือตัวอย่างวิธีใช้สำหรับคลาสคนตัดไม้

template <class T>
class Logger
{
public:
  void Log(int value)
  {
    std::cout << GetClassName<T>()  << ": " << value << std::endl;
    std::cout << GetClassName(this) << ": " << value << std::endl;
  }
};

class Example : protected Logger<Example>
{
public:
  void Run()
  {
    Log(0);
  }
}

ผลลัพธ์ของExample::Runจะเป็น

Example: 0
Logger<Example>: 0

โปรดทราบว่าสิ่งนี้จะไม่คำนึงถึงความหลากหลายหากคุณมีตัวชี้ไปที่ฐาน (ซึ่งอาจจะใช้ได้)
การแข่งขัน Lightness ในวงโคจร

0

วิธีนี้ใช้ได้ผลดีทีเดียวหากคุณยินดีจ่ายค่าตัวชี้

class State 
{
public:
    State( const char* const stateName ) :mStateName( stateName ) {};
    const char* const GetName( void ) { return mStateName; }
private:
    const char * const mStateName;
};

class ClientStateConnected
    : public State
{
public:
    ClientStateConnected( void ) : State( __FUNCTION__ ) {};
};

0

ทำงานร่วมกับ msvc และ gcc ด้วย

#ifdef _MSC_VER
#define __class_func__ __FUNCTION__
#endif

#ifdef __GNUG__
#include <cxxabi.h>
#include <execinfo.h>
char *class_func(const char *c, const char *f)
{
    int status;
    static char buff[100];
    char *demangled = abi::__cxa_demangle(c, NULL, NULL, &status);
    snprintf(buff, sizeof(buff), "%s::%s", demangled, f);
    free(demangled);
    return buff;
}
#define __class_func__ class_func(typeid(*this).name(), __func__)
#endif

0

โซลูชันทั้งหมดที่โพสต์ข้างต้นซึ่งขึ้นอยู่กับ__PRETTY_FUNCTION__do มีขอบเฉพาะกรณีที่ไม่ส่งคืนชื่อคลาส / ชื่อคลาสเท่านั้น ตัวอย่างเช่นพิจารณาค่าฟังก์ชันที่สวยงามต่อไปนี้:

static std::string PrettyFunctionHelper::Test::testMacro(std::string)

การใช้การเกิดขึ้นครั้งสุดท้ายของ"::"as ตัวคั่นจะไม่ทำงานเนื่องจากพารามิเตอร์ฟังก์ชันประกอบด้วย a "::"( std::string) คุณสามารถค้นหาขอบเคสที่คล้ายกันสำหรับ"("ตัวคั่นและอื่น ๆ ทางออกเดียวที่ฉันพบใช้ทั้งมาโคร__FUNCTION__และ__PRETTY_FUNCTION__มาโครเป็นพารามิเตอร์ นี่คือรหัสเต็ม:

namespace PrettyFunctionHelper{
    static constexpr const auto UNKNOWN_CLASS_NAME="UnknownClassName";
    /**
     * @param prettyFunction as obtained by the macro __PRETTY_FUNCTION__
     * @return a string containing the class name at the end, optionally prefixed by the namespace(s).
     * Example return values: "MyNamespace1::MyNamespace2::MyClassName","MyNamespace1::MyClassName" "MyClassName"
     */
    static std::string namespaceAndClassName(const std::string& function,const std::string& prettyFunction){
        //AndroidLogger(ANDROID_LOG_DEBUG,"NoT")<<prettyFunction;
        // Here I assume that the 'function name' does not appear multiple times. The opposite is highly unlikely
        const size_t len1=prettyFunction.find(function);
        if(len1 == std::string::npos)return UNKNOWN_CLASS_NAME;
        // The substring of len-2 contains the function return type and the "namespaceAndClass" area
        const std::string returnTypeAndNamespaceAndClassName=prettyFunction.substr(0,len1-2);
        // find the last empty space in the substring. The values until the first empty space are the function return type
        // for example "void ","std::optional<std::string> ", "static std::string "
        // See how the 3rd example return type also contains a " ".
        // However, it is guaranteed that the area NamespaceAndClassName does not contain an empty space
        const size_t begin1 = returnTypeAndNamespaceAndClassName.rfind(" ");
        if(begin1 == std::string::npos)return UNKNOWN_CLASS_NAME;
        const std::string namespaceAndClassName=returnTypeAndNamespaceAndClassName.substr(begin1+1);
        return namespaceAndClassName;
    }
    /**
     * @param namespaceAndClassName value obtained by namespaceAndClassName()
     * @return the class name only (without namespace prefix if existing)
     */
    static std::string className(const std::string& namespaceAndClassName){
        const size_t end=namespaceAndClassName.rfind("::");
        if(end!=std::string::npos){
            return namespaceAndClassName.substr(end+2);
        }
        return namespaceAndClassName;
    }
    class Test{
    public:
        static std::string testMacro(std::string exampleParam=""){
            const auto namespaceAndClassName=PrettyFunctionHelper::namespaceAndClassName(__FUNCTION__,__PRETTY_FUNCTION__);
            //AndroidLogger(ANDROID_LOG_DEBUG,"NoT2")<<namespaceAndClassName;
            assert(namespaceAndClassName.compare("PrettyFunctionHelper::Test") == 0);
            const auto className=PrettyFunctionHelper::className(namespaceAndClassName);
            //AndroidLogger(ANDROID_LOG_DEBUG,"NoT2")<<className;
            assert(className.compare("Test") == 0);
            return "";
        }
    };
}
#ifndef __CLASS_NAME__
#define __CLASS_NAME__ PrettyFunctionHelper::namespaceAndClassName(__FUNCTION__,__PRETTY_FUNCTION__)
#endif

0

ฉันสร้างฟังก์ชันโดยใช้__PRETTY_FUNCTION__และconstexprด้วยconstexpr std::string_viewวิธีC ++ 17 ฉันยังอัปเดตอัลกอริทึมอีกเล็กน้อยเพื่อให้น่าเชื่อถือยิ่งขึ้น(ขอบคุณ@n. 'pronouns' mสำหรับความช่วยเหลือของคุณใน64387023 )

constexpr std::string_view method_name(const char* s)
{
    std::string_view prettyFunction(s);
    size_t bracket = prettyFunction.rfind("(");
    size_t space = prettyFunction.rfind(" ", bracket) + 1;
    return prettyFunction.substr(space, bracket-space);
}
#define __METHOD_NAME__ method_name(__PRETTY_FUNCTION__)

ใน C ++ 20 เราสามารถประกาศฟังก์ชันว่าconstevalบังคับให้ประเมินตามเวลาคอมไพล์ นอกจากนี้ยังมีสำหรับใช้เป็นแม่แบบพารามิเตอร์std::basic_fixed_string


-2

วิธีการต่อไปนี้ (ตาม methodName () ด้านบน) ยังสามารถจัดการอินพุตเช่น "int main (int argc, char ** argv)":

string getMethodName(const string& prettyFunction)
{
    size_t end = prettyFunction.find("(") - 1;
    size_t begin = prettyFunction.substr(0, end).rfind(" ") + 1;

    return prettyFunction.substr(begin, end - begin + 1) + "()";
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.