c++ 基类的自动静态成员函数,以字符串形式返回派生类名

fjnneemd  于 2023-01-10  发布在  其他
关注(0)|答案(2)|浏览(95)

尝试实现这样的函数,该函数将以字符串形式生成其类名,而无需手动键入。发现PRETTY_FUNCTION可以完成此工作,并且msvc 2022表示该二进制文件|找不到使用std::string_view类型的左手数的运算符。如何修复?

static constexpr std::string_view ClassName() {
        constexpr std::string_view pretty_function = std::source_location::current().function_name();
        consteval auto tokens = pretty_function | std::ranges::views::split([](std::string_view view) { return view == " "; })
            | std::ranges::views::transform([](const std::string_view&& token) {
            return token.compare("class") == 0 ? std::string_view{} : token;
        });
        for (const auto && token : tokens) {
            if (token.find('<') != std::string_view::npos) {
                return token.substr(0, token.find('<'));
            }
        } return tokens.back();
    }
nx7onnlm

nx7onnlm1#

如果你需要从基类中得到derivedclass的名字,那么你显然需要virtual函数,而不是static。为了减少代码,你只需要使用宏。
简单方法:

#include <iostream>
#include <string_view>

#define DECLARE_GETNAME(X) \
    virtual std::string_view getName() { \
        return #X; \
    }

class Base {
public:
    virtual ~Base() = default;
    DECLARE_GETNAME(Base)
};

class Derived : public Base{
    DECLARE_GETNAME(Derived)
};

int main()
{
    Derived d;
    Base& b = d;
    std::cout << b.getName();
}

如果你不想每次都输入类名,你也可以这么做,但是要复杂一点,比如:

#include <iostream>
#include <string_view>

template<typename T>
struct TypeName {
    constexpr static std::string_view fullname_intern() {
        #if defined(__clang__) || defined(__GNUC__)
            return __PRETTY_FUNCTION__;
        #elif defined(_MSC_VER)
            return __FUNCSIG__;
        #else
            #error "Unsupported compiler"
        #endif
    }
    constexpr static std::string_view name() {
        size_t prefix_len = TypeName<void>::fullname_intern().find("void");
        size_t multiple   = TypeName<void>::fullname_intern().size() - TypeName<int>::fullname_intern().size();
        size_t dummy_len  = TypeName<void>::fullname_intern().size() - 4*multiple;
        size_t target_len = (fullname_intern().size() - dummy_len)/multiple;
        std::string_view rv = fullname_intern().substr(prefix_len, target_len);
        if (rv.rfind(' ') == rv.npos)
            return rv;
        return rv.substr(rv.rfind(' ')+1);
    }

    using type = T;
    constexpr static std::string_view value = name();
};

#define DECLARE_GETNAME() \
    virtual std::string_view getName() { \
        return TypeName<std::remove_cvref_t<decltype(*this)>>::value; \
    }

class Base {
public:
    virtual ~Base() = default;
    DECLARE_GETNAME()
};

class Derived : public Base{
    DECLARE_GETNAME()
};

int main()
{
    Derived d;
    Base& b = d;
    std::cout << b.getName();
}

用于获取从此处复制的类型名称的代码:https://stackoverflow.com/a/68139582/11680056

eagi6jfj

eagi6jfj2#

我想了想不同的方法,似乎这不知为什么错过了。
下面是我自己的解决方案(不知道它是否能在MSVC 20以外的其他编译器中工作:

virtual const std::string_view& ClassName() const {
    
    const auto&& sub_name = [&](auto&& obj) {
        const std::string_view n(typeid(*obj).name());
        auto p = n.find(" ");
        return p == std::string_view::npos ? n : n.substr(p + 1);
    };
    static const std::string_view name = sub_name(this);
    return name;
}

相关问题