Home > Enterprise >  automatic static member function for base class that return name of derived class in string
automatic static member function for base class that return name of derived class in string

Time:01-09

Trying to do implementation of such function that will produce it's class name in string form without typing it manually. found that PRETTY_FUNCTION may do the job, and msvc 2022 says that binary | no operator found which takes left hand operand of type std::string_view. How to fix?

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();
    }

CodePudding user response:

If you need to get name of derived class from base class, then you obviously need virtual function, not static. And to minimize code you need to type just use macro.

Simple way:

#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();
}

If you don't want to type class name every time, you can do it also, but little bit more complicated. Something like that:

#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();
}

Code to get type name copied from here: https://stackoverflow.com/a/68139582/11680056

CodePudding user response:

I though about different approaches, seems this somewhy missed.

here is my own solution (dunno if it works in other compilers than 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;
}
  • Related