Skip to content
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

Enum utils #4601

Merged
merged 13 commits into from
May 7, 2024
63 changes: 63 additions & 0 deletions nano/core_test/enums.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <nano/lib/enum_util.hpp>
#include <nano/lib/stats_enums.hpp>
#include <nano/test_common/system.hpp>
#include <nano/test_common/testutil.hpp>
Expand Down Expand Up @@ -62,4 +63,66 @@ TEST (enums, log_category)
ASSERT_FALSE (to_string (nano::log::type::_last).empty ());
ASSERT_NO_THROW (std::string{ to_string (nano::log::type::_last) });
ASSERT_EQ (to_string (nano::log::type::_last), "_last");
}

namespace
{
enum class test_enum
{
_invalid,
one,
two,
three,
_last
};

enum class test_enum2
{
one,
};
}

TEST (enum_util, name)
{
ASSERT_EQ (nano::enum_util::name (test_enum::_invalid), "_invalid");
ASSERT_EQ (nano::enum_util::name (test_enum::one), "one");
ASSERT_EQ (nano::enum_util::name (test_enum::two), "two");
ASSERT_EQ (nano::enum_util::name (test_enum::three), "three");
ASSERT_EQ (nano::enum_util::name (test_enum::_last), "_last");
}

TEST (enum_util, values)
{
auto values = nano::enum_util::values<test_enum> ();
ASSERT_EQ (values.size (), 3);
ASSERT_EQ (values[0], test_enum::one);
ASSERT_EQ (values[1], test_enum::two);
ASSERT_EQ (values[2], test_enum::three);

auto all_values = nano::enum_util::values<test_enum, /* don't ignore reserved */ false> ();
ASSERT_EQ (all_values.size (), 5);
ASSERT_EQ (all_values[0], test_enum::_invalid);
ASSERT_EQ (all_values[1], test_enum::one);
ASSERT_EQ (all_values[2], test_enum::two);
ASSERT_EQ (all_values[3], test_enum::three);
ASSERT_EQ (all_values[4], test_enum::_last);
}

TEST (enum_util, parse)
{
ASSERT_EQ (nano::enum_util::try_parse<test_enum> ("one"), test_enum::one);
ASSERT_EQ (nano::enum_util::try_parse<test_enum> ("two"), test_enum::two);
ASSERT_EQ (nano::enum_util::try_parse<test_enum> ("three"), test_enum::three);
ASSERT_FALSE (nano::enum_util::try_parse<test_enum> ("four").has_value ());
ASSERT_FALSE (nano::enum_util::try_parse<test_enum> ("_invalid").has_value ());
ASSERT_FALSE (nano::enum_util::try_parse<test_enum> ("_last").has_value ());

ASSERT_NO_THROW (nano::enum_util::parse<test_enum> ("one"));
ASSERT_THROW (nano::enum_util::parse<test_enum> ("four"), std::invalid_argument);
ASSERT_THROW (nano::enum_util::parse<test_enum> ("_invalid"), std::invalid_argument);
}

TEST (enum_util, cast)
{
ASSERT_EQ (nano::enum_util::cast<test_enum> (test_enum2::one), test_enum::one);
}
5 changes: 2 additions & 3 deletions nano/lib/block_type.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#include <nano/lib/block_type.hpp>

#include <magic_enum.hpp>
#include <nano/lib/enum_util.hpp>

std::string_view nano::to_string (nano::block_type type)
{
return magic_enum::enum_name (type);
return nano::enum_util::name (type);
}

void nano::serialize_block_type (nano::stream & stream, const nano::block_type & type)
Expand Down
2 changes: 1 addition & 1 deletion nano/lib/blocks.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <nano/crypto_lib/random_pool.hpp>
#include <nano/lib/blocks.hpp>
#include <nano/lib/enum_util.hpp>
#include <nano/lib/memory.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/threading.hpp>
Expand All @@ -11,7 +12,6 @@
#include <bitset>

#include <cryptopp/words.h>
#include <magic_enum.hpp>

size_t constexpr nano::send_block::size;
size_t constexpr nano::receive_block::size;
Expand Down
103 changes: 103 additions & 0 deletions nano/lib/enum_util.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#pragma once

#include <nano/lib/utility.hpp>

#include <magic_enum.hpp>
#include <magic_enum_containers.hpp>

namespace nano
{
/**
* Array indexable by enum values
*/
template <typename Index, typename Value>
using enum_array = magic_enum::containers::array<Index, Value>;
}

// Needs nested namespace to avoid ADL collisions with magic_enum
namespace nano::enum_util
{
std::string_view name (auto value)
{
auto name = magic_enum::enum_name (value);
debug_assert (!name.empty ());
release_assert (name.size () < 64); // Safety check
return name;
}

/**
* Same as `magic_enum::enum_values (...)` but ignores reserved values (starting with underscore).
*/
template <class E, bool ignore_reserved = true>
std::vector<E> const & values ()
{
static std::vector<E> all = [] () {
std::vector<E> result;
for (auto const & [val, name] : magic_enum::enum_entries<E> ())
{
if (!ignore_reserved || !name.starts_with ('_'))
{
result.push_back (val);
}
}
return result;
}();
return all;
}

/**
* Same as `magic_enum::enum_cast (...)` but ignores reserved values (starting with underscore) by default.
* Case insensitive.
*/
template <class E>
std::optional<E> try_parse (std::string_view name, bool ignore_reserved = true)
{
if (ignore_reserved && name.starts_with ('_'))
{
return std::nullopt;
}
else
{
return magic_enum::enum_cast<E> (name, magic_enum::case_insensitive);
}
}

/**
* Same as `magic_enum::enum_cast (...)` but ignores reserved values (starting with underscore) by default.
* Case insensitive.
* @throws std::invalid_argument if the name is not found
*/
template <class E>
E parse (std::string_view name, bool ignore_reserved = true)
{
auto value = try_parse<E> (name, ignore_reserved);
if (value)
{
return *value;
}
throw std::invalid_argument ("Invalid value of " + std::string{ magic_enum::enum_type_name<E> () } + ": \"" + std::string{ name } + "\"");
}

template <typename T, typename S>
consteval void ensure_all_castable ()
{
for (auto value : magic_enum::enum_values<S> ())
{
if (!magic_enum::enum_cast<T> (magic_enum::enum_name (value)))
{
// If this fails, it means that the target enum is missing a value present in the source enum
throw std::logic_error ("Value of " + std::string{ magic_enum::enum_type_name<S> () } + " (" + std::string{ magic_enum::enum_name (value) } + ") cannot be cast to " + std::string{ magic_enum::enum_type_name<T> () });
}
}
}

template <class T, class S>
T cast (S value)
{
ensure_all_castable<T, S> ();

auto conv = magic_enum::enum_cast<T> (nano::enum_util::name (value));
debug_assert (conv);
return conv.value_or (T{});
}
}
2 changes: 1 addition & 1 deletion nano/lib/logging.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#include <nano/lib/config.hpp>
#include <nano/lib/enum_util.hpp>
#include <nano/lib/env.hpp>
#include <nano/lib/logging.hpp>
#include <nano/lib/logging_enums.hpp>
#include <nano/lib/utility.hpp>

#include <fmt/chrono.h>
#include <magic_enum.hpp>
#include <spdlog/pattern_formatter.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
Expand Down
73 changes: 22 additions & 51 deletions nano/lib/logging_enums.cpp
Original file line number Diff line number Diff line change
@@ -1,113 +1,84 @@
#include <nano/lib/enum_util.hpp>
#include <nano/lib/logging_enums.hpp>
#include <nano/lib/utility.hpp>

#include <magic_enum.hpp>

std::string_view nano::log::to_string (nano::log::type tag)
{
return magic_enum::enum_name (tag);
return nano::enum_util::name (tag);
}

std::string_view nano::log::to_string (nano::log::detail detail)
{
return magic_enum::enum_name (detail);
return nano::enum_util::name (detail);
}

std::string_view nano::log::to_string (nano::log::level level)
{
return magic_enum::enum_name (level);
return nano::enum_util::name (level);
}

const std::vector<nano::log::level> & nano::log::all_levels ()
{
static std::vector<nano::log::level> all = [] () {
return nano::util::enum_values<nano::log::level> ();
}();
return all;
return nano::enum_util::values<nano::log::level> ();
}

const std::vector<nano::log::type> & nano::log::all_types ()
{
static std::vector<nano::log::type> all = [] () {
return nano::util::enum_values<nano::log::type> ();
}();
return all;
return nano::enum_util::values<nano::log::type> ();
}

nano::log::level nano::log::parse_level (std::string_view name)
{
auto value = nano::util::parse_enum<nano::log::level> (name);
auto value = nano::enum_util::try_parse<nano::log::level> (name);
if (value.has_value ())
{
return value.value ();
}
else
{
auto all_levels_str = nano::util::join (nano::log::all_levels (), ", ", [] (auto const & lvl) {
return to_string (lvl);
});

throw std::invalid_argument ("Invalid log level: " + std::string (name) + ". Must be one of: " + all_levels_str);
}
auto all_levels_str = nano::util::join (nano::log::all_levels (), ", ", [] (auto const & lvl) {
return to_string (lvl);
});
throw std::invalid_argument ("Invalid log level: " + std::string (name) + ". Must be one of: " + all_levels_str);
}

nano::log::type nano::log::parse_type (std::string_view name)
{
auto value = nano::util::parse_enum<nano::log::type> (name);
auto value = nano::enum_util::try_parse<nano::log::type> (name);
if (value.has_value ())
{
return value.value ();
}
else
{
throw std::invalid_argument ("Invalid log type: " + std::string (name));
}
throw std::invalid_argument ("Invalid log type: " + std::string (name));
}

nano::log::detail nano::log::parse_detail (std::string_view name)
{
auto value = nano::util::parse_enum<nano::log::detail> (name);
auto value = nano::enum_util::try_parse<nano::log::detail> (name);
if (value.has_value ())
{
return value.value ();
}
else
{
throw std::invalid_argument ("Invalid log detail: " + std::string (name));
}
throw std::invalid_argument ("Invalid log detail: " + std::string (name));
}

std::string_view nano::log::to_string (nano::log::tracing_format format)
{
return magic_enum::enum_name (format);
return nano::enum_util::name (format);
}

nano::log::tracing_format nano::log::parse_tracing_format (std::string_view name)
{
auto value = magic_enum::enum_cast<nano::log::tracing_format> (name);
auto value = nano::enum_util::try_parse<nano::log::tracing_format> (name);
if (value.has_value ())
{
return value.value ();
}
else
{
auto all_formats_str = nano::util::join (nano::log::all_tracing_formats (), ", ", [] (auto const & fmt) {
return to_string (fmt);
});

throw std::invalid_argument ("Invalid tracing format: " + std::string (name) + ". Must be one of: " + all_formats_str);
}
auto all_formats_str = nano::util::join (nano::log::all_tracing_formats (), ", ", [] (auto const & fmt) {
return to_string (fmt);
});
throw std::invalid_argument ("Invalid tracing format: " + std::string (name) + ". Must be one of: " + all_formats_str);
}

const std::vector<nano::log::tracing_format> & nano::log::all_tracing_formats ()
{
static std::vector<nano::log::tracing_format> all = [] () {
std::vector<nano::log::tracing_format> result;
for (auto const & fmt : magic_enum::enum_values<nano::log::tracing_format> ())
{
result.push_back (fmt);
}
return result;
}();
return all;
return nano::enum_util::values<nano::log::tracing_format> ();
}
11 changes: 5 additions & 6 deletions nano/lib/stats_enums.cpp
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
#include <nano/lib/enum_util.hpp>
#include <nano/lib/stats_enums.hpp>

#include <magic_enum.hpp>

std::string_view nano::to_string (nano::stat::type type)
{
return magic_enum::enum_name (type);
return nano::enum_util::name (type);
}

std::string_view nano::to_string (nano::stat::detail detail)
{
return magic_enum::enum_name (detail);
return nano::enum_util::name (detail);
}

std::string_view nano::to_string (nano::stat::dir dir)
{
return magic_enum::enum_name (dir);
return nano::enum_util::name (dir);
}

std::string_view nano::to_string (nano::stat::sample sample)
{
return magic_enum::enum_name (sample);
return nano::enum_util::name (sample);
}
5 changes: 2 additions & 3 deletions nano/lib/thread_roles.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#include <nano/lib/enum_util.hpp>
#include <nano/lib/thread_roles.hpp>
#include <nano/lib/utility.hpp>

#include <magic_enum.hpp>

std::string_view nano::thread_role::to_string (nano::thread_role::name name)
{
return magic_enum::enum_name (name);
return nano::enum_util::name (name);
}

std::string nano::thread_role::get_string (nano::thread_role::name role)
Expand Down
Loading
Loading