C++ 中有 __CLASS__ 宏吗?
Posted
技术标签:
【中文标题】C++ 中有 __CLASS__ 宏吗?【英文标题】:Is there a __CLASS__ macro in C++? 【发布时间】:2010-12-12 14:44:58 【问题描述】:C++ 中是否有一个__CLASS__
宏,它给出的类名类似于给出函数名的__FUNCTION__
宏
【问题讨论】:
【参考方案1】:我使用 __PRETTY_FUNCTION__
和 constexpr
和 C++17 constexpr
std::string_view
方法创建了一个函数。我还稍微更新了算法以使其更可靠(感谢@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
for use as template parameter。
【讨论】:
这是最好的答案,在编译时得到你想要的【参考方案2】:上面发布的所有依赖于__PRETTY_FUNCTION__
的解决方案都有特定的边缘情况,它们不只返回类名/类名。例如,考虑以下漂亮的函数值:
static std::string PrettyFunctionHelper::Test::testMacro(std::string)
使用最后出现的"::"
作为分隔符将不起作用,因为函数参数还包含"::"
(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
【讨论】:
【参考方案3】:如果您需要在编译时实际生成类名的东西,您可以使用 C++11 来执行此操作:
#define __CLASS__ std::remove_reference<decltype(classMacroImpl(this))>::type
template<class T> T& classMacroImpl(const T* t);
我认识到这与__FUNCTION__
不同,但我在寻找这样的答案时发现了这篇文章。 :D
【讨论】:
这是迄今为止我见过的最好的解决方案!它需要在编译时,而不是运行时! 编译错误,我在 GCC-11 上试过,选项 -std=gnu++20 它只是获取类的类型,而不是字符串表示【参考方案4】:下面的方法(基于上面的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) + "()";
【讨论】:
【参考方案5】:也适用于 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
【讨论】:
【参考方案6】:我想推荐boost::typeindex,这是我从 Scott Meyer 的“Effective Modern 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”编译,生成以下内容
输出
如果你想打印一个模板类型,那很简单。
T = 双倍
要从对象实例中获取它,只需使用 decltype:
fb的类型是:foo_bar
【讨论】:
是否有可能只获得没有命名空间的类名?又名coliru.stacked-crooked.com/a/cf1b1a865bb7ecc7【参考方案7】:如果您愿意支付指针的费用,这将非常有效。
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__ ) ;
;
【讨论】:
【参考方案8】:我认为使用__PRETTY_FUNCTION__
就足够了,尽管它也包含命名空间,即namespace::classname::functionname
,直到__CLASS__
可用。
【讨论】:
【参考方案9】:这是一个基于 __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
【讨论】:
请注意,如果你有一个指向基址的指针(这可能没问题),这不会考虑多态性。【参考方案10】:我的解决方案:
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 工作。
【讨论】:
【参考方案11】:使用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__
包围它吗?
可以使用rfind(' ', colons)
代替substr(0,colons).rfind(" ")
来创建一个额外的字符串。
我宁愿使用 find_last_of("::") 否则函数只会返回一个命名空间,如果有的话
我写了一个可能范围更广的 __METHOD_NAME__
宏版本。检查here。
在 C++11 中,您可以尝试将其设为 constexpr
函数以在编译时对其进行评估【参考方案12】:
您可以获取包含类名的函数名。 这可以处理 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) + "()";
【讨论】:
【参考方案13】:最接近的方法是调用typeid(your_class).name()
- 但这会产生编译器特定的错误名称。
要在课堂上使用它,只需typeid(*this).name()
【讨论】:
typeid(*this).name() 可以在类函数中使用 这样更好。至于知道类,定义 char 数组听起来比推迟到运行时更好。 可惜它没有像 __ CLASS __ 那样定义,在预处理阶段可以很方便! :( @Max 它没有但可以。它了解函数的类似方式:-P @kexik:预处理器也不知道函数,标准的__func__
和非标准的__FUNCTION__
不是宏。微软将__FUNCTION__
记录为一个宏,但事实并非如此,当您使用/P
编译时,它不会被预处理器扩展。【参考方案14】:
如果你的编译器恰好是g++
,而你要求__CLASS__
,因为你想要一种方法来获取包括类在内的当前方法名称,__PRETTY_FUNCTION__
应该会有所帮助(根据info gcc
,5.43 函数名作为字符串)。
【讨论】:
【参考方案15】:如果你说的是 MS C++(你应该声明,尤其是 __FUNCTION__
是一个非标准扩展),你可以解析 __FUNCDNAME__
and __FUNCSIG__
symbols
【讨论】:
【参考方案16】:还没有。 (我认为__class__
是在某处提出的)。您也可以尝试从__PRETTY_FUNCTION__
中提取类部分。
【讨论】:
以上是关于C++ 中有 __CLASS__ 宏吗?的主要内容,如果未能解决你的问题,请参考以下文章