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__ 将返回&lt;class&gt;::&lt;method&gt;() 形式的字符串,从__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 gcc5.43 函数名作为字符串)。

【讨论】:

【参考方案15】:

如果你说的是 MS C++(你应该声明,尤其是 __FUNCTION__ 是一个非标准扩展),你可以解析 __FUNCDNAME__ and __FUNCSIG__ symbols

【讨论】:

【参考方案16】:

还没有。 (我认为__class__ 是在某处提出的)。您也可以尝试从__PRETTY_FUNCTION__中提取类部分。

【讨论】:

以上是关于C++ 中有 __CLASS__ 宏吗?的主要内容,如果未能解决你的问题,请参考以下文章

C++内存管理5_Per-class allocator

二叉树:C++实现

c++中的宏 #define _CLASSDEF(name) class name

jquery 节点查找(查同名 class 的下一个)

1_类的定义(Defining Class)

检查 self.__class__ 的目的是啥?