-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added std::type_info formatter #3978
Conversation
matt77hias
commented
May 25, 2024
- Added std::type_info formatter;
- Reused std::type_info formatter in std::exception formatters;
- Updated MSVC std::type_info outputting to exclude all class, struct and enum occurences.
Ok still WIP in order to support |
75cdef1
to
ce222b5
Compare
Update: removed union occurrences as well for MSVC |
cb58b68
to
7c74917
Compare
I think formatting Before your changes, |
Note that prior to my changes, the
I think we can output the characters one by one while jumping over the occurrences instead of using a I do wonder whether I could erronously strip part of the actual identifier, instead of language keywords. All of the patterns do contain a space character that should prevent this. MSVC does not seem to use spaces before and after |
Suggested changes Create function: namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out,
std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
std::string demangled_name(ti.name());
details::remove_all_substrings(demangled_name, "class ");
details::remove_all_substrings(demangled_name, "enum ");
details::remove_all_substrings(demangled_name, "struct ");
details::remove_all_substrings(demangled_name, "union ");
return detail::write_bytes<Char>(out, demangled_name);
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} and reuse it in formatters. We don't need to use |
Out of scope improvements:
|
LGTM |
55387ae
to
3bf8a0b
Compare
include/fmt/std.h
Outdated
#else | ||
out = detail::write_bytes<Char>(out, string_view("std::exception")); | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am sorry :(
It was my mistake.
#else
code will never execute if FMT_USE_RTTI
is disabled, because with_typename_ = FMT_USE_RTTI != 0;
in parse()
function.
More correct variant is remove this #else
block and move #endif
after return detail::write_bytes<Char>(out, string_view(ex.what()));
This was before PR.
I'm sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be more clear to do something like the below instead:
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good but please a test case for the new formatter.
It is actually implicitly tested already via the exception formatter. |
TEST(std_test, type_info) { | ||
EXPECT_EQ(fmt::format("{}", typeid(std::runtime_error)), | ||
"std::runtime_error"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be wrapped in FMT_USE_RTTI check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note
EXPECT_EQ("std::runtime_error: Test Exception", fmt::format("{:t}", ex));
EXPECT_THAT(fmt::format("{:t}", ex), StartsWith("std::system_error: "));
EXPECT_THAT(fmt::format("{:t}", ex),
StartsWith("std::filesystem::filesystem_error: "));
None of these tests would succeed without FMT_USE_RTTI
, which is not used as guard in std-test.cc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a bug.
* Added std::type_info formatter; * Reused std::type_info formatter in std::exception formatters; * Updated MSVC std::type_info outputting to exclude all class, struct and enum occurences.