diff --git a/nano/core_test/enums.cpp b/nano/core_test/enums.cpp index 53b25a6d3..0ea1c7144 100644 --- a/nano/core_test/enums.cpp +++ b/nano/core_test/enums.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -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 (); + 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 (); + 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 ("one"), test_enum::one); + ASSERT_EQ (nano::enum_util::try_parse ("two"), test_enum::two); + ASSERT_EQ (nano::enum_util::try_parse ("three"), test_enum::three); + ASSERT_FALSE (nano::enum_util::try_parse ("four").has_value ()); + ASSERT_FALSE (nano::enum_util::try_parse ("_invalid").has_value ()); + ASSERT_FALSE (nano::enum_util::try_parse ("_last").has_value ()); + + ASSERT_NO_THROW (nano::enum_util::parse ("one")); + ASSERT_THROW (nano::enum_util::parse ("four"), std::invalid_argument); + ASSERT_THROW (nano::enum_util::parse ("_invalid"), std::invalid_argument); +} + +TEST (enum_util, cast) +{ + ASSERT_EQ (nano::enum_util::cast (test_enum2::one), test_enum::one); } \ No newline at end of file diff --git a/nano/lib/block_type.cpp b/nano/lib/block_type.cpp index 4afb9b0fd..70ec28b40 100644 --- a/nano/lib/block_type.cpp +++ b/nano/lib/block_type.cpp @@ -1,10 +1,9 @@ #include - -#include +#include 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) diff --git a/nano/lib/blocks.cpp b/nano/lib/blocks.cpp index 653ecd292..a4c21b867 100644 --- a/nano/lib/blocks.cpp +++ b/nano/lib/blocks.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -11,7 +12,6 @@ #include #include -#include size_t constexpr nano::send_block::size; size_t constexpr nano::receive_block::size; diff --git a/nano/lib/enum_util.hpp b/nano/lib/enum_util.hpp new file mode 100644 index 000000000..2190a3c16 --- /dev/null +++ b/nano/lib/enum_util.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include + +#include +#include + +namespace nano +{ +/** + * Array indexable by enum values + */ +template +using enum_array = magic_enum::containers::array; +} + +// 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 +std::vector const & values () +{ + static std::vector all = [] () { + std::vector result; + for (auto const & [val, name] : magic_enum::enum_entries ()) + { + 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 +std::optional 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 (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 +E parse (std::string_view name, bool ignore_reserved = true) +{ + auto value = try_parse (name, ignore_reserved); + if (value) + { + return *value; + } + throw std::invalid_argument ("Invalid value of " + std::string{ magic_enum::enum_type_name () } + ": \"" + std::string{ name } + "\""); +} + +template +consteval void ensure_all_castable () +{ + for (auto value : magic_enum::enum_values ()) + { + if (!magic_enum::enum_cast (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 () } + " (" + std::string{ magic_enum::enum_name (value) } + ") cannot be cast to " + std::string{ magic_enum::enum_type_name () }); + } + } +} + +template +T cast (S value) +{ + ensure_all_castable (); + + auto conv = magic_enum::enum_cast (nano::enum_util::name (value)); + debug_assert (conv); + return conv.value_or (T{}); +} +} \ No newline at end of file diff --git a/nano/lib/logging.cpp b/nano/lib/logging.cpp index be90deb36..da7f6e8dc 100644 --- a/nano/lib/logging.cpp +++ b/nano/lib/logging.cpp @@ -1,11 +1,11 @@ #include +#include #include #include #include #include #include -#include #include #include #include diff --git a/nano/lib/logging_enums.cpp b/nano/lib/logging_enums.cpp index 7111ca659..8ef883e21 100644 --- a/nano/lib/logging_enums.cpp +++ b/nano/lib/logging_enums.cpp @@ -1,113 +1,84 @@ +#include #include #include -#include - 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::all_levels () { - static std::vector all = [] () { - return nano::util::enum_values (); - }(); - return all; + return nano::enum_util::values (); } const std::vector & nano::log::all_types () { - static std::vector all = [] () { - return nano::util::enum_values (); - }(); - return all; + return nano::enum_util::values (); } nano::log::level nano::log::parse_level (std::string_view name) { - auto value = nano::util::parse_enum (name); + auto value = nano::enum_util::try_parse (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 (name); + auto value = nano::enum_util::try_parse (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 (name); + auto value = nano::enum_util::try_parse (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 (name); + auto value = nano::enum_util::try_parse (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::all_tracing_formats () { - static std::vector all = [] () { - std::vector result; - for (auto const & fmt : magic_enum::enum_values ()) - { - result.push_back (fmt); - } - return result; - }(); - return all; + return nano::enum_util::values (); } \ No newline at end of file diff --git a/nano/lib/stats_enums.cpp b/nano/lib/stats_enums.cpp index d58cf8fe2..d9f74084e 100644 --- a/nano/lib/stats_enums.cpp +++ b/nano/lib/stats_enums.cpp @@ -1,23 +1,22 @@ +#include #include -#include - 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); } \ No newline at end of file diff --git a/nano/lib/thread_roles.cpp b/nano/lib/thread_roles.cpp index ebd26a73f..32dc0e2ba 100644 --- a/nano/lib/thread_roles.cpp +++ b/nano/lib/thread_roles.cpp @@ -1,11 +1,10 @@ +#include #include #include -#include - 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) diff --git a/nano/lib/utility.hpp b/nano/lib/utility.hpp index 321cfccdb..35d19892d 100644 --- a/nano/lib/utility.hpp +++ b/nano/lib/utility.hpp @@ -14,9 +14,6 @@ #include #include -#include -#include - namespace boost { namespace system @@ -58,12 +55,6 @@ namespace program_options namespace nano { -/** - * Array indexable by enum values - */ -template -using enum_array = magic_enum::containers::array; - /* 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 (val); } - -/** - * Same as `magic_enum::enum_values (...)` but ignores reserved values (starting with underscore) - */ -template -std::vector enum_values () -{ - std::vector result; - for (auto const & [val, name] : magic_enum::enum_entries ()) - { - 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 -std::optional parse_enum (std::string_view name) -{ - if (name.starts_with ('_')) - { - return std::nullopt; - } - else - { - return magic_enum::enum_cast (name, magic_enum::case_insensitive); - } -} } \ No newline at end of file diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 1fd12ae26..dcff01a67 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 1ee92ae2f..c5ec31dca 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -11,8 +12,6 @@ #include -#include - /* * block_processor::context */ @@ -467,14 +466,12 @@ std::unique_ptr 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 (magic_enum::enum_name (type)); - debug_assert (value); - return value.value_or (nano::stat::detail{}); + return nano::enum_util::cast (type); } /* diff --git a/nano/node/election.cpp b/nano/node/election.cpp index acc973655..e588352a2 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -8,8 +9,6 @@ #include #include -#include - 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 (magic_enum::enum_name (behavior)); - debug_assert (value); - return value.value_or (nano::stat::detail{}); + return nano::enum_util::cast (behavior); } std::string_view nano::to_string (nano::election_state state) { - return magic_enum::enum_name (state); + return nano::enum_util::name (state); } \ No newline at end of file diff --git a/nano/node/messages.cpp b/nano/node/messages.cpp index 39665a4d3..5a7bb5499 100644 --- a/nano/node/messages.cpp +++ b/nano/node/messages.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -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 (magic_enum::enum_name (type)); - debug_assert (value); - return value.value_or (nano::stat::detail{}); + return nano::enum_util::cast (type); } nano::log::detail nano::to_log_detail (nano::message_type type) { - auto value = magic_enum::enum_cast (magic_enum::enum_name (type)); - debug_assert (value); - return value.value_or (nano::log::detail{}); + return nano::enum_util::cast (type); } diff --git a/nano/node/rep_tiers.cpp b/nano/node/rep_tiers.cpp index df65aea88..f8593ba48 100644 --- a/nano/node/rep_tiers.cpp +++ b/nano/node/rep_tiers.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -5,8 +6,6 @@ #include #include -#include - 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::rep_tiers::collect_contain nano::stat::detail nano::to_stat_detail (nano::rep_tier tier) { - auto value = magic_enum::enum_cast (magic_enum::enum_name (tier)); - debug_assert (value); - return value.value_or (nano::stat::detail{}); + return nano::enum_util::cast (tier); } \ No newline at end of file diff --git a/nano/node/transport/message_deserializer.cpp b/nano/node/transport/message_deserializer.cpp index 174a875eb..52d725b54 100644 --- a/nano/node/transport/message_deserializer.cpp +++ b/nano/node/transport/message_deserializer.cpp @@ -1,8 +1,7 @@ +#include #include #include -#include - 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> () }, @@ -382,14 +381,16 @@ std::unique_ptr 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 (magic_enum::enum_name (status)); - debug_assert (value); - return value.value_or (nano::stat::detail{}); + return nano::enum_util::cast (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); } diff --git a/nano/node/transport/socket.cpp b/nano/node/transport/socket.cpp index 5747b03d1..4176bf2ca 100644 --- a/nano/node/transport/socket.cpp +++ b/nano/node/transport/socket.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -13,8 +14,6 @@ #include #include -#include - /* * 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); } diff --git a/nano/node/transport/tcp_listener.cpp b/nano/node/transport/tcp_listener.cpp index 7cf6a0b6e..044f3b5c4 100644 --- a/nano/node/transport/tcp_listener.cpp +++ b/nano/node/transport/tcp_listener.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -10,8 +11,6 @@ #include #include -#include - using namespace std::chrono_literals; /* @@ -595,6 +594,10 @@ std::unique_ptr 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) diff --git a/nano/secure/common.cpp b/nano/secure/common.cpp index 5ce28c208..28952a4c8 100644 --- a/nano/secure/common.cpp +++ b/nano/secure/common.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -16,7 +17,6 @@ #include #include -#include 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 (magic_enum::enum_name (code)); - debug_assert (value); - return value.value_or (nano::stat::detail{}); + return nano::enum_util::cast (code); } nano::stat::detail nano::to_stat_detail (nano::vote_source source) { - auto value = magic_enum::enum_cast (magic_enum::enum_name (source)); - debug_assert (value); - return value.value_or (nano::stat::detail{}); + return nano::enum_util::cast (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 (magic_enum::enum_name (code)); - debug_assert (value); - return value.value_or (nano::stat::detail{}); + return nano::enum_util::cast (code); }