Відповіді:
Найближче, до чого можна зателефонувати typeid(your_class).name()
- але це створює конкретне спотворене ім'я компілятора.
Щоб просто використовувати його всередині класу typeid(*this).name()
__func__
та нестандартні не __FUNCTION__
є макросами. Microsoft документує __FUNCTION__
як макрос, але тим, що насправді це не так, є те, що він не розширюється препроцесором під час компіляції /P
.
Проблема використання 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__)
#ifdef __GNU_C__
?
substr(0,colons).rfind(" ")
можна було rfind(' ', colons)
пощадити створення зайвого рядка.
constexpr
функцію для оцінки під час компіляції
Я хотів би запропонувати boost :: typeindex , про який я дізнався з "Ефективного сучасного C ++" Скотта Мейєра. Ось базовий приклад:
Приклад
#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" це дає таке
Вихід
Якщо ви хочете надрукувати тип шаблону, це легко.
Т = подвійний
Щоб отримати його з екземпляра об'єкта, просто використовуйте decltype:
Тип fb: foo_bar
Ще ні. (Думаю __class__
, десь пропонується). Ви також можете спробувати витягти частину класу з __PRETTY_FUNCTION__
.
Якщо вам потрібно щось, що насправді створюватиме назву класу під час компіляції, ви можете використовувати C ++ 11 для цього:
#define __CLASS__ std::remove_reference<decltype(classMacroImpl(this))>::type
template<class T> T& classMacroImpl(const T* t);
Я усвідомлюю, що це не те саме, що, __FUNCTION__
але я знайшов цей пост, шукаючи відповідь на зразок цього. : D
Якщо ви говорите про MS C ++ (ви повинні вказати, особливо, як __FUNCTION__
нестандартне розширення), є __FUNCDNAME__
і __FUNCSIG__
символи, які ви можете проаналізувати
Ви можете отримати назву функції, включаючи назву класу. Це може обробляти фунцитони типу С.
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) + "()";
}
Моє рішення:
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.
Ось рішення на основі __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
Це працює дуже добре, якщо ви готові заплатити вартість вказівника.
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__ ) {};
};
Працює з 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
Усі рішення, опубліковані вище, які покладаються на __PRETTY_FUNCTION__
do, мають конкретні випадки краю, коли вони не повертають лише ім'я класу / ім'я класу. Наприклад, розглянемо таке гарне значення функції:
static std::string PrettyFunctionHelper::Test::testMacro(std::string)
Використання останнього випадку "::"
as delimter не буде працювати, оскільки параметр функції також містить "::"
( 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
Наступний метод (на основі методу nameName () вище) також може обробляти введення типу "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) + "()";
}