update nameof_enum

This commit is contained in:
terik23 2019-04-08 22:55:35 +05:00
parent 0bc3e1d0c7
commit 498cddf99f
4 changed files with 94 additions and 125 deletions

View file

@ -86,10 +86,19 @@ Header-only C++17 library provides nameof macros and functions to obtain simple
* Nameof expression argument are identified, but do not evaluated.
* Enum variable must be in range `(-NAMEOF_ENUM_RANGE, NAMEOF_ENUM_RANGE)`. By default `NAMEOF_ENUM_RANGE = 128`. If you need a larger range, redefine the macro `NAMEOF_ENUM_RANGE`.
* Enum value must be in range `[-256, 256]`. If you need another range, add specialization enum_range for necessary enum type.
```cpp
#define NAMEOF_ENUM_RANGE 1028 // Redefine NAMEOF_ENUM_RANGE for larger range.
#include <nameof.hpp>
enum number { one = 100, two = 200, three = 300 };
namespace nameof {
template <>
struct enum_range<number> {
static constexpr int min = 100;
static constexpr int max = 300;
};
}
```
* If you need name with template suffix, use NAMEOF_FULL.
@ -113,6 +122,45 @@ Header-only C++17 library provides nameof macros and functions to obtain simple
NAMEOF( somevar ) -> "somevar"
```
* Nameof enum obtains the first defined value enums, and won't work if value are aliased.
```cpp
enum ShapeKind {
ConvexBegin = 0,
Box = 0, // Won't work.
Sphere = 1,
ConvexEnd = 2,
Donut = 2, // Won't work too.
Banana = 3,
COUNT = 4,
};
// NAMEOF_ENUM(ShapeKind::Box) -> "ConvexBegin"
// nameof::nameof_enum(ShapeKind::Box) -> "ConvexBegin"
```
Work around the issue:
```cpp
enum ShapeKind {
// Convex shapes, see ConvexBegin and ConvexEnd below.
Box = 0,
Sphere = 1,
// Non-convex shapes.
Donut = 2,
Banana = 3,
COUNT = Banana + 1,
// Non-reflected aliases.
ConvexBegin = Box,
ConvexEnd = Sphere + 1,
};
// NAMEOF_ENUM(ShapeKind::Box) -> "Box"
// nameof::nameof_enum(ShapeKind::Box) -> "Box"
// Non-reflected aliases.
// NAMEOF_ENUM(ShapeKind::ConvexBegin) -> "Box"
// nameof::nameof_enum(ShapeKind::ConvexBegin) -> "Box"
```
## Integration
You should add required file [nameof.hpp](include/nameof.hpp).

View file

@ -93,11 +93,6 @@ int main() {
std::cout << nameof::nameof_enum(color) << std::endl; // 'RED'
std::cout << NAMEOF_ENUM(color) << std::endl; // 'RED'
// Nameof static storage enum variable.
constexpr auto const_color = Color::BLUE;
std::cout << nameof::nameof_enum<const_color>() << std::endl; // 'BLUE'
std::cout << NAMEOF_CONST_ENUM(const_color) << std::endl; // 'BLUE'
// Nameof.
std::cout << NAMEOF(structvar) << std::endl; // 'structvar'
std::cout << NAMEOF(::structvar) << std::endl; // 'structvar'

View file

@ -31,24 +31,21 @@
#pragma once
#include <array>
#include <cstddef>
#include <type_traits>
#include <limits>
#include <type_traits>
#include <string_view>
// Enum variable must be in range (-NAMEOF_ENUM_RANGE, NAMEOF_ENUM_RANGE). If you need a larger range, redefine the macro NAMEOF_ENUM_RANGE.
#if !defined(NAMEOF_ENUM_RANGE)
# define NAMEOF_ENUM_RANGE 128
#endif
namespace nameof {
static_assert(NAMEOF_ENUM_RANGE > 0,
"NAMEOF_ENUM_RANGE must be positive and greater than zero.");
static_assert(NAMEOF_ENUM_RANGE % 8 == 0,
"NAMEOF_ENUM_RANGE must be a multiple of 8.");
static_assert(NAMEOF_ENUM_RANGE < std::numeric_limits<int>::max(),
"NAMEOF_ENUM_RANGE must be less INT_MAX.");
// Enum value must be in range [-256, 256]. If you need another range, add specialization enum_range for necessary enum type.
template <typename E>
struct enum_range final {
static_assert(std::is_enum_v<E>, "nameof::enum_range requires enum type.");
static constexpr int min = std::is_signed_v<std::underlying_type_t<E>> ? -256 : 0;
static constexpr int max = 256;
};
namespace detail {
@ -168,13 +165,14 @@ template <typename T>
while (name.back() == ' ') {
name.remove_suffix(1);
}
return name;
#endif
}
template <typename E, E V>
[[nodiscard]] constexpr std::string_view nameof_enum_impl() noexcept {
static_assert(std::is_enum_v<E>, "nameof::nameof_enum require enum type.");
[[nodiscard]] constexpr std::string_view enum_name_impl() noexcept {
static_assert(std::is_enum_v<E>, "nameof::enum_name_impl requires enum type.");
#if defined(__clang__)
std::string_view name{__PRETTY_FUNCTION__};
constexpr auto suffix = sizeof("]") - 1;
@ -190,48 +188,37 @@ template <typename E, E V>
#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER)
name.remove_suffix(suffix);
return pretty_name(name, false);
#endif
}
template <typename E, int V>
struct nameof_enum_impl_t final {
[[nodiscard]] constexpr std::string_view operator()(int value) const noexcept {
static_assert(std::is_enum_v<E>, "nameof::nameof_enum require enum type.");
if constexpr (V > std::numeric_limits<std::underlying_type_t<E>>::max()) {
return {}; // Enum variable out of range.
}
template <typename E, int... I>
[[nodiscard]] constexpr decltype(auto) enum_strings_impl(std::integer_sequence<int, I...>) noexcept {
static_assert(std::is_enum_v<E>, "magic_enum::detail::enum_strings_impl requires enum type.");
using U = std::underlying_type_t<E>;
constexpr int min = (enum_range<E>::min > std::numeric_limits<U>::min()) ? enum_range<E>::min : std::numeric_limits<U>::min();
constexpr std::array<std::string_view, sizeof...(I)> enum_names{{enum_name_impl<E, static_cast<E>(I + min)>()...}};
switch (value - V) {
case 0:
return nameof_enum_impl<E, static_cast<E>(V)>();
case 1:
return nameof_enum_impl<E, static_cast<E>(V + 1)>();
case 2:
return nameof_enum_impl<E, static_cast<E>(V + 2)>();
case 3:
return nameof_enum_impl<E, static_cast<E>(V + 3)>();
case 4:
return nameof_enum_impl<E, static_cast<E>(V + 4)>();
case 5:
return nameof_enum_impl<E, static_cast<E>(V + 5)>();
case 6:
return nameof_enum_impl<E, static_cast<E>(V + 6)>();
case 7:
return nameof_enum_impl<E, static_cast<E>(V + 7)>();
default:
return nameof_enum_impl_t<E, V + 8>{}(value);
return enum_names;
}
}
};
template <typename E>
struct nameof_enum_impl_t<E, NAMEOF_ENUM_RANGE> final {
[[nodiscard]] constexpr std::string_view operator()(int) const noexcept {
static_assert(std::is_enum_v<E>, "nameof::nameof_enum require enum type.");
return {}; // Enum variable out of range NAMEOF_ENUM_RANGE.
[[nodiscard]] constexpr std::string_view nameof_enum_impl(int value) noexcept {
static_assert(std::is_enum_v<E>, "magic_enum::detail::nameof_enum_impl requires enum type.");
using U = std::underlying_type_t<E>;
constexpr int max = (enum_range<E>::max < std::numeric_limits<U>::max()) ? enum_range<E>::max : std::numeric_limits<U>::max();
constexpr int min = (enum_range<E>::min > std::numeric_limits<U>::min()) ? enum_range<E>::min : std::numeric_limits<U>::min();
constexpr auto enum_range = std::make_integer_sequence<int, max - min + 1>{};
constexpr auto enum_names = enum_strings_impl<E>(enum_range);
const int i = value - min;
if (i >= 0 && static_cast<std::size_t>(i) < enum_names.size()) {
return enum_names[i];
} else {
return {};
}
}
};
template <typename T>
[[nodiscard]] constexpr std::string_view nameof_impl(std::string_view name, bool with_template_suffix) noexcept {
@ -248,18 +235,7 @@ template <typename T>
// nameof_enum(enum) obtains simple (unqualified) string enum name of enum variable.
template <typename T, typename = std::enable_if_t<std::is_enum_v<std::decay_t<T>>>>
[[nodiscard]] constexpr std::string_view nameof_enum(T value) noexcept {
constexpr bool s = std::is_signed_v<std::underlying_type_t<std::decay_t<T>>>;
constexpr int min = s ? -NAMEOF_ENUM_RANGE : 0;
if (static_cast<int>(value) >= NAMEOF_ENUM_RANGE || static_cast<int>(value) <= -NAMEOF_ENUM_RANGE) {
return {}; // Enum variable out of range NAMEOF_ENUM_RANGE.
}
return detail::nameof_enum_impl_t<std::decay_t<T>, min>{}(static_cast<int>(value));
}
// nameof_enum<enum>() obtains simple (unqualified) string enum name of static storage enum variable.
template <auto V, typename = std::enable_if_t<std::is_enum_v<std::decay_t<decltype(V)>>>>
[[nodiscard]] constexpr std::string_view nameof_enum() noexcept {
return detail::nameof_enum_impl<decltype(V), V>();
return detail::nameof_enum_impl<std::decay_t<T>>(static_cast<int>(value));
}
// nameof_type<type>() obtains string name of type.
@ -282,9 +258,6 @@ template <typename T>
// NAMEOF_ENUM obtains simple (unqualified) string enum name of enum variable.
#define NAMEOF_ENUM(...) ::nameof::nameof_enum<decltype(__VA_ARGS__)>(__VA_ARGS__)
// NAMEOF_CONST_ENUM obtains simple (unqualified) string enum name of static storage enum variable.
#define NAMEOF_CONST_ENUM(...) ::nameof::nameof_enum<__VA_ARGS__>()
// NAMEOF_TYPE obtains string name of type.
#define NAMEOF_TYPE(...) ::nameof::nameof_type<__VA_ARGS__>()

View file

@ -23,7 +23,6 @@
#define CATCH_CONFIG_MAIN
#include <catch.hpp>
#define NAMEOF_ENUM_RANGE 120
#include <nameof.hpp>
#include <string>
@ -205,11 +204,14 @@ TEST_CASE("NAMEOF_RAW") {
TEST_CASE("NAMEOF_ENUM") {
#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER)
Color color_ = Color::BLUE;
constexpr Color color_ = Color::BLUE;
Color m[3] = {Color::RED, Color::GREEN, Color::BLUE};
REQUIRE(NAMEOF_ENUM(Color::RED) == "RED");
REQUIRE(NAMEOF_ENUM(color) == "RED");
constexpr auto color_name = NAMEOF_ENUM(color_);
REQUIRE(color_name == "BLUE");
REQUIRE(NAMEOF_ENUM(Color::BLUE) == "BLUE");
REQUIRE(NAMEOF_ENUM(color_) == "BLUE");
@ -218,10 +220,8 @@ TEST_CASE("NAMEOF_ENUM") {
REQUIRE(NAMEOF_ENUM(Directions::Right) == "Right");
REQUIRE(NAMEOF_ENUM(directions) == "Right");
REQUIRE(NAMEOF_ENUM((Color)NAMEOF_ENUM_RANGE).empty());
REQUIRE(NAMEOF_ENUM((Color)-100).empty());
REQUIRE(NAMEOF_ENUM((Color)100).empty());
REQUIRE(NAMEOF_ENUM((Directions)NAMEOF_ENUM_RANGE).empty());
REQUIRE(NAMEOF_ENUM((Directions)100).empty());
REQUIRE(NAMEOF_ENUM((Directions)100).empty());
#endif
@ -229,11 +229,14 @@ TEST_CASE("NAMEOF_ENUM") {
TEST_CASE("nameof::nameof_enum(enum)") {
#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER)
Color color_ = Color::BLUE;
constexpr Color color_ = Color::BLUE;
Color m[3] = {Color::RED, Color::GREEN, Color::BLUE};
REQUIRE(nameof::nameof_enum(Color::RED) == "RED");
REQUIRE(nameof::nameof_enum(color) == "RED");
constexpr auto color_name = nameof::nameof_enum(color_);
REQUIRE(color_name == "BLUE");
REQUIRE(nameof::nameof_enum(Color::BLUE) == "BLUE");
REQUIRE(nameof::nameof_enum(color_) == "BLUE");
@ -242,63 +245,13 @@ TEST_CASE("nameof::nameof_enum(enum)") {
REQUIRE(nameof::nameof_enum(Directions::Right) == "Right");
REQUIRE(nameof::nameof_enum(directions) == "Right");
REQUIRE(nameof::nameof_enum((Color)NAMEOF_ENUM_RANGE).empty());
REQUIRE(nameof::nameof_enum((Color)-100).empty());
REQUIRE(nameof::nameof_enum((Color)100).empty());
REQUIRE(nameof::nameof_enum((Directions)NAMEOF_ENUM_RANGE).empty());
REQUIRE(nameof::nameof_enum((Directions)100).empty());
REQUIRE(nameof::nameof_enum((Directions)100).empty());
#endif
}
TEST_CASE("NAMEOF_CONST_ENUM") {
#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER)
static const Color color_ = Color::BLUE;
constexpr Color m[3] = {Color::RED, Color::GREEN, Color::BLUE};
REQUIRE(NAMEOF_CONST_ENUM(Color::RED) == "RED");
REQUIRE(NAMEOF_CONST_ENUM(color) == "RED");
REQUIRE(NAMEOF_CONST_ENUM(Color::BLUE) == "BLUE");
REQUIRE(NAMEOF_CONST_ENUM(color_) == "BLUE");
REQUIRE(NAMEOF_CONST_ENUM(m[1]) == "GREEN");
REQUIRE(NAMEOF_CONST_ENUM(Directions::Right) == "Right");
REQUIRE(NAMEOF_CONST_ENUM(directions) == "Right");
REQUIRE(NAMEOF_CONST_ENUM((Color)NAMEOF_ENUM_RANGE).empty());
REQUIRE(NAMEOF_CONST_ENUM((Color)-100).empty());
REQUIRE(NAMEOF_CONST_ENUM((Color)100).empty());
REQUIRE(NAMEOF_CONST_ENUM((Directions)NAMEOF_ENUM_RANGE).empty());
REQUIRE(NAMEOF_CONST_ENUM((Directions)100).empty());
REQUIRE(NAMEOF_CONST_ENUM((Directions)100).empty());
#endif
}
TEST_CASE("nameof::nameof_enum<enum>()") {
#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER)
static const Color color_ = Color::BLUE;
constexpr Color m[3] = {Color::RED, Color::GREEN, Color::BLUE};
REQUIRE(nameof::nameof_enum<Color::RED>() == "RED");
REQUIRE(nameof::nameof_enum<color>() == "RED");
REQUIRE(nameof::nameof_enum<Color::BLUE>() == "BLUE");
REQUIRE(nameof::nameof_enum<color_>() == "BLUE");
REQUIRE(nameof::nameof_enum<m[1]>() == "GREEN");
REQUIRE(nameof::nameof_enum<Directions::Right>() == "Right");
REQUIRE(nameof::nameof_enum<directions>() == "Right");
REQUIRE(nameof::nameof_enum<(Color)NAMEOF_ENUM_RANGE>().empty());
REQUIRE(nameof::nameof_enum<(Color)-100>().empty());
REQUIRE(nameof::nameof_enum<(Color)100>().empty());
REQUIRE(nameof::nameof_enum<(Directions)NAMEOF_ENUM_RANGE>().empty());
REQUIRE(nameof::nameof_enum<(Directions)100>().empty());
REQUIRE(nameof::nameof_enum<(Directions)100>().empty());
#endif
}
TEST_CASE("NAMEOF_VAR_TYPE") {
#if defined(__clang__)
REQUIRE(NAMEOF_VAR_TYPE(struct_var) == "SomeStruct");