update nameof_enum
This commit is contained in:
parent
0bc3e1d0c7
commit
498cddf99f
4 changed files with 94 additions and 125 deletions
52
README.md
52
README.md
|
@ -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).
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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__>()
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue