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