diff --git a/headers/enum.hpp/enum.hpp b/headers/enum.hpp/enum.hpp index 3bf34bf..e48b4aa 100644 --- a/headers/enum.hpp/enum.hpp +++ b/headers/enum.hpp/enum.hpp @@ -6,6 +6,141 @@ #pragma once +#include + +#include +#include + namespace enum_hpp { + class exception final : public std::runtime_error { + public: + explicit exception(const char* what) + : std::runtime_error(what) {} + }; } + +// +// ENUM_HPP_GENERATE_INTERNAL_NAMES +// + +#define ENUM_HPP_GENERATE_INTERNAL_NAMES(Fields) + +// +// ENUM_HPP_GENERATE_ENUM_FIELDS +// + +#define ENUM_HPP_GENERATE_ENUM_FIELDS(Fields) + +// +// ENUM_HPP_GENERATE_VALUES +// + +#define ENUM_HPP_GENERATE_VALUES(Enum, Fields)\ + Enum(0), + +// +// ENUM_HPP_GENERATE_NAMES +// + +#define ENUM_HPP_GENERATE_NAMES(Fields)\ + "", + +// +// ENUM_HPP_ENUM_CLASS +// + +#define ENUM_HPP_ENUM_CLASS(Enum, Type, Fields)\ + enum class Enum : Type {\ + ENUM_HPP_GENERATE_ENUM_FIELDS(Fields)\ + };\ + struct Enum##_traits {\ + private:\ + enum enum_names_for_this_score_ { ENUM_HPP_GENERATE_INTERNAL_NAMES(Fields) };\ + public:\ + static constexpr std::size_t size = ENUM_HPP_PP_SEQ_SIZE(Fields);\ + static constexpr const Enum values[] = { ENUM_HPP_GENERATE_VALUES(Enum, Fields) }; \ + static constexpr const std::string_view names[] = { ENUM_HPP_GENERATE_NAMES(Fields) };\ + public:\ + static constexpr std::string_view to_string(Enum e) noexcept {\ + for ( std::size_t i = 0; i < size; ++i) {\ + if ( e == values[i] ) {\ + return names[i];\ + }\ + }\ + return std::string_view();\ + }\ + \ + static Enum from_string(std::string_view name){\ + for ( std::size_t i = 0; i < size; ++i) {\ + if ( name == names[i] ) {\ + return values[i];\ + }\ + }\ + throw ::enum_hpp::exception(#Enum "_traits::from_string(): invalid argument");\ + }\ + \ + static constexpr bool from_string_nothrow(std::string_view name, Enum& result) noexcept {\ + for ( std::size_t i = 0; i < size; ++i) {\ + if ( name == names[i] ) {\ + result = values[i];\ + return true;\ + }\ + }\ + return false;\ + }\ + }; + +// ----------------------------------------------------------------------------- +// +// ENUM_HPP_PP +// +// ----------------------------------------------------------------------------- + +// +// ENUM_HPP_PP_CAT +// + +#define ENUM_HPP_PP_CAT(x, y) ENUM_HPP_PP_CAT_I(x, y) +#define ENUM_HPP_PP_CAT_I(x, y) x ## y + +// +// ENUM_HPP_PP_APPLY +// + +#define ENUM_HPP_PP_APPLY(m, ...)\ + m(__VA_ARGS__) + +// +// ENUM_HPP_PP_SEQ_HEAD +// + +#define ENUM_HPP_PP_SEQ_HEAD(Seq) ENUM_HPP_PP_SEQ_HEAD_I(ENUM_HPP_PP_SEQ_HEAD_II Seq) +#define ENUM_HPP_PP_SEQ_HEAD_I(x) ENUM_HPP_PP_SEQ_HEAD_III(x) +#define ENUM_HPP_PP_SEQ_HEAD_II(x) x, ENUM_HPP_PP_SEQ_NOTHING +#define ENUM_HPP_PP_SEQ_HEAD_III(x, _) x + +// +// ENUM_HPP_PP_SEQ_TAIL +// + +#define ENUM_HPP_PP_SEQ_TAIL(Seq) ENUM_HPP_PP_SEQ_TAIL_I Seq +#define ENUM_HPP_PP_SEQ_TAIL_I(_) + +// +// ENUM_HPP_PP_SEQ_SIZE +// + +#define ENUM_HPP_PP_SEQ_SIZE(Seq)\ + ENUM_HPP_PP_CAT(ENUM_HPP_PP_SEQ_SIZE_, ENUM_HPP_PP_SEQ_SIZE_0 Seq) + +#define ENUM_HPP_PP_SEQ_SIZE_0(_) ENUM_HPP_PP_SEQ_SIZE_1 +#define ENUM_HPP_PP_SEQ_SIZE_1(_) ENUM_HPP_PP_SEQ_SIZE_2 +#define ENUM_HPP_PP_SEQ_SIZE_2(_) ENUM_HPP_PP_SEQ_SIZE_3 +#define ENUM_HPP_PP_SEQ_SIZE_3(_) ENUM_HPP_PP_SEQ_SIZE_4 + +#define ENUM_HPP_PP_SEQ_SIZE_ENUM_HPP_PP_SEQ_SIZE_0 0 +#define ENUM_HPP_PP_SEQ_SIZE_ENUM_HPP_PP_SEQ_SIZE_1 1 +#define ENUM_HPP_PP_SEQ_SIZE_ENUM_HPP_PP_SEQ_SIZE_2 2 +#define ENUM_HPP_PP_SEQ_SIZE_ENUM_HPP_PP_SEQ_SIZE_3 3 +#define ENUM_HPP_PP_SEQ_SIZE_ENUM_HPP_PP_SEQ_SIZE_4 4 diff --git a/untests/enum_tests.cpp b/untests/enum_tests.cpp index 76cd66c..628bdcc 100644 --- a/untests/enum_tests.cpp +++ b/untests/enum_tests.cpp @@ -9,5 +9,166 @@ #include -TEST_CASE("enum") { +#include +#include +#include + +using namespace std::string_view_literals; + +namespace +{ + template < typename Enum > + constexpr std::underlying_type_t enum_to_underlying(Enum e) noexcept { + return static_cast>(e); + } +} + +namespace some_namespace +{ + ENUM_HPP_ENUM_CLASS(color, unsigned, + (red = 2) + (green) + (blue = red + 4) + ) + + struct render { + ENUM_HPP_ENUM_CLASS(mask, unsigned char, + (none) + (color = 1 << 0) + (alpha = 1 << 1) + (all = color | alpha) + ) + }; +} + +TEST_CASE("enum_pp") { + SECTION("seq_head") { + REQUIRE("head"sv == ENUM_HPP_PP_SEQ_HEAD(("head"))); + REQUIRE("head"sv == ENUM_HPP_PP_SEQ_HEAD(("head")("tail"))); + REQUIRE("head"sv == ENUM_HPP_PP_SEQ_HEAD(("head")("tail1")("tail2"))); + } + + SECTION("seq_tail") { + const auto tail0 = [](){ return true; }; + REQUIRE(tail0 ENUM_HPP_PP_SEQ_TAIL(("head"))); + + const auto tail1 = [](auto t){ + REQUIRE("tail"sv == t); + return true; + }; + REQUIRE(tail1 ENUM_HPP_PP_SEQ_TAIL(("head")("tail"))); + + const auto tail2 = [](auto t1){ + REQUIRE("tail1"sv == t1); + return [](auto t2){ + REQUIRE("tail2"sv == t2); + return true; + }; + }; + REQUIRE(tail2 ENUM_HPP_PP_SEQ_TAIL(("head")("tail1")("tail2"))); + } + + SECTION("seq_size") { + STATIC_REQUIRE(ENUM_HPP_PP_SEQ_SIZE() == 0); + STATIC_REQUIRE(ENUM_HPP_PP_SEQ_SIZE((a)) == 1); + STATIC_REQUIRE(ENUM_HPP_PP_SEQ_SIZE((a)(b)) == 2); + STATIC_REQUIRE(ENUM_HPP_PP_SEQ_SIZE((a)(b)(c)) == 3); + } +} + +TEST_CASE("enum") { + namespace sn = some_namespace; + + SECTION("declaration") { + { + STATIC_REQUIRE(std::is_same_v< + std::underlying_type_t, + unsigned>); + + // REQUIRE(enum_to_underlying(sn::color::red) == 2); + // REQUIRE(enum_to_underlying(sn::color::green) == 3); + // REQUIRE(enum_to_underlying(sn::color::blue) == 6); + } + { + STATIC_REQUIRE(std::is_same_v< + std::underlying_type_t, + unsigned char>); + + // REQUIRE(enum_to_underlying(sn::render::mask::none) == 0); + // REQUIRE(enum_to_underlying(sn::render::mask::color) == 1); + // REQUIRE(enum_to_underlying(sn::render::mask::alpha) == 2); + // REQUIRE(enum_to_underlying(sn::render::mask::all) == 3); + } + } + + SECTION("size") { + { + REQUIRE(sn::color_traits::size == 3); + // REQUIRE(sn::color_traits::size == std::size(sn::color_traits::names)); + // REQUIRE(sn::color_traits::size == std::size(sn::color_traits::values)); + } + { + REQUIRE(sn::render::mask_traits::size == 4); + // REQUIRE(sn::render::mask_traits::size == std::size(sn::render::mask_traits::names)); + // REQUIRE(sn::render::mask_traits::size == std::size(sn::render::mask_traits::values)); + } + } + + SECTION("to_string") { + { + // STATIC_REQUIRE(sn::color_traits::to_string(sn::color::red) == "red"); + // STATIC_REQUIRE(sn::color_traits::to_string(sn::color::green) == "green"); + // STATIC_REQUIRE(sn::color_traits::to_string(sn::color::blue) == "blue"); + // STATIC_REQUIRE(sn::color_traits::to_string(sn::color(42)) == ""); + } + { + // STATIC_REQUIRE(sn::render::mask_traits_traits::to_string(sn::render::mask::none) == "none"); + // STATIC_REQUIRE(sn::render::mask_traits_traits::to_string(sn::render::mask::color) == "color"); + // STATIC_REQUIRE(sn::render::mask_traits_traits::to_string(sn::render::mask::alpha) == "alpha"); + // STATIC_REQUIRE(sn::render::mask_traits_traits::to_string(sn::render::mask::all) == "all"); + } + } + + SECTION("from_string") { + { + // REQUIRE(sn::color_traits::from_string("red") == sn::color::red); + // REQUIRE(sn::color_traits::from_string("green") == sn::color::green); + // REQUIRE(sn::color_traits::from_string("blue") == sn::color::blue); + // REQUIRE_THROWS_AS(sn::color_traits::from_string("42"), enum_hpp::exception); + } + { + // REQUIRE(sn::render::mask_traits_traits::from_string("none") == sn::render::mask_traits::none); + // REQUIRE(sn::render::mask_traits_traits::from_string("color") == sn::render::mask_traits::color); + // REQUIRE(sn::render::mask_traits_traits::from_string("alpha") == sn::render::mask_traits::alpha); + // REQUIRE(sn::render::mask_traits_traits::from_string("all") == sn::render::mask_traits::all); + // REQUIRE_THROWS_AS(sn::render::mask_traits_traits::from_string("42"), enum_hpp::exception); + } + } + + SECTION("from_string_nothrow") { + { + // sn::color result{42}; + // STATIC_REQUIRE(sn::color_traits::from_string_nothrow("red", result)); + // STATIC_REQUIRE(result == sn::color::red); + // STATIC_REQUIRE(sn::color_traits::from_string_nothrow("green", result)); + // STATIC_REQUIRE(result == sn::color::green); + // STATIC_REQUIRE(sn::color_traits::from_string_nothrow("blue", result)); + // STATIC_REQUIRE(result == sn::color::blue); + // STATIC_REQUIRE_FALSE(sn::color_traits::from_string_nothrow("42", result)); + // STATIC_REQUIRE(result == sn::color::blue); + } + { + // sn::render::mask result{42}; + // STATIC_REQUIRE(sn::render::mask_traits::from_string_nothrow("none", result)); + // STATIC_REQUIRE(result == sn::render::mask::none); + // STATIC_REQUIRE(sn::render::mask_traits::from_string_nothrow("color", result)); + // STATIC_REQUIRE(result == sn::render::mask::color); + // STATIC_REQUIRE(sn::render::mask_traits::from_string_nothrow("alpha", result)); + // STATIC_REQUIRE(result == sn::render::mask::alpha); + // STATIC_REQUIRE(sn::render::mask_traits::from_string_nothrow("all", result)); + // STATIC_REQUIRE(result == sn::render::mask::all); + // STATIC_REQUIRE_FALSE(sn::render::mask_traits::from_string_nothrow("42", result)); + // STATIC_REQUIRE(result == sn::render::mask::all); + } + } }