Câu trả lời:
Đ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()
__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
.
Vấn đề với việc sử dụng typeid(*this).name()
là không có this
con 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__)
#ifdef __GNU_C__
?
substr(0,colons).rfind(" ")
một người có thể sử dụng rfind(' ', colons)
để tạo thêm một chuỗi.
constexpr
hàm để đánh giá nó tại thời điểm biên dịch
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
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__
.
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
Nếu bạn đang nói MS C ++ (Bạn nên nói rõ, đặc biệt __FUNCTION__
là một phần mở rộng không chuẩn), có __FUNCDNAME__
và __FUNCSIG__
các ký hiệu mà bạn có thể phân tích cú pháp
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) + "()";
}
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.
Đâ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::Run
sau đó sẽ là
Example: 0
Logger<Example>: 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__ ) {};
};
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
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__
và __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
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) + "()";
}