improve nameof

This commit is contained in:
neargye 2019-04-02 20:42:47 +05:00
parent b236173ad9
commit e25503192f
4 changed files with 38 additions and 48 deletions

View file

@ -102,8 +102,7 @@ Header-only C++17 library provides nameof macros and functions to obtain simple
* If you need of raw fully-qualified name, use NAMEOF_RAW.
```cpp
NAMEOF_RAW(somevar.somefield) -> "somevar.somefield"
NAMEOF_RAW(&SomeStruct::SomeMethod) -> "&SomeStruct::SomeMethod"
NAMEOF_RAW(const SomeClass<int> volatile *) -> "const SomeClass<int> volatile *"
NAMEOF_RAW(&some_class::some_method<int>) -> "&some_class::some_method<int>"
```
* Spaces and Tabs ignored

View file

@ -102,6 +102,7 @@ int main() {
std::cout << NAMEOF(structvar.somefield) << std::endl; // somefield
std::cout << NAMEOF((&structvar)->somefield) << std::endl; // somefield
std::cout << NAMEOF(othervar.ll.field) << std::endl; // field
std::cout << NAMEOF(ptrvar) << std::endl; // 'ptrvar'
// Function name.
std::cout << NAMEOF(&SomeStruct::SomeMethod1) << std::endl; // SomeMethod1
@ -120,7 +121,7 @@ int main() {
// Type name.
std::cout << NAMEOF_VAR_TYPE(structvar) << std::endl; // SomeStruct
std::cout << nameof::nameof_type<decltype(structvar)>() << std::endl; // SomeStruct
std::cout << NAMEOF_VAR_TYPE(SomeClass<int>{}) << std::endl; // SomeClass
std::cout << NAMEOF_VAR_TYPE(SomeClass<int>{}) << std::endl; // SomeClass<int>
std::cout << NAMEOF_VAR_TYPE(othervar.ll) << std::endl; // Long::LL
std::cout << NAMEOF_VAR_TYPE(std::declval<const SomeClass<int>>()) << std::endl; // const SomeClass<int> &&
@ -135,7 +136,6 @@ int main() {
// Raw name.
std::cout << NAMEOF_RAW(structvar.somefield) << std::endl; // structvar.somefield
std::cout << NAMEOF_RAW(&SomeStruct::SomeMethod1) << std::endl; // &SomeStruct::SomeMethod1
std::cout << NAMEOF_RAW(const SomeClass<int> volatile *) << std::endl; // const SomeClass<int> volatile *
// Some more complex example.
@ -158,30 +158,20 @@ int main() {
/* Remarks */
#if 0
// This expression does not have name.
std::cout << NAMEOF("Bad case"_string) << std::endl; // '_string'
std::cout << NAMEOF(ptrvar[0]) << std::endl; // ''
std::cout << NAMEOF(42.0) << std::endl; // ''
std::cout << NAMEOF(42.f) << std::endl; // 'f'
std::cout << NAMEOF(42) << std::endl; // ''
std::cout << NAMEOF(42.0_deg) << std::endl; // ''
std::cout << NAMEOF(std::string()) << std::endl; // 'string'
std::cout << NAMEOF(std::string{}) << std::endl; // 'string'
std::cout << NAMEOF(std::string{"test"}) << std::endl; // 'string'
std::cout << NAMEOF(structvar.somefield + structvar.somefield) << std::endl; // ' somefield'
std::cout << NAMEOF(42 + 42) << std::endl; // ''
std::cout << NAMEOF((structvar)) << std::endl; // ''
std::cout << NAMEOF((SomeMethod4<int, float>)(1.0f)) << std::endl; // ''
std::cout << NAMEOF(42, 42, 42) << std::endl; // ''
#endif
#if 0
// This expression does not have name and not compilation.
std::cout << NAMEOF("Bad case") << std::endl;
std::cout << NAMEOF("somevar.somefield") << std::endl;
std::cout << NAMEOF(std::basic_string<char>) << std::endl;
std::cout << NAMEOF(ptrvar[0]) << std::endl;
std::cout << NAMEOF(std::cout << structvar << std::endl) << std::endl;
std::cout << NAMEOF(decltype(structvar)) << std::endl;
std::cout << NAMEOF(typeid(structvar)) << std::endl;
std::cout << NAMEOF((structvar)) << std::endl;
std::cout << NAMEOF(42 + 42) << std::endl; // ''
std::cout << NAMEOF("Bad case"_string) << std::endl; // ''
std::cout << NAMEOF("Bad case") << std::endl; // ''
std::cout << NAMEOF("somevar.somefield") << std::endl; // ''
std::cout << NAMEOF(42.f) << std::endl; // ''
std::cout << NAMEOF(structvar.somefield++) << std::endl; // ''
std::cout << NAMEOF(std::string{"test"}) << std::endl; // ''
#endif
return 0;

View file

@ -61,22 +61,13 @@ struct identity final {
return (!front && c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
[[nodiscard]] constexpr bool is_bracket_char(char c) noexcept {
return c == ')' || c == '}' || c == '>' || c == '(' || c == '{' || c == '<';
}
[[nodiscard]] constexpr std::string_view pretty_name(std::string_view name, bool with_template_suffix) noexcept {
for (std::size_t i = name.size(), h = 0, s = 0; i > 0; --i) {
if (h == 0 && !is_name_char(name[i - 1], false) && !is_bracket_char(name[i - 1])) {
++s;
continue;
}
if (name[i - 1] == ')' || name[i - 1] == '}') {
if (name[i - 1] == ')') {
++h;
++s;
continue;
} else if (name[i - 1] == '(' || name[i - 1] == '{') {
} else if (name[i - 1] == '(') {
--h;
++s;
continue;
@ -91,6 +82,10 @@ struct identity final {
}
}
if (name.length() > 0 && (name.front() == '"' || (name.front() >= '0' && name.front() <= '9') || name.back() == '"')) {
return {}; // Invalid name.
}
std::size_t s = 0;
for (std::size_t i = name.size(), h = 0; i > 0; --i) {
if (name[i - 1] == '>') {
@ -122,7 +117,7 @@ struct identity final {
if (name.length() > 0 && is_name_char(name.front(), true)) {
return name;
} else {
return {}; // Does not have name.
return {}; // Invalid name.
}
}
@ -183,7 +178,19 @@ template <typename E, E V>
#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER)
name.remove_suffix(suffix);
return pretty_name(name, false);
for (std::size_t i = name.size(); i > 0; --i) {
if (!is_name_char(name[i - 1], false)) {
name.remove_prefix(i);
break;
}
}
if (name.length() > 0 && is_name_char(name.front(), true)) {
return name;
}
else {
return {}; // Invalid enum name.
}
#endif
}
@ -226,11 +233,12 @@ struct nameof_enum_impl_t<E, NAMEOF_ENUM_RANGE> final {
}
};
template <typename T, typename = std::enable_if_t<!std::is_reference_v<T>>>
template <typename T>
[[nodiscard]] constexpr std::string_view nameof_impl(std::string_view name, bool with_template_suffix) noexcept {
return pretty_name(name, with_template_suffix);
}
template <typename T>
[[nodiscard]] constexpr std::string_view nameof_raw_impl(std::string_view name) noexcept {
return name;
}
@ -268,6 +276,9 @@ template <typename T>
// NAMEOF_FULL used to obtain the simple (unqualified) full (with template suffix) string name of variable, function, enum, macro.
#define NAMEOF_FULL(...) ::nameof::detail::nameof_impl<decltype(__VA_ARGS__)>(#__VA_ARGS__, true)
// NAMEOF_RAW used to obtain the raw string name of variable, function, enum, macro.
#define NAMEOF_RAW(...) ::nameof::detail::nameof_raw_impl<decltype(__VA_ARGS__)>(#__VA_ARGS__)
// NAMEOF_ENUM used to obtain the simple (unqualified) string enum name of enum variable.
#define NAMEOF_ENUM(...) ::nameof::nameof_enum<decltype(__VA_ARGS__)>(__VA_ARGS__)
@ -279,6 +290,3 @@ template <typename T>
// NAMEOF_VAR_TYPE used to obtain the string name of variable type.
#define NAMEOF_VAR_TYPE(...) ::nameof::nameof_type<decltype(__VA_ARGS__)>()
// NAMEOF_RAW used to obtain the raw string name of variable, function, enum, macro.
#define NAMEOF_RAW(...) ::nameof::detail::nameof_raw_impl(#__VA_ARGS__)

View file

@ -93,7 +93,6 @@ TEST_CASE("NAMEOF") {
SECTION("member") {
REQUIRE(NAMEOF(struct_var.somefield) == "somefield");
REQUIRE(NAMEOF(struct_var.somefield++) == "somefield");
REQUIRE(NAMEOF((&struct_var)->somefield) == "somefield");
REQUIRE(NAMEOF(othervar.ll.field) == "field");
}
@ -129,7 +128,6 @@ TEST_CASE("NAMEOF_FULL") {
SECTION("member") {
REQUIRE(NAMEOF_FULL(struct_var.somefield) == "somefield");
REQUIRE(NAMEOF_FULL(struct_var.somefield++) == "somefield");
REQUIRE(NAMEOF_FULL((&struct_var)->somefield) == "somefield");
REQUIRE(NAMEOF_FULL(othervar.ll.field) == "field");
}
@ -163,6 +161,7 @@ TEST_CASE("NAMEOF_RAW") {
REQUIRE(NAMEOF_RAW(::struct_var) == "::struct_var");
REQUIRE(NAMEOF_RAW(ptr_s) == "ptr_s");
REQUIRE(NAMEOF_RAW(*ptr_s) == "*ptr_s");
REQUIRE(NAMEOF_RAW(ptr_s[0]) == "ptr_s[0]");
}
SECTION("member") {
@ -202,12 +201,6 @@ TEST_CASE("NAMEOF_RAW") {
REQUIRE(NAMEOF_RAW(__LINE__) == "__LINE__");
REQUIRE(NAMEOF_RAW(__FILE__) == "__FILE__");
}
SECTION("type") {
REQUIRE(NAMEOF_RAW(const SomeStruct volatile *) == "const SomeStruct volatile *");
REQUIRE(NAMEOF_RAW(SomeClass<int>) == "SomeClass<int>");
REQUIRE(NAMEOF_RAW(decltype(othervar)) == "decltype(othervar)");
}
}
TEST_CASE("NAMEOF_ENUM") {