在 C++ 的静态方法中获取 class 名称作为字符串

Getting class name as a string in static method in C++

假设我们严格使用 Clang。没有使用其他编译器。另请注意,Clang 支持 CXX ABI。

我们正在使用 C++14。

通常情况下,我们会像这样删除 class 名称:

#include <cxxabi.h>

class GoodClass {
public:
    virtual const char *foo() const noexcept;
}

const char *
GoodClass::foo() const noexcept
{
    //  Naive implementation, not gonna' check any errors and stuff.
    int32_t status = 0;

    return abi::__cxa_demangle(typeid(*this).name(), 0, 0, &status);
}

当我们需要此 class 的 class 个子 class 的 class 名称时,此方法会有所帮助:

class SomeSubclassOfGoodClass : public GoodClass { }

SomeSubclassOfGoodClass object;
std::cout << object.foo(); //  prints "SomeSubclassOfGoodClass"

然而,在静态方法中,我们不能使用 this 因为没有实例。因此,不可能向 typeid 指令提供对象。

示例方法效果很好(具有多态性),但它需要一个实例才能运行。这会涉及到关于OO的问题(比如构造器)

遇到这种情况你会怎么做?

感谢您的关注。

typeid 运算符也可以应用于类型,而不仅仅是表达式:当您无法访问 this.

时,typeid(GoodClass) 应该可以工作

编辑:如果没有实例,您需要转向静态多态性。您可以在基础 class Identifiable<X> 中混合使用上面建议的代码的静态方法,但改用 typeid(X) 。你的 classes 需要扩展这个 class 将自己作为模板参数传递(奇怪的递归模板模式),但不可能确保 class 这样做:

class C : public Identifiable<C> {}; // method returns C
class D : public Identifiable<C> {}; // also returns C

demangle 的使用需要一些工作。目前你有内存泄漏。

这是解决该问题的一种方法:

#include <cxxabi.h>
#include <memory>
#include <iostream>
#include <string>
#include <typeinfo>
#include <typeindex>
#include <cassert>
#include <stdexcept>

struct demangled_string
{
    using ptr_type = std::unique_ptr<char, void(*)(void*)>;
    demangled_string(ptr_type&& ptr) noexcept;
    const char* c_str() const;
    operator std::string() const;

    std::ostream& write(std::ostream& os) const;
private:
    ptr_type _ptr;
};

inline std::ostream& operator<<(std::ostream& os, const demangled_string& str)
{
    return str.write(os);
}

inline std::string operator+ (std::string l, const demangled_string& r) {
    return l + r.c_str();
}

inline std::string operator+(const demangled_string& l, const std::string& r)
{
    return std::string(l) + r;
}

demangled_string demangle(const char* name);
demangled_string demangle(const std::type_info& type);
demangled_string demangle(std::type_index type);

template<class T>
demangled_string demangle(T* p) {
    return demangle(typeid(*p));
}

template<class T>
demangled_string demangle()
{
    return demangle(typeid(T));
}


// implementation

demangled_string::demangled_string(ptr_type&& ptr) noexcept
: _ptr(std::move(ptr))
{}

std::ostream& demangled_string::write(std::ostream& os) const
{
    if (_ptr) {
        return os << _ptr.get();
    }
    else {
        return os << "{nullptr}";
    }
}

const char* demangled_string::c_str() const
{
    if (!_ptr)
    {
        throw std::logic_error("demangled_string - zombie object");
    }
    else {
        return _ptr.get();
    }
}

demangled_string::operator std::string() const {
    return std::string(c_str());
}


demangled_string demangle(const char* name)
{
    using namespace std::string_literals;

    int status = -4;

    demangled_string::ptr_type ptr {
        abi::__cxa_demangle(name, nullptr, nullptr, &status),
        std::free
    };

    if (status == 0) return { std::move(ptr) };

    switch(status)
    {
        case -1: throw std::bad_alloc();
        case -2: {
            std::string msg = "invalid mangled name~";
            msg += name;
            auto p = (char*)std::malloc(msg.length() + 1);
            strcpy(p, msg.c_str());
            return demangled_string::ptr_type { p, std::free };
        }
        case -3:
            assert(!"invalid argument sent to __cxa_demangle");
            throw std::logic_error("invalid argument sent to __cxa_demangle");
        default:
            assert(!"PANIC! unexpected return value");
            throw std::logic_error("PANIC! unexpected return value");
    }
}

demangled_string demangle(const std::type_info& type)
{
    return demangle(type.name());
}

demangled_string demangle(std::type_index type)
{
    return demangle(type.name());
}

std::string method(const demangled_string& cls, const char* method)
{
    return std::string(cls) + "::" + method;
}

// test

class test_class
{
    using this_class = test_class;

    static auto classname() { return demangle<this_class>(); }

public:
    static void test1() {
        std::cout << method(demangle<this_class>(), __func__) << std::endl;
        std::cout << method(classname(), __func__) << std::endl;
    }

    void test2() {
        std::cout << method(demangle(this), __func__) << std::endl;
        std::cout << method(classname(), __func__) << std::endl;
    }

};

int main()
{
    test_class t;
    t.test1();
    t.test2();
}

预期输出:

test_class::test1
test_class::test1
test_class::test2
test_class::test2