Enum utils (#4601)

* Move to `enum_utils` header

* Add `enum_cast` helper

* Rename to `enum_parse`

* Add `ignore_reserved` flag

* Enum name helper

* Namespace

* Convert usages

* Simplify `enum_util::values ()`

* Parsing

* Enum casting

* Tests

* Naming consistency

* Fix
This commit is contained in:
Piotr Wójcik 2024-05-07 09:56:26 +02:00 committed by GitHub
commit 881adfdd53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 238 additions and 157 deletions

View file

@ -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>
@ -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);
}

View file

@ -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)

View file

@ -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>
@ -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;

103
nano/lib/enum_util.hpp Normal file
View file

@ -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{});
}
}

View file

@ -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>

View file

@ -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> ();
}

View file

@ -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);
}

View file

@ -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)

View file

@ -14,9 +14,6 @@
#include <sstream>
#include <vector>
#include <magic_enum.hpp>
#include <magic_enum_containers.hpp>
namespace boost
{
namespace system
@ -58,12 +55,6 @@ namespace program_options
namespace nano
{
/**
* Array indexable by enum values
*/
template <typename Index, typename Value>
using enum_array = magic_enum::containers::array<Index, Value>;
/* These containers are used to collect information about sequence containers.
* It makes use of the composite design pattern to collect information
* from sequence containers and sequence containers inside member variables.
@ -294,38 +285,4 @@ std::string to_str (T const & val)
{
return boost::lexical_cast<std::string> (val);
}
/**
* Same as `magic_enum::enum_values (...)` but ignores reserved values (starting with underscore)
*/
template <class E>
std::vector<E> enum_values ()
{
std::vector<E> result;
for (auto const & [val, name] : magic_enum::enum_entries<E> ())
{
if (!name.starts_with ('_'))
{
result.push_back (val);
}
}
return result;
}
/**
* Same as `magic_enum::enum_cast (...)` but ignores reserved values (starting with underscore).
* Case insensitive.
*/
template <class E>
std::optional<E> parse_enum (std::string_view name)
{
if (name.starts_with ('_'))
{
return std::nullopt;
}
else
{
return magic_enum::enum_cast<E> (name, magic_enum::case_insensitive);
}
}
}

View file

@ -1,5 +1,6 @@
#pragma once
#include <nano/lib/enum_util.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/node/election_behavior.hpp>
#include <nano/node/election_insertion_result.hpp>

View file

@ -1,4 +1,5 @@
#include <nano/lib/blocks.hpp>
#include <nano/lib/enum_util.hpp>
#include <nano/lib/threading.hpp>
#include <nano/lib/timer.hpp>
#include <nano/node/active_transactions.hpp>
@ -11,8 +12,6 @@
#include <utility>
#include <magic_enum.hpp>
/*
* block_processor::context
*/
@ -467,14 +466,12 @@ std::unique_ptr<nano::container_info_component> nano::block_processor::collect_c
std::string_view nano::to_string (nano::block_source source)
{
return magic_enum::enum_name (source);
return nano::enum_util::name (source);
}
nano::stat::detail nano::to_stat_detail (nano::block_source type)
{
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (type));
debug_assert (value);
return value.value_or (nano::stat::detail{});
return nano::enum_util::cast<nano::stat::detail> (type);
}
/*

View file

@ -1,4 +1,5 @@
#include <nano/lib/blocks.hpp>
#include <nano/lib/enum_util.hpp>
#include <nano/node/active_transactions.hpp>
#include <nano/node/confirmation_solicitor.hpp>
#include <nano/node/election.hpp>
@ -8,8 +9,6 @@
#include <nano/node/vote_generator.hpp>
#include <nano/secure/ledger.hpp>
#include <magic_enum.hpp>
using namespace std::chrono;
std::chrono::milliseconds nano::election::base_latency () const
@ -799,17 +798,15 @@ void nano::election_extended_status::operator() (nano::object_stream & obs) cons
std::string_view nano::to_string (nano::election_behavior behavior)
{
return magic_enum::enum_name (behavior);
return nano::enum_util::name (behavior);
}
nano::stat::detail nano::to_stat_detail (nano::election_behavior behavior)
{
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (behavior));
debug_assert (value);
return value.value_or (nano::stat::detail{});
return nano::enum_util::cast<nano::stat::detail> (behavior);
}
std::string_view nano::to_string (nano::election_state state)
{
return magic_enum::enum_name (state);
return nano::enum_util::name (state);
}

View file

@ -1,5 +1,6 @@
#include <nano/lib/blocks.hpp>
#include <nano/lib/config.hpp>
#include <nano/lib/enum_util.hpp>
#include <nano/lib/memory.hpp>
#include <nano/lib/stats_enums.hpp>
#include <nano/lib/stream.hpp>
@ -1965,19 +1966,15 @@ void nano::asc_pull_ack::frontiers_payload::operator() (nano::object_stream & ob
std::string_view nano::to_string (nano::message_type type)
{
return magic_enum::enum_name (type);
return nano::enum_util::name (type);
}
nano::stat::detail nano::to_stat_detail (nano::message_type type)
{
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (type));
debug_assert (value);
return value.value_or (nano::stat::detail{});
return nano::enum_util::cast<nano::stat::detail> (type);
}
nano::log::detail nano::to_log_detail (nano::message_type type)
{
auto value = magic_enum::enum_cast<nano::log::detail> (magic_enum::enum_name (type));
debug_assert (value);
return value.value_or (nano::log::detail{});
return nano::enum_util::cast<nano::log::detail> (type);
}

View file

@ -1,3 +1,4 @@
#include <nano/lib/enum_util.hpp>
#include <nano/lib/logging.hpp>
#include <nano/lib/thread_roles.hpp>
#include <nano/node/online_reps.hpp>
@ -5,8 +6,6 @@
#include <nano/secure/common.hpp>
#include <nano/secure/ledger.hpp>
#include <magic_enum.hpp>
using namespace std::chrono_literals;
nano::rep_tiers::rep_tiers (nano::ledger & ledger_a, nano::network_params & network_params_a, nano::online_reps & online_reps_a, nano::stats & stats_a, nano::logger & logger_a) :
@ -147,7 +146,5 @@ std::unique_ptr<nano::container_info_component> nano::rep_tiers::collect_contain
nano::stat::detail nano::to_stat_detail (nano::rep_tier tier)
{
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (tier));
debug_assert (value);
return value.value_or (nano::stat::detail{});
return nano::enum_util::cast<nano::stat::detail> (tier);
}

View file

@ -1,8 +1,7 @@
#include <nano/lib/enum_util.hpp>
#include <nano/node/node.hpp>
#include <nano/node/transport/message_deserializer.hpp>
#include <magic_enum.hpp>
nano::transport::message_deserializer::message_deserializer (nano::network_constants const & network_constants_a, nano::network_filter & publish_filter_a, nano::block_uniquer & block_uniquer_a, nano::vote_uniquer & vote_uniquer_a,
read_query read_op) :
read_buffer{ std::make_shared<std::vector<uint8_t>> () },
@ -382,14 +381,16 @@ std::unique_ptr<nano::asc_pull_ack> nano::transport::message_deserializer::deser
return {};
}
/*
*
*/
nano::stat::detail nano::transport::to_stat_detail (nano::transport::parse_status status)
{
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (status));
debug_assert (value);
return value.value_or (nano::stat::detail{});
return nano::enum_util::cast<nano::stat::detail> (status);
}
std::string_view nano::transport::to_string (nano::transport::parse_status status)
{
return magic_enum::enum_name (status);
return nano::enum_util::name (status);
}

View file

@ -1,5 +1,6 @@
#include <nano/boost/asio/bind_executor.hpp>
#include <nano/boost/asio/read.hpp>
#include <nano/lib/enum_util.hpp>
#include <nano/node/node.hpp>
#include <nano/node/transport/socket.hpp>
#include <nano/node/transport/transport.hpp>
@ -13,8 +14,6 @@
#include <memory>
#include <utility>
#include <magic_enum.hpp>
/*
* socket
*/
@ -530,10 +529,10 @@ std::size_t network_prefix)
std::string_view nano::transport::to_string (socket_type type)
{
return magic_enum::enum_name (type);
return nano::enum_util::name (type);
}
std::string_view nano::transport::to_string (socket_endpoint type)
{
return magic_enum::enum_name (type);
return nano::enum_util::name (type);
}

View file

@ -1,3 +1,4 @@
#include <nano/lib/enum_util.hpp>
#include <nano/lib/interval.hpp>
#include <nano/node/messages.hpp>
#include <nano/node/node.hpp>
@ -10,8 +11,6 @@
#include <memory>
#include <ranges>
#include <magic_enum.hpp>
using namespace std::chrono_literals;
/*
@ -595,6 +594,10 @@ std::unique_ptr<nano::container_info_component> nano::transport::tcp_listener::c
return composite;
}
/*
*
*/
nano::stat::dir nano::transport::tcp_listener::to_stat_dir (connection_type type)
{
switch (type)
@ -610,7 +613,7 @@ nano::stat::dir nano::transport::tcp_listener::to_stat_dir (connection_type type
std::string_view nano::transport::tcp_listener::to_string (connection_type type)
{
return magic_enum::enum_name (type);
return nano::enum_util::name (type);
}
nano::transport::socket_endpoint nano::transport::tcp_listener::to_socket_endpoint (connection_type type)

View file

@ -1,6 +1,7 @@
#include <nano/crypto_lib/random_pool.hpp>
#include <nano/lib/blocks.hpp>
#include <nano/lib/config.hpp>
#include <nano/lib/enum_util.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/timer.hpp>
#include <nano/lib/utility.hpp>
@ -16,7 +17,6 @@
#include <crypto/ed25519-donna/ed25519.h>
#include <cryptopp/words.h>
#include <magic_enum.hpp>
nano::networks nano::network_constants::active_network = nano::networks::ACTIVE_NETWORK;
@ -360,28 +360,26 @@ nano::block_hash const & nano::unchecked_key::key () const
return previous;
}
/*
*
*/
nano::stat::detail nano::to_stat_detail (nano::vote_code code)
{
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (code));
debug_assert (value);
return value.value_or (nano::stat::detail{});
return nano::enum_util::cast<nano::stat::detail> (code);
}
nano::stat::detail nano::to_stat_detail (nano::vote_source source)
{
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (source));
debug_assert (value);
return value.value_or (nano::stat::detail{});
return nano::enum_util::cast<nano::stat::detail> (source);
}
std::string_view nano::to_string (nano::block_status code)
{
return magic_enum::enum_name (code);
return nano::enum_util::name (code);
}
nano::stat::detail nano::to_stat_detail (nano::block_status code)
{
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (code));
debug_assert (value);
return value.value_or (nano::stat::detail{});
return nano::enum_util::cast<nano::stat::detail> (code);
}