在C ++中使用静态方法将类名称作为字符串

时间:2016-06-11 13:45:23

标签: c++ polymorphism clang c++14 static-methods

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

我们正在使用C ++ 14。

通常情况下,我们会得到像这样的demangled类名:

#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 SomeSubclassOfGoodClass : public GoodClass { }

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

但是,在静态方法中,我们无法使用this,因为没有实例。因此,不可能将对象提供给typeid指令。

示例方法很好(具有多态性),但它需要一个实例来操作。这将涉及有关OO的问题(例如构造函数)。

在这种情况下你会做什么?

感谢您的关注。

2 个答案:

答案 0 :(得分:1)

typeid运算符也可以应用于某个类型,而不仅仅是表达式:typeid(GoodClass)当您无法访问this时,它应该有效。

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

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

答案 1 :(得分:1)

使用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