Có macro __CLASS__ trong C ++ không?


100

__CLASS__macro nào trong C ++ cung cấp tên lớp tương tự như __FUNCTION__macro cung cấp tên hàm không

Câu trả lời:


67

Điều gần nhất là gọi typeid(your_class).name()- nhưng điều này tạo ra tên mangled cụ thể của trình biên dịch.

Để sử dụng nó trong lớp chỉ typeid(*this).name()


2
typeid (* this) .name () có thể được sử dụng bên trong các chức năng lớp
Aleksei Potov

2
Cái đó tốt hơn. Đối với việc biết lớp, việc xác định mảng char nghe có vẻ tốt hơn là hoãn nó cho đến thời gian chạy.
Michael Krelin - hacker

5
Thật tiếc vì nó không được định nghĩa như __ CLASS __, nó có thể hữu ích ở giai đoạn tiền xử lý! :(
k3a

2
@Max Nó không nhưng có thể. Cách tương tự nó biết về chức năng :-P
k3a

5
@kexik: bộ tiền xử lý cũng không biết về các chức năng, chuẩn __func__và không chuẩn __FUNCTION__không phải là macro. Tài liệu của Microsoft __FUNCTION__dưới dạng macro, nhưng phần quà không thực sự là nó không được mở rộng bởi bộ tiền xử lý khi bạn biên dịch /P.
Steve Jessop

77

Vấn đề với việc sử dụng typeid(*this).name()là không có thiscon trỏ trong một cuộc gọi phương thức tĩnh. Macro __PRETTY_FUNCTION__báo cáo tên lớp trong các hàm tĩnh cũng như các cuộc gọi phương thức. Tuy nhiên, điều này sẽ chỉ hoạt động với gcc.

Đây là một ví dụ về trích xuất thông tin thông qua giao diện kiểu macro.

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__)

Macro __METHOD_NAME__sẽ trả về một chuỗi của biểu mẫu <class>::<method>(), cắt bớt kiểu trả về, các công cụ sửa đổi và đối số từ những gì __PRETTY_FUNCTION__cung cấp cho bạn.

Đối với một cái gì đó chỉ trích xuất tên lớp, cần phải cẩn thận một số để bẫy các tình huống không có lớp:

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
Bạn không nên bao quanh điều này với #ifdef __GNU_C__?
einpoklum

1
thay vì substr(0,colons).rfind(" ")một người có thể sử dụng rfind(' ', colons)để tạo thêm một chuỗi.
mariusm

1
Tôi muốn sử dụng find_last_of ( "::") Nếu không hàm sẽ chỉ trả lại một không gian tên nếu có một
underdoeg

Tôi đã viết một phiên bản có thể rộng hơn của __METHOD_NAME__macro. Kiểm tra tại đây .
Antonio

Trong C ++ 11, bạn có thể thử biến đây thành một constexprhàm để đánh giá nó tại thời điểm biên dịch
Andre Holzner

11

Tôi muốn đề xuất boost :: typeindex , mà tôi đã học được từ "C ++ hiện đại hiệu quả" của Scott Meyer Đây là một ví dụ cơ bản:

Thí dụ

#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";
}

Được biên dịch với "g ++ --std = c ++ 14", điều này tạo ra như sau

Đầu ra

Nếu bạn muốn in một loại mẫu, điều đó thật dễ dàng.

T = gấp đôi

Để lấy nó từ một cá thể đối tượng, chỉ cần sử dụng kiểu khai báo:

loại của fb là: foo_bar


Có thể chỉ lấy tên lớp mà không có không gian tên với cái này không? aka coliru.stacked-crooked.com/a/cf1b1a865bb7ecc7
tower120

9

Chưa. (Tôi nghĩ __class__là được đề xuất ở đâu đó). Bạn cũng có thể cố gắng trích xuất một phần lớp từ __PRETTY_FUNCTION__.


7

Tôi nghĩ rằng sử dụng __PRETTY_FUNCTION__là đủ tốt mặc dù nó bao gồm không gian tên cũng như namespace::classname::functionnamecho đến khi __CLASS__có sẵn.


4

Nếu trình biên dịch của bạn xảy ra g++và bạn đang yêu cầu __CLASS__vì bạn muốn một cách để lấy tên phương thức hiện tại bao gồm cả lớp, __PRETTY_FUNCTION__sẽ giúp ích (theo info gccmục 5.43 Tên hàm dưới dạng chuỗi ).


3

Nếu bạn cần thứ gì đó thực sự tạo ra tên lớp tại thời điểm biên dịch, bạn có thể sử dụng C ++ 11 để thực hiện việc này:

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

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

Tôi nhận ra rằng đây không phải là điều giống như __FUNCTION__nhưng tôi đã tìm thấy bài đăng này trong khi tìm kiếm câu trả lời như thế này. : D



2

Bạn có thể lấy tên hàm bao gồm tên lớp. Điều này có thể xử lý các funcitons loại 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

Giải pháp của tôi:

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__)

Tôi làm việc cho Visual C ++ 12.


1

Đây là một giải pháp dựa trên các mẫu __FUNCTION__macro và 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();
}

Đây là một ví dụ về cách điều này có thể được sử dụng cho một lớp trình ghi nhật ký

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);
  }
}

Đầu ra của Example::Runsau đó sẽ là

Example: 0
Logger<Example>: 0

Lưu ý rằng điều này sẽ không tính đến tính đa hình nếu bạn có một con trỏ đến cơ sở (có thể là tốt).
Các cuộc đua ánh sáng trong quỹ đạo

0

Điều này hoạt động khá tốt nếu bạn sẵn sàng trả chi phí của một con trỏ.

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

Hoạt động với msvc và gcc nữa

#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

Tất cả các giải pháp được đăng ở trên dựa trên __PRETTY_FUNCTION__do có (các) trường hợp cạnh cụ thể mà chúng không chỉ trả về tên lớp / tên lớp. Ví dụ, hãy xem xét giá trị hàm khá sau:

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

Việc sử dụng lần xuất hiện cuối cùng của "::"as delimter sẽ không hoạt động vì tham số hàm cũng chứa một "::"( std::string). Bạn có thể tìm thấy các trường hợp cạnh tương tự "("như dấu phân cách và hơn thế nữa. Giải pháp duy nhất tôi tìm thấy lấy cả macro __FUNCTION____PRETTY_FUNCTION__macro làm tham số. Đây là mã đầy đủ:

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

-2

Phương thức sau (dựa trên methodName () ở trên) cũng có thể xử lý đầu vào như "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) + "()";
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.