From 8320a5ee7d8ffa4e60959c8de8ed9dcadecc7703 Mon Sep 17 00:00:00 2001 From: Brandon Michael Whitchurch Date: Fri, 27 Jan 2023 21:24:59 -0800 Subject: [PATCH 01/12] Enum things --- CHANGELOG.md | 9 +++- src/CMakeLists.txt | 1 + src/EnumUtils/CMakeLists.txt | 7 +++ src/EnumUtils/enumUtils.cpp | 0 src/EnumUtils/enumUtils.hpp | 79 ++++++++++++++++++++++++++++ src/EnumUtils/test/CMakeLists.txt | 20 +++++++ src/EnumUtils/test/enumUtilsTest.cpp | 34 ++++++++++++ 7 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 src/EnumUtils/CMakeLists.txt create mode 100644 src/EnumUtils/enumUtils.cpp create mode 100644 src/EnumUtils/enumUtils.hpp create mode 100644 src/EnumUtils/test/CMakeLists.txt create mode 100644 src/EnumUtils/test/enumUtilsTest.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index bd39095..6bb9f0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog -#[Unreleased](https://github.com/bwhitchurch/CubeTimer/compare/0.1.0...HEAD) +#[Unreleased](https://github.com/bwhitchurch/CubeTimer/compare/0.1.1...HEAD) + +## Miscellaneous + +- Enum things [`6575b6f`](https://github.com/bwhitchurch/CubeTimer/commit/6575b6f0c5b04ebe01838fb01754597988a7ae30) +#[0.1.1](https://github.com/bwhitchurch/CubeTimer/compare/0.1.0...0.1.1) ## New Features @@ -8,7 +13,7 @@ ## Minor Changes -- dev: some basic ci [`8573379`](https://github.com/bwhitchurch/CubeTimer/commit/8573379353ac600f34d8fe38a4d46ab47479b7aa) +- dev: some basic ci [`adde650`](https://github.com/bwhitchurch/CubeTimer/commit/adde6502800a7ca5425df555e5ffed572a7dab2f) ## Miscellaneous diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 89f5b80..64032b1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(EnumUtils) add_library(scrambler scrambler.cpp) target_include_directories(scrambler PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) set_target_properties(scrambler PROPERTIES PUBLIC_HEADER diff --git a/src/EnumUtils/CMakeLists.txt b/src/EnumUtils/CMakeLists.txt new file mode 100644 index 0000000..30adab3 --- /dev/null +++ b/src/EnumUtils/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(EnumUtils enumUtils.cpp) +target_include_directories(EnumUtils PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(EnumUtils PRIVATE project_options project_warnings) + +if(ENABLE_TESTING) + add_subdirectory(test) +endif() diff --git a/src/EnumUtils/enumUtils.cpp b/src/EnumUtils/enumUtils.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/EnumUtils/enumUtils.hpp b/src/EnumUtils/enumUtils.hpp new file mode 100644 index 0000000..956de86 --- /dev/null +++ b/src/EnumUtils/enumUtils.hpp @@ -0,0 +1,79 @@ +#ifndef ENUM_UTILS_HPP +#define ENUM_UTILS_HPP + +#define MAKE_ENUM(NAME,TYPE,...) \ + enum class NAME : TYPE {__VA_ARGS__}; + +#include +#include +#include +#include +#include +#include + +template < typename Enum > struct ReflectiveEnum { + private: + static std::vector< Enum > sequence_values; + static std::unordered_map< Enum, std::string > enum_names; + static std::unordered_map< std::string, Enum > names_to_enums; + public: + + ReflectiveEnum()= default; + + // cppcheck-suppress noExplicitConstructor ; because initializer list + // should support var = {l1, l2, l3} syntax. + ReflectiveEnum( + std::initializer_list< std::pair< Enum, std::string > > t_enum_name_list + ) + : ReflectiveEnum() { + for (const auto& enum_pair : t_enum_name_list) { + sequence_values.push_back(enum_pair.first); + enum_names.insert(enum_pair); + names_to_enums.insert({enum_pair.second, enum_pair.first}); + } + } + + Enum& operator[](const std::string& t_name) { + return names_to_enums[t_name]; + } + + const Enum& operator[](const std::string& t_name) const { + return names_to_enums.at(t_name); + } + + std::string& operator[](const Enum& t_val) { return enum_names[t_val]; } + + const std::string& operator[](const Enum& t_val) const { + return enum_names.at(t_val); + } + + typename std::vector< Enum >::iterator begin() { + return sequence_values.begin(); + } + + typename std::vector< Enum >::const_iterator begin() const { + return sequence_values.begin(); + } + + typename std::vector< Enum >::const_iterator cbegin() const { + return sequence_values.cbegin(); + } + + typename std::vector< Enum >::iterator end() { + return sequence_values.end(); + } + + typename std::vector< Enum >::const_iterator end() const { + return sequence_values.end(); + } + + typename std::vector< Enum >::const_iterator cend() const { + return sequence_values.cend(); + } + + friend std::ostream& operator << (std::ostream& t_os, const Enum& t_e_val); +}; + +template +std::ostream& operator << (std::ostream& t_os, const Enum& t_e_val); +#endif diff --git a/src/EnumUtils/test/CMakeLists.txt b/src/EnumUtils/test/CMakeLists.txt new file mode 100644 index 0000000..a6cb5f9 --- /dev/null +++ b/src/EnumUtils/test/CMakeLists.txt @@ -0,0 +1,20 @@ +add_executable(enumUtilsTest enumUtilsTest.cpp) +target_link_libraries(enumUtilsTest PUBLIC Catch2::Catch2WithMain EnumUtils + fmt::fmt) +target_link_libraries(enumUtilsTest PRIVATE project_options project_warnings) +set_target_properties(enumUtilsTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_TEST_OUTPUT_DIRECTORY}") + +include(Catch) +catch_discover_tests( + enumUtilsTest + TEST_PREFIX + "libEnumUtils." + REPORTER + XML + OUTPUT_DIR + . + OUTPUT_PREFIX + "libEnumUtils." + OUTPUT_SUFFIX + .xml) diff --git a/src/EnumUtils/test/enumUtilsTest.cpp b/src/EnumUtils/test/enumUtilsTest.cpp new file mode 100644 index 0000000..47521e4 --- /dev/null +++ b/src/EnumUtils/test/enumUtilsTest.cpp @@ -0,0 +1,34 @@ +#include "enumUtils.hpp" + +#include + +#include + +TEST_CASE("Reflective Enum Creation") {} + +TEST_CASE("Make Enum"){ + MAKE_ENUM(Color, int, YELLOW, ORANGE = 7, BLACK, GREY); + Color my_color = Color::YELLOW; + CHECK(my_color != Color::BLACK); +} + +TEST_CASE("Reflective Enum Use") { + enum class Color { + RED, + BLUE, + GREEN = 42, + PURPLE, + }; + + const ReflectiveEnum< Color > color_helper{ + {{Color::RED, "red"}, + {Color::BLUE, "blue"}, + {Color::GREEN, "green"}, + {Color::PURPLE, "purple"}} + }; + + CHECK(color_helper[Color::RED] == "red"); + CHECK(color_helper["blue"] == Color::BLUE); + + for (auto color : color_helper) { fmt::print("{}\n", color_helper[color]); } +} From 7d34190747b18cabc922cf4bad7c1ce38c7e5b6a Mon Sep 17 00:00:00 2001 From: Brandon Michael Whitchurch Date: Sat, 28 Jan 2023 20:43:41 -0800 Subject: [PATCH 02/12] feat: reflective enums through preprocessor shenanigans. --- CHANGELOG.md | 6 +- CMakeLists.txt | 3 + src/CMakeLists.txt | 1 + src/EnumUtils/CMakeLists.txt | 5 +- src/EnumUtils/enumUtils.hpp | 157 ++++++++++++++++----------- src/EnumUtils/test/enumUtilsTest.cpp | 40 +++---- src/ppUtils/CMakeLists.txt | 7 ++ src/ppUtils/ppUtils.hpp | 91 ++++++++++++++++ src/ppUtils/test/CMakeLists.txt | 20 ++++ src/ppUtils/test/ppUtilsTest.cpp | 44 ++++++++ 10 files changed, 284 insertions(+), 90 deletions(-) create mode 100644 src/ppUtils/CMakeLists.txt create mode 100644 src/ppUtils/ppUtils.hpp create mode 100644 src/ppUtils/test/CMakeLists.txt create mode 100644 src/ppUtils/test/ppUtilsTest.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bb9f0e..4478124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,13 @@ #[Unreleased](https://github.com/bwhitchurch/CubeTimer/compare/0.1.1...HEAD) +## New Features + +- feat: reflective enums through preprocessor shenanigans. [`671371a`](https://github.com/bwhitchurch/CubeTimer/commit/671371a80af9226e1778da5d8a71b2970a5b6d40) + ## Miscellaneous -- Enum things [`6575b6f`](https://github.com/bwhitchurch/CubeTimer/commit/6575b6f0c5b04ebe01838fb01754597988a7ae30) +- Enum things [`8320a5e`](https://github.com/bwhitchurch/CubeTimer/commit/8320a5ee7d8ffa4e60959c8de8ed9dcadecc7703) #[0.1.1](https://github.com/bwhitchurch/CubeTimer/compare/0.1.0...0.1.1) ## New Features diff --git a/CMakeLists.txt b/CMakeLists.txt index 464ad34..b5034d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,9 @@ dynamic_project_options( --suppress=unmatchedSuppression --suppress=passedByValue --suppress=syntaxError + --suppress=duplicateExpression + --suppress=knownConditionTrueFalse + --suppress=preprocessorErrorDirective --inconclusive) if(ENABLE_INCLUDE_WHAT_YOU_USE) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 64032b1..1d47584 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(ppUtils) add_subdirectory(EnumUtils) add_library(scrambler scrambler.cpp) target_include_directories(scrambler PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/EnumUtils/CMakeLists.txt b/src/EnumUtils/CMakeLists.txt index 30adab3..5793631 100644 --- a/src/EnumUtils/CMakeLists.txt +++ b/src/EnumUtils/CMakeLists.txt @@ -1,6 +1,9 @@ add_library(EnumUtils enumUtils.cpp) target_include_directories(EnumUtils PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(EnumUtils PRIVATE project_options project_warnings) +target_link_libraries( + EnumUtils + PUBLIC ppUtils + PRIVATE project_options project_warnings) if(ENABLE_TESTING) add_subdirectory(test) diff --git a/src/EnumUtils/enumUtils.hpp b/src/EnumUtils/enumUtils.hpp index 956de86..79634e4 100644 --- a/src/EnumUtils/enumUtils.hpp +++ b/src/EnumUtils/enumUtils.hpp @@ -1,79 +1,106 @@ #ifndef ENUM_UTILS_HPP #define ENUM_UTILS_HPP +#include "ppUtils.hpp" -#define MAKE_ENUM(NAME,TYPE,...) \ - enum class NAME : TYPE {__VA_ARGS__}; +#include +#include +#include +using namespace std::string_view_literals; -#include -#include -#include -#include -#include -#include +template < typename Type > struct IgnoreEquals { + Type value; -template < typename Enum > struct ReflectiveEnum { - private: - static std::vector< Enum > sequence_values; - static std::unordered_map< Enum, std::string > enum_names; - static std::unordered_map< std::string, Enum > names_to_enums; - public: - - ReflectiveEnum()= default; - - // cppcheck-suppress noExplicitConstructor ; because initializer list - // should support var = {l1, l2, l3} syntax. - ReflectiveEnum( - std::initializer_list< std::pair< Enum, std::string > > t_enum_name_list - ) - : ReflectiveEnum() { - for (const auto& enum_pair : t_enum_name_list) { - sequence_values.push_back(enum_pair.first); - enum_names.insert(enum_pair); - names_to_enums.insert({enum_pair.second, enum_pair.first}); - } + template < typename Any > + constexpr IgnoreEquals& operator=([[maybe_unused]] Any t_val) { + return *this; } - Enum& operator[](const std::string& t_name) { - return names_to_enums[t_name]; - } + constexpr explicit IgnoreEquals() : value() {} - const Enum& operator[](const std::string& t_name) const { - return names_to_enums.at(t_name); - } + constexpr explicit IgnoreEquals(Type t_value) : value(t_value) {} - std::string& operator[](const Enum& t_val) { return enum_names[t_val]; } + // NOLINTNEXTLINE(hicpp-explicit-conversions) + constexpr operator Type() const { return value; } - const std::string& operator[](const Enum& t_val) const { - return enum_names.at(t_val); + constexpr IgnoreEquals operator*(const Type& t_value) { + return IgnoreEquals(t_value); } - - typename std::vector< Enum >::iterator begin() { - return sequence_values.begin(); - } - - typename std::vector< Enum >::const_iterator begin() const { - return sequence_values.begin(); - } - - typename std::vector< Enum >::const_iterator cbegin() const { - return sequence_values.cbegin(); - } - - typename std::vector< Enum >::iterator end() { - return sequence_values.end(); - } - - typename std::vector< Enum >::const_iterator end() const { - return sequence_values.end(); - } - - typename std::vector< Enum >::const_iterator cend() const { - return sequence_values.cend(); - } - - friend std::ostream& operator << (std::ostream& t_os, const Enum& t_e_val); }; -template -std::ostream& operator << (std::ostream& t_os, const Enum& t_e_val); +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +#define MAKE_ENUM(enum_name, enum_type, ...) \ + enum enum_name : enum_type { __VA_ARGS__ }; + +#define NAME_ENTRY(arg) #arg##sv.substr(0, #arg##sv.find_first_of('=') - 1) + +#define MAKE_NAMES(array_prefix, enum_name, ...) \ + constexpr static std:: \ + array< std::string_view, MM_COUNT_ARGS(__VA_ARGS__) > \ + array_prefix##_names{{MM_TRANSFORM(NAME_ENTRY, __VA_ARGS__)}}; + +// NOLINTNEXTLINE(bugprone-macro-parentheses) +#define VALUE_ENTRY(enum_name, arg) IgnoreEquals< enum_name >{} * arg +#define MAKE_VALUES(array_prefix, enum_name, ...) \ + constexpr static std::array< enum_name, MM_COUNT_ARGS(__VA_ARGS__) > \ + array_prefix##_values{ \ + {MM_TRANSFORM_1(VALUE_ENTRY, enum_name, __VA_ARGS__)}}; + +#define BETTER_ENUM(enum_name, type, ...) \ + class enum_name { \ + public: \ + MAKE_ENUM(m_enumeration, type, __VA_ARGS__) \ + private: \ + MAKE_VALUES(m, m_enumeration, __VA_ARGS__) \ + MAKE_NAMES(m, m_enumeration, __VA_ARGS__) \ + constexpr static std::string_view m_name{#enum_name##sv}; \ + m_enumeration m_value; \ + \ + public: \ + using value_container = decltype(m_values); \ + using value_iterator = typename value_container::iterator; \ + using value_type = typename value_container::value_type; \ + using name_container = decltype(m_names); \ + using name_iterator = typename name_container::iterator; \ + using name_type = typename name_container::value_type; \ + using underlying = type; \ + \ + /* NOLINTNEXTLINE(hicpp-explicit-conversions)*/ \ + constexpr enum_name(const m_enumeration& t_val) : m_value(t_val) {} \ + \ + friend constexpr enum_name operator+(const m_enumeration& t_val); \ + \ + constexpr static value_container& values() { return m_values; } \ + constexpr static name_container& names() { return m_names; } \ + constexpr static size_t size() { return MM_COUNT_ARGS(__VA_ARGS__); } \ + constexpr static std::string_view name() { return m_name; } \ + constexpr static const value_type& from_string(std::string_view t_name \ + ) { \ + const auto* found_ptr = std::find_if( \ + m_names.begin(), \ + m_names.end(), \ + [t_name](auto t_iter_name) { return t_iter_name == t_name; } \ + ); \ + auto start_val = m_values.begin(); \ + std::advance( \ + start_val, std::distance(m_names.begin(), found_ptr) \ + ); \ + return *start_val; \ + } \ + constexpr name_type to_string() { \ + const auto* found_ptr = std::find_if( \ + m_values.begin(), \ + m_values.end(), \ + [this](auto t_iter_val) { return t_iter_val == m_value; } \ + ); \ + auto start_name = m_names.begin(); \ + std::advance( \ + start_name, std::distance(m_values.begin(), found_ptr) \ + ); \ + return *start_name; \ + } \ + }; \ + constexpr enum_name operator+(const enum_name::m_enumeration& t_val) { \ + return enum_name(t_val); \ + } \ +// NOLINTEND(cppcoreguidelines-macro-usage) #endif diff --git a/src/EnumUtils/test/enumUtilsTest.cpp b/src/EnumUtils/test/enumUtilsTest.cpp index 47521e4..6a3c15d 100644 --- a/src/EnumUtils/test/enumUtilsTest.cpp +++ b/src/EnumUtils/test/enumUtilsTest.cpp @@ -1,34 +1,28 @@ #include "enumUtils.hpp" #include +#include #include -TEST_CASE("Reflective Enum Creation") {} +TEST_CASE("ENUM MACROS") { + MAKE_ENUM(Color, int, RED, GREEN, BLUE = 3, ORANGE) -TEST_CASE("Make Enum"){ - MAKE_ENUM(Color, int, YELLOW, ORANGE = 7, BLACK, GREY); - Color my_color = Color::YELLOW; - CHECK(my_color != Color::BLACK); -} - -TEST_CASE("Reflective Enum Use") { - enum class Color { - RED, - BLUE, - GREEN = 42, - PURPLE, - }; + MAKE_NAMES(Color, Color, RED, GREEN, BLUE = 3, ORANGE) + fmt::print("{}\n", Color_names); + CHECK(Color_names[0] == "RED"); - const ReflectiveEnum< Color > color_helper{ - {{Color::RED, "red"}, - {Color::BLUE, "blue"}, - {Color::GREEN, "green"}, - {Color::PURPLE, "purple"}} - }; + MAKE_VALUES(Color, Color, RED, GREEN, BLUE = 3, ORANGE) + CHECK(Color_values[0] == RED); +} - CHECK(color_helper[Color::RED] == "red"); - CHECK(color_helper["blue"] == Color::BLUE); +BETTER_ENUM(Color, int, RED, GREEN, ORANGE = 7, YELLOW) - for (auto color : color_helper) { fmt::print("{}\n", color_helper[color]); } +TEST_CASE("BETTER_ENUM") { + CHECK(Color::values()[0] == Color::RED); + fmt::print("{}\n", Color::name()); + fmt::print("{}\n", Color::names()); + fmt::print("{}\n", Color::values()); + fmt::print("{}\n", Color::from_string("ORANGE")); + fmt::print("{}\n", (+Color::RED).to_string()); } diff --git a/src/ppUtils/CMakeLists.txt b/src/ppUtils/CMakeLists.txt new file mode 100644 index 0000000..4e58a0e --- /dev/null +++ b/src/ppUtils/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(ppUtils INTERFACE) +target_include_directories(ppUtils INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(ppUtils INTERFACE project_options project_warnings) +set_target_properties(ppUtils PROPERTIES PUBLIC_HEADER ppUtils.hpp) +if(ENABLE_TESTING) + add_subdirectory(test) +endif() diff --git a/src/ppUtils/ppUtils.hpp b/src/ppUtils/ppUtils.hpp new file mode 100644 index 0000000..d0ac442 --- /dev/null +++ b/src/ppUtils/ppUtils.hpp @@ -0,0 +1,91 @@ +#ifndef PP_UTILS_HPP +#define PP_UTILS_HPP +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +/****************************************************************************** + * Object Literals * + ******************************************************************************/ +#define T_PARENS () +#define T_LPAREN ( +#define T_RPAREN ) +#define T_COMMA , + +/****************************************************************************** + * Basic Functions * + ******************************************************************************/ +#define MM_ID(arg) arg +#define MM_CONCAT(arg_a, arg_b) arg_a##arg_b +#define MM_STRINGIFY(arg) #arg +#define MM_STRINGIFY_M(arg) MM_STRINGIFY(arg) + +/*! + * Macros that will cause unexpanded macros in __VA_ARGS__ to be expanded. + * Current max depth is 2^8 + */ +#define MM_EXPAND(...) MM_EXPAND_8(__VA_ARGS__) +#define MM_EXPAND_1(...) __VA_ARGS__ +#define MM_EXPAND_2(...) MM_EXPAND_1(MM_EXPAND_1(__VA_ARGS__)) +#define MM_EXPAND_3(...) MM_EXPAND_2(MM_EXPAND_2(__VA_ARGS__)) +#define MM_EXPAND_4(...) MM_EXPAND_3(MM_EXPAND_3(__VA_ARGS__)) +#define MM_EXPAND_5(...) MM_EXPAND_4(MM_EXPAND_4(__VA_ARGS__)) +#define MM_EXPAND_6(...) MM_EXPAND_5(MM_EXPAND_5(__VA_ARGS__)) +#define MM_EXPAND_7(...) MM_EXPAND_6(MM_EXPAND_6(__VA_ARGS__)) +#define MM_EXPAND_8(...) MM_EXPAND_7(MM_EXPAND_7(__VA_ARGS__)) + +/****************************************************************************** + * High Order Macros * + ******************************************************************************/ +/*! + * Applies macro to each argument int the trailing argument list + */ +#define MM_FOR_EACH(macro, ...) \ + __VA_OPT__(MM_EXPAND(MM_FOR_EACH_HELPER(macro, __VA_ARGS__))) +#define MM_FOR_EACH_HELPER(macro, _1, ...) \ + macro(_1) __VA_OPT__(MM_FOR_EACH_AGAIN T_PARENS(macro, __VA_ARGS__)) +#define MM_FOR_EACH_AGAIN() MM_FOR_EACH_HELPER + +#define MM_TRANSFORM(macro, ...) \ + __VA_OPT__(MM_EXPAND(MM_TRANSFORM_HELPER(macro, __VA_ARGS__))) +#define MM_TRANSFORM_HELPER(macro, _1, ...) \ + macro(_1) __VA_OPT__( \ + T_COMMA MM_TRANSFORM_AGAIN T_PARENS T_LPAREN macro, \ + __VA_ARGS__ T_RPAREN \ + ) +#define MM_TRANSFORM_AGAIN() MM_TRANSFORM_HELPER + +#define MM_TRANSFORM_1(macro, ...) \ + __VA_OPT__(MM_EXPAND(MM_TRANSFORM_HELPER_1(macro, __VA_ARGS__))) +#define MM_TRANSFORM_HELPER_1(macro, _1, _2, ...) \ + macro(_1, _2) __VA_OPT__( \ + T_COMMA MM_TRANSFORM_AGAIN_1 T_PARENS T_LPAREN macro, \ + _1, \ + __VA_ARGS__ T_RPAREN \ + ) +#define MM_TRANSFORM_AGAIN_1() MM_TRANSFORM_HELPER_1 +/*! + * Calls macro(_1, arg) for each arg in the variadic argument list + */ +#define MM_FOR_EACH_1(macro, _1, ...) \ + __VA_OPT__(MM_EXPAND(MM_FOR_EACH_HELPER_1(macro, _1, __VA_ARGS__))) +#define MM_FOR_EACH_HELPER_1(macro, _1, _2, ...) \ + macro(_1, _2) \ + __VA_OPT__(MM_FOR_EACH_AGAIN_1 T_PARENS(macro, _1, __VA_ARGS__)) +#define MM_FOR_EACH_AGAIN_1() MM_FOR_EACH_HELPER_1 + +/*! + * Calls macro(a,b) on the first two arguments in the variadic list. The next + * call is generated as macro(macro(a,b), c) and so on until all arguments are + * consumed. + */ +#define MM_FOLD_LEFT(macro, ...) \ + __VA_OPT__(MM_EXPAND(MM_FOLD_LEFT_HELPER(macro, __VA_ARGS__))) +#define MM_FOLD_LEFT_HELPER(macro, _1, _2, ...) \ + __VA_OPT__(MM_FOLD_LEFT_AGAIN T_PARENS T_LPAREN macro, ) \ + macro(_1, _2) __VA_OPT__(, __VA_ARGS__ T_RPAREN) +#define MM_FOLD_LEFT_AGAIN() MM_FOLD_LEFT_HELPER + +#define MM_INCREMENT(arg) ((arg) + 1) +#define MM_COUNT_IF(sum, arg_b) MM_INCREMENT(sum) +#define MM_COUNT_ARGS(...) MM_FOLD_LEFT(MM_COUNT_IF, 0, __VA_ARGS__) + +// NOLINTEND(cppcoreguidelines-macro-usage) +#endif diff --git a/src/ppUtils/test/CMakeLists.txt b/src/ppUtils/test/CMakeLists.txt new file mode 100644 index 0000000..8151811 --- /dev/null +++ b/src/ppUtils/test/CMakeLists.txt @@ -0,0 +1,20 @@ +add_executable(ppUtilsTest ppUtilsTest.cpp) +target_link_libraries(ppUtilsTest PUBLIC Catch2::Catch2WithMain fmt::fmt + ppUtils) +target_link_libraries(ppUtilsTest PRIVATE project_options project_warnings) +set_target_properties(ppUtilsTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_TEST_OUTPUT_DIRECTORY}") + +include(Catch) +catch_discover_tests( + ppUtilsTest + TEST_PREFIX + "libEnumUtils." + REPORTER + XML + OUTPUT_DIR + . + OUTPUT_PREFIX + "libEnumUtils." + OUTPUT_SUFFIX + .xml) diff --git a/src/ppUtils/test/ppUtilsTest.cpp b/src/ppUtils/test/ppUtilsTest.cpp new file mode 100644 index 0000000..d6e334b --- /dev/null +++ b/src/ppUtils/test/ppUtilsTest.cpp @@ -0,0 +1,44 @@ +#include "ppUtils.hpp" + +#include + +#include + +TEST_CASE("Basic Function Macros", "[macro][preprocessor][pp]") { + SECTION("ID") { + SECTION("char input") { REQUIRE(MM_ID('a') == 'a'); } + SECTION("string input") { + REQUIRE(MM_ID(std::string("ppUtils")) == std::string("ppUtils")); + } + SECTION("integer input") { REQUIRE(MM_ID(1) == 1); } + SECTION("floating input") { REQUIRE(MM_ID(1.0) == 1.0); } + } + + SECTION("CONCAT") { + const int a12 = 8; + REQUIRE(MM_CONCAT(a, 12) == 8); + } + SECTION("EXPAND") { + CHECK( + std::string(MM_STRINGIFY_M(MM_ID(MM_ID T_PARENS 2.0))) + == std::string("MM_ID () 2.0") + ); + + CHECK(MM_EXPAND(MM_ID T_PARENS 2.0) == 2.0); + } + + SECTION("FOR_EACH") { + const std::string res = MM_STRINGIFY_M(MM_FOR_EACH(MM_ID, 1, 2, 3, 4, 5)); + REQUIRE(res == "1 2 3 4 5"); + } + + SECTION("FOLD") { + REQUIRE( + std::string(MM_STRINGIFY_M(MM_FOLD_LEFT(MM_CONCAT, a, b, c, d))) + == "abcd" + ); + } + SECTION("NARG") { + CHECK(MM_COUNT_ARGS(a, b, c, d) == 4); + } +} From 9b0f1a644d1720c5d9cd796d8867fd476183e103 Mon Sep 17 00:00:00 2001 From: Brandon Michael Whitchurch Date: Sun, 29 Jan 2023 12:42:03 -0800 Subject: [PATCH 03/12] Change: cube header makes use of enum utils. --- CHANGELOG.md | 3 +- src/CMakeLists.txt | 4 +- src/EnumUtils/enumUtils.hpp | 9 +- src/cube.hpp | 176 ++++++++++++++++++++++-------------- src/test/CMakeLists.txt | 20 ++++ src/test/cubeTest.cpp | 29 ++++++ 6 files changed, 167 insertions(+), 74 deletions(-) create mode 100644 src/test/cubeTest.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 4478124..fcbd9c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,11 @@ ## New Features -- feat: reflective enums through preprocessor shenanigans. [`671371a`](https://github.com/bwhitchurch/CubeTimer/commit/671371a80af9226e1778da5d8a71b2970a5b6d40) +- feat: reflective enums through preprocessor shenanigans. [`7d34190`](https://github.com/bwhitchurch/CubeTimer/commit/7d34190747b18cabc922cf4bad7c1ce38c7e5b6a) ## Miscellaneous +- Change: cube header makes use of enum utils. [`a2d456a`](https://github.com/bwhitchurch/CubeTimer/commit/a2d456a07469672d11545c4e1d312ee4434b995a) - Enum things [`8320a5e`](https://github.com/bwhitchurch/CubeTimer/commit/8320a5ee7d8ffa4e60959c8de8ed9dcadecc7703) #[0.1.1](https://github.com/bwhitchurch/CubeTimer/compare/0.1.0...0.1.1) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d47584..06c0b91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,8 +6,8 @@ set_target_properties(scrambler PROPERTIES PUBLIC_HEADER "scrambler.hpp;enum.hpp") target_link_libraries( scrambler - PUBLIC project_options project_warnings - PRIVATE fmt::fmt spdlog::spdlog) + PRIVATE project_options project_warnings + PUBLIC fmt::fmt spdlog::spdlog EnumUtils) target_include_directories( scrambler PRIVATE "${CMAKE_BINARY_DIR}/configured_files/include") diff --git a/src/EnumUtils/enumUtils.hpp b/src/EnumUtils/enumUtils.hpp index 79634e4..d14975f 100644 --- a/src/EnumUtils/enumUtils.hpp +++ b/src/EnumUtils/enumUtils.hpp @@ -5,6 +5,7 @@ #include #include #include +#include using namespace std::string_view_literals; template < typename Type > struct IgnoreEquals { @@ -66,6 +67,7 @@ template < typename Type > struct IgnoreEquals { \ /* NOLINTNEXTLINE(hicpp-explicit-conversions)*/ \ constexpr enum_name(const m_enumeration& t_val) : m_value(t_val) {} \ + constexpr operator underlying() const { return m_value; } \ \ friend constexpr enum_name operator+(const m_enumeration& t_val); \ \ @@ -99,8 +101,13 @@ template < typename Type > struct IgnoreEquals { return *start_name; \ } \ }; \ - constexpr enum_name operator+(const enum_name::m_enumeration& t_val) { \ + constexpr enum_name operator+(const enum_name::m_enumeration& t_val) { \ return enum_name(t_val); \ } \ + template <> struct std::hash< enum_name > { \ + size_t operator()(const enum_name& t_enum) const noexcept { \ + return std::hash< type >{}(t_enum); \ + } \ + }; // NOLINTEND(cppcoreguidelines-macro-usage) #endif diff --git a/src/cube.hpp b/src/cube.hpp index 02f9351..a34cd69 100644 --- a/src/cube.hpp +++ b/src/cube.hpp @@ -8,92 +8,128 @@ */ #ifndef CUBE_HPP #define CUBE_HPP -#include // for format_parse_context, formatter -#include // for uint8_t -#include // for size_t +#include "enumUtils.hpp" -#include "enum.hpp" // for BiEnum +#include // for size_t +#include +#include +#include // for uint8_t +#include +#include + +#include // for format_parse_context, formatter +#include // for format_parse_context, formatter /*! * @brief number of faces on a cube. */ constexpr size_t num_cube_faces = 6; -/*! - * @brief CubeFace enum labels for faces of the cube. - */ -enum class CubeFace : uint8_t { UP, DOWN, RIGHT, LEFT, FRONT, BACK }; +BETTER_ENUM(CubeFace, uint8_t, UP, DOWN, RIGHT, LEFT, FRONT, BACK) /*! - * @brief BiEnum for mapping from CubeFace labels to quarter-turn metric - * notation. + * @brief Number of different turns that each face can make */ -constexpr BiEnum< CubeFace, char, num_cube_faces > cube_face_notation{ - {{{CubeFace::UP, 'U'}, - {CubeFace::DOWN, 'D'}, - {CubeFace::RIGHT, 'R'}, - {CubeFace::LEFT, 'L'}, - {CubeFace::FRONT, 'F'}, - {CubeFace::BACK, 'B'}}}}; +constexpr size_t num_turns = 3; -/*! - * @brief formatter specialization for CubeFace enum class - */ -template <> struct fmt::formatter< CubeFace > { - /*! - * @brief parse the format string - * - * @param ctx the format context. - * - * @return iterator to one past last parse char of the format string. - */ - static constexpr auto parse(format_parse_context& t_ctx) - -> decltype(t_ctx.begin()) { - return t_ctx.end(); +BETTER_ENUM(FaceTurn, uint8_t, ANTICLOCKWISE, CLOCKWISE, HALFTURN) + +constexpr auto makeFirstQuery(const auto& t_first_val) { + return [t_first_val](auto t_pair_val) { + return t_pair_val.first == t_first_val; + }; +} + +constexpr auto makeSecondQuery(const auto& t_second_val) { + return [t_second_val](auto t_pair_val) { + return t_pair_val.second == t_second_val; + }; +} + +struct CubeMove { + constexpr static std::array< std::pair< CubeFace, char >, CubeFace::size() > + face_notation{ + {{CubeFace::UP, 'U'}, + {CubeFace::DOWN, 'D'}, + {CubeFace::RIGHT, 'R'}, + {CubeFace::LEFT, 'L'}, + {CubeFace::FRONT, 'F'}, + {CubeFace::BACK, 'B'}} + }; + + constexpr static std::array< std::pair< FaceTurn, char >, FaceTurn::size() > + turn_notation{ + {{FaceTurn::ANTICLOCKWISE, '\''}, + {FaceTurn::CLOCKWISE, '\0'}, + {FaceTurn::HALFTURN, '2'}} + }; + + constexpr static CubeFace getFace(const char& t_c) { + const auto* face_ptr = std::find_if( + face_notation.begin(), face_notation.end(), makeSecondQuery(t_c) + ); + if (face_ptr == face_notation.end()) { + throw std::invalid_argument(fmt::format( + "{} is not recognized notation for a cube face\n", t_c + )); + } + return face_ptr->first; } - /*! - * @brief formats the CubeFace element into a printable character - * - * @tparam FormatContext - * @param face - * @param ctx - * - * @return Format ctx output iterator; - */ - template < typename FormatContext > - static auto format(const CubeFace& t_face, FormatContext& t_ctx) - -> decltype(t_ctx.out()) { - return fmt::format_to(t_ctx.out(), "{}", cube_face_notation[t_face]); + constexpr static FaceTurn getTurn(const char& t_c) { + const auto* turn_ptr = std::find_if( + turn_notation.begin(), turn_notation.end(), makeSecondQuery(t_c) + ); + if (turn_ptr == turn_notation.end()) { + throw std::invalid_argument(fmt::format( + "{} is not recognized notation for a face turn\n", t_c + )); + } + return turn_ptr->first; + } + + CubeFace face; + FaceTurn turn; + + explicit constexpr CubeMove(const CubeFace& t_face, const FaceTurn& t_turn) + : face(t_face), turn(t_turn) {} + + explicit constexpr CubeMove(const std::string_view t_notation) + : face(getFace(t_notation[0])), turn(getTurn(t_notation[1])) {} + + [[nodiscard]] constexpr std::string getNotation() const { + const auto* face_ptr = std::find_if( + face_notation.begin(), face_notation.end(), makeFirstQuery(face) + ); + const auto* turn_ptr = std::find_if( + turn_notation.begin(), turn_notation.end(), makeFirstQuery(turn) + ); + + return std::string(&(face_ptr->second), 1) + + std::string(&(turn_ptr->second), 1); + } + + static std::vector< CubeMove > generateMoveList() { + std::vector< CubeMove > move_list; + move_list.reserve(CubeFace::size() * FaceTurn::size()); + for (auto face : CubeFace::values()) { + for (auto turn : FaceTurn::values()) { + move_list.emplace_back(face, turn); + } + } + return move_list; } }; -/*! - * @brief Number of different turns that each face can make - */ -constexpr size_t num_turns = 3; -/*! - * @brief FaceTurn an enum class to label the different modes for each face - * move. - */ -enum class FaceTurn { ANTICLOCKWISE, CLOCKWISE, HALFTURN }; -/*! - * @brief BiEnum to map turn modes to their quarter-turn metric notation - */ -constexpr BiEnum< FaceTurn, char, num_turns > turn_notation{ - {{{FaceTurn::ANTICLOCKWISE, '\''}, - {FaceTurn::CLOCKWISE, '\0'}, - {FaceTurn::HALFTURN, '2'}}}}; +template <> struct fmt::formatter< CubeMove > { + template < typename ParseCtx > constexpr auto parse(ParseCtx& t_ctx) const { + return t_ctx.begin(); + } -/*! - * @brief Array to map faces to their opposite axis partners. - */ -constexpr BiEnum< CubeFace, CubeFace, num_cube_faces > cube_face_axis_pair{ - {{{CubeFace::UP, CubeFace::DOWN}, - {CubeFace::DOWN, CubeFace::UP}, - {CubeFace::RIGHT, CubeFace::LEFT}, - {CubeFace::LEFT, CubeFace::RIGHT}, - {CubeFace::FRONT, CubeFace::BACK}, - {CubeFace::BACK, CubeFace::FRONT}}}}; + template < typename FormatCtx > + constexpr auto format(const CubeMove& t_move, FormatCtx& t_ctx) const { + return format_to(t_ctx.out(), "{}", t_move.getNotation()); + } +}; #endif diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 0ba6f1a..5189963 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -6,6 +6,14 @@ target_link_libraries(scramblerTest PRIVATE scrambler Catch2::Catch2WithMain set_target_properties(scramblerTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_TEST_OUTPUT_DIRECTORY}") + +add_executable(cubeTest cubeTest.cpp) +target_link_libraries(cubeTest PRIVATE scrambler Catch2::Catch2WithMain + project_options project_warnings) + +set_target_properties(cubeTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_TEST_OUTPUT_DIRECTORY}") + include(Catch) catch_discover_tests( @@ -20,3 +28,15 @@ catch_discover_tests( "libscrambler." OUTPUT_SUFFIX .xml) +catch_discover_tests( + cubeTest + TEST_PREFIX + "cube." + REPORTER + XML + OUTPUT_DIR + . + OUTPUT_PREFIX + "cube." + OUTPUT_SUFFIX + .xml) diff --git a/src/test/cubeTest.cpp b/src/test/cubeTest.cpp new file mode 100644 index 0000000..6a78dcc --- /dev/null +++ b/src/test/cubeTest.cpp @@ -0,0 +1,29 @@ +#include + +#include +#include + +#include + +TEST_CASE("CubeMove") { + const auto my_face = CubeFace::UP; + const auto my_turn = FaceTurn::ANTICLOCKWISE; + + const CubeMove my_move(my_face, my_turn); + REQUIRE(my_move.getNotation() == "U'"); + fmt::print("{}\n", my_move.getNotation()); + + const CubeMove another_move("R"); + REQUIRE(another_move.face == CubeFace::RIGHT); + REQUIRE(another_move.turn == FaceTurn::CLOCKWISE); + + const CubeMove another_move2("B2"); + REQUIRE(another_move2.face == CubeFace::BACK); + REQUIRE(another_move2.turn == FaceTurn::HALFTURN); + + REQUIRE_THROWS(CubeMove("A'")); + REQUIRE_THROWS(CubeMove("U$")); + + std::vector< CubeMove > move_list = CubeMove::generateMoveList(); + fmt::print("{}\n", move_list); +} From f268ef27d5541603acb786a228f410548e4e52f0 Mon Sep 17 00:00:00 2001 From: Brandon Michael Whitchurch Date: Sun, 29 Jan 2023 12:44:20 -0800 Subject: [PATCH 04/12] enum.hpp is dead --- CHANGELOG.md | 3 +- src/enum.hpp | 87 ---------------------------------------------------- 2 files changed, 2 insertions(+), 88 deletions(-) delete mode 100644 src/enum.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index fcbd9c5..15c166c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,9 @@ ## Miscellaneous -- Change: cube header makes use of enum utils. [`a2d456a`](https://github.com/bwhitchurch/CubeTimer/commit/a2d456a07469672d11545c4e1d312ee4434b995a) +- Change: cube header makes use of enum utils. [`9b0f1a6`](https://github.com/bwhitchurch/CubeTimer/commit/9b0f1a644d1720c5d9cd796d8867fd476183e103) - Enum things [`8320a5e`](https://github.com/bwhitchurch/CubeTimer/commit/8320a5ee7d8ffa4e60959c8de8ed9dcadecc7703) +- enum.hpp is dead [`a0a1dfd`](https://github.com/bwhitchurch/CubeTimer/commit/a0a1dfdeb5326133ed342c8aaafaae6f0aa1fc3c) #[0.1.1](https://github.com/bwhitchurch/CubeTimer/compare/0.1.0...0.1.1) ## New Features diff --git a/src/enum.hpp b/src/enum.hpp deleted file mode 100644 index 09ab87e..0000000 --- a/src/enum.hpp +++ /dev/null @@ -1,87 +0,0 @@ -/*! - * @file enum.hpp - * @brief Helper functions and classes for working with enumerators. - * @author Brandon M. Whitchurch - * @version 0.0.1 - * @date 2023-01-16 - */ -#ifndef ENUM_HPP -#define ENUM_HPP - -#include // for find_if -#include // for array -#include // for size_t -#include // for pair - -/*! - * @brief BiEnumTraits traits class for BiEnum - * - * @tparam Enum enumerator type. Either plain enum or enum class is acceptable. - * @tparam Value - * @tparam N - */ -template < typename Enum, typename Value, size_t N > struct BiEnumTraits { - /*! - * @brief type of element stored in array. - */ - using element_type = std::pair< Enum, Value >; - /*! - * @brief typedef for the array type to be used as a map. - */ - using array_type = std::array< element_type, N >; -}; - -/*! - * @brief BiEnum is a bidirectional enum. I.e. one that allows converting - * between enum labels and string representations - * - * @tparam Enum - * @tparam Value - * @tparam N - */ -template < typename Enum, typename Value, size_t N > struct BiEnum { - - /*! - * @brief type traits for the BiEnum. - */ - using traits_type = BiEnumTraits< Enum, Value, N >; - /*! - * @brief array of pairs to function as contexpr map - */ - typename traits_type::array_type m_enum_map; - - /*! - * @brief Search the map by Value, the second member of the pair type. - * - * @param val - * - * @return Enum label that corresponds with val. - */ - constexpr Enum operator[](const Value& t_val) const { - const auto* found_ptr = std::find_if( - m_enum_map.begin(), - m_enum_map.end(), - [t_val](auto t_iter_val) { return t_iter_val.second == t_val; } - ); - return found_ptr->first; - } - - /*! - * @brief Search the map b Enum, the first member of the pair type. - * - * @param eVal - * - * @return Value correponding with the enum label. - */ - constexpr Value operator[](const Enum& t_e_val) const - requires(!std::is_same_v< Enum, Value >) - { - const auto* found_ptr = std::find_if( - m_enum_map.begin(), - m_enum_map.end(), - [t_e_val](auto t_iter_val) { return t_iter_val.first == t_e_val; } - ); - return found_ptr->second; - } -}; -#endif From e0edfb1b2f518fd5ef3a43b6c988eeb1a40dc062 Mon Sep 17 00:00:00 2001 From: Brandon Michael Whitchurch Date: Sun, 29 Jan 2023 13:21:13 -0800 Subject: [PATCH 05/12] Change: Finished reworking scrambler to use new enum and cubeMove classes --- CHANGELOG.md | 3 +- src/CMakeLists.txt | 4 +- src/cube.cpp | 16 +++++++ src/cube.hpp | 1 + src/scrambler.hpp | 88 ++++++++++++++++++-------------------- src/test/scramblerTest.cpp | 19 -------- 6 files changed, 62 insertions(+), 69 deletions(-) create mode 100644 src/cube.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 15c166c..3259bc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ - Change: cube header makes use of enum utils. [`9b0f1a6`](https://github.com/bwhitchurch/CubeTimer/commit/9b0f1a644d1720c5d9cd796d8867fd476183e103) - Enum things [`8320a5e`](https://github.com/bwhitchurch/CubeTimer/commit/8320a5ee7d8ffa4e60959c8de8ed9dcadecc7703) -- enum.hpp is dead [`a0a1dfd`](https://github.com/bwhitchurch/CubeTimer/commit/a0a1dfdeb5326133ed342c8aaafaae6f0aa1fc3c) +- Change: Finished reworking scrambler to use new enum and cubeMove classes [`6d0e635`](https://github.com/bwhitchurch/CubeTimer/commit/6d0e6356285ba1b035ebd595113021fc8325caca) +- enum.hpp is dead [`f268ef2`](https://github.com/bwhitchurch/CubeTimer/commit/f268ef27d5541603acb786a228f410548e4e52f0) #[0.1.1](https://github.com/bwhitchurch/CubeTimer/compare/0.1.0...0.1.1) ## New Features diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 06c0b91..36e9de4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,9 +1,9 @@ add_subdirectory(ppUtils) add_subdirectory(EnumUtils) -add_library(scrambler scrambler.cpp) +add_library(scrambler scrambler.cpp cube.cpp) target_include_directories(scrambler PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) set_target_properties(scrambler PROPERTIES PUBLIC_HEADER - "scrambler.hpp;enum.hpp") + "scrambler.hpp;cube.cpp") target_link_libraries( scrambler PRIVATE project_options project_warnings diff --git a/src/cube.cpp b/src/cube.cpp new file mode 100644 index 0000000..5a22f4a --- /dev/null +++ b/src/cube.cpp @@ -0,0 +1,16 @@ +#include "cube.hpp" + +#include + +const std::unordered_map< CubeFace, CubeFace >& getAxisPairs() { + + const static std::unordered_map< CubeFace, CubeFace > face_opposites{ + { CubeFace::UP, CubeFace::DOWN}, + { CubeFace::DOWN, CubeFace::UP}, + {CubeFace::RIGHT, CubeFace::LEFT}, + { CubeFace::LEFT, CubeFace::RIGHT}, + {CubeFace::FRONT, CubeFace::BACK}, + { CubeFace::BACK, CubeFace::FRONT}, + }; + return face_opposites; +} diff --git a/src/cube.hpp b/src/cube.hpp index a34cd69..637d522 100644 --- a/src/cube.hpp +++ b/src/cube.hpp @@ -132,4 +132,5 @@ template <> struct fmt::formatter< CubeMove > { } }; +const std::unordered_map< CubeFace, CubeFace >& getAxisPairs(); #endif diff --git a/src/scrambler.hpp b/src/scrambler.hpp index 35fa95e..9355bea 100644 --- a/src/scrambler.hpp +++ b/src/scrambler.hpp @@ -1,28 +1,28 @@ #ifndef SCRAMBLER_HPP #define SCRAMBLER_HPP -#include // for fill_n -#include // for array -#include // for size_t -#include // for operator<<, basic_ostream, stringstream, basic_... -#include // for mt19937, uniform_real_distribution, random_device -#include // for char_traits, allocator, string, getline -#include +#include "cube.hpp" // for num_cube_faces, CubeFace (ptr only), num_turns -#include "cube.hpp" // for num_cube_faces, CubeFace (ptr only), num_turns -#include "enum.hpp" // for BiEnum +#include // for fill_n +#include // for array +#include // for size_t +#include // for operator<<, basic_ostream, stringstream, basic_... +#include // for mt19937, uniform_real_distribution, random_device +#include +#include // for char_traits, allocator, string, getline +#include constexpr size_t default_scramble_length = 25; class Scrambler { - size_t m_length; - std::mt19937 m_gen; - std::uniform_real_distribution<> m_dis; + size_t m_length; + std::mt19937 m_gen; + std::uniform_real_distribution<> m_dis; std::array< CubeFace, 2 > m_state; std::array< double, num_cube_faces > m_computeTransitionProbs() { std::array< double, num_cube_faces > transition_probs{}; - if (cube_face_axis_pair[m_state[0]] != m_state[1]) { + if (getAxisPairs().at(m_state[0]) != m_state[1]) { transition_probs.fill(1.0 / (num_cube_faces - 1)); transition_probs.at(static_cast< size_t >(m_state[1])) = 0.0; } else { @@ -46,55 +46,53 @@ class Scrambler { : m_length(t_scramble_length), m_gen(std::random_device{}()), m_dis(0.0, 1.0), - m_state{} { - std::array< double, num_cube_faces > state_prob{}; + m_state({CubeFace::RIGHT, CubeFace::UP}) { + std::array< double, CubeFace::size() > state_prob{}; state_prob.fill(1.0 / num_cube_faces); double sum = 0.0; for (auto& prob : state_prob) { sum += prob; - prob = sum; + prob = sum; } double rand_val = m_dis(m_gen); - size_t idx = 0; + size_t idx = 0; while (rand_val >= state_prob.at(idx)) { ++idx; } - m_state[0] = CubeFace(idx); + m_state[0] = CubeFace::values()[idx]; state_prob.fill(1.0 / (num_cube_faces - 1)); state_prob.at(static_cast< size_t >(m_state[0])) = 0.0; - sum = 0.0; + sum = 0.0; for (auto& prob : state_prob) { sum += prob; - prob = sum; + prob = sum; } - idx = 0; + idx = 0; rand_val = m_dis(m_gen); while (rand_val > state_prob.at(idx)) { ++idx; } - m_state[1] = CubeFace(idx); + m_state[1] = CubeFace::values()[idx]; } std::string generateScramble() { - size_t moves_generated = 0; - std::stringstream the_scramble{""}; + size_t moves_generated = 0; + std::vector< CubeMove > the_scramble; + the_scramble.reserve(m_length); while (moves_generated < m_length) { - if (moves_generated > 0) { the_scramble << " "; } auto prob_table = m_computeTransitionProbs(); const double rand_face = m_dis(m_gen); - size_t idx = 0; + size_t idx = 0; while (rand_face > prob_table.at(idx)) { ++idx; } - const auto curr_face = CubeFace(idx); - m_state[0] = m_state[1]; - m_state[1] = curr_face; - the_scramble << cube_face_notation[curr_face]; + const auto curr_face = CubeFace::values()[idx]; + m_state[0] = m_state[1]; + m_state[1] = curr_face; const double rand_turn = m_dis(m_gen); - if (rand_turn < 1. / num_turns) { - the_scramble << turn_notation[FaceTurn::ANTICLOCKWISE]; - } else if (rand_turn < 2. / num_turns) { - the_scramble << turn_notation[FaceTurn::CLOCKWISE]; - } else { - the_scramble << turn_notation[FaceTurn::HALFTURN]; - } + idx = 0; + while (rand_turn > double(idx + 1) / 3) { ++idx; } + const auto curr_turn = + FaceTurn::values()[idx]; the_scramble.emplace_back( + curr_face, curr_turn + ); ++moves_generated; - } - return the_scramble.str(); + } + return fmt::format("{}", fmt::join(the_scramble, " ")); } static void scrambleStats( @@ -102,14 +100,10 @@ class Scrambler { std::array< size_t, num_cube_faces * num_turns >& t_stats ) { std::stringstream scramble_stream(t_scramble); - std::string move; - while (std::getline(scramble_stream, move, ' ')) { - const CubeFace face = cube_face_notation[move[0]]; - const FaceTurn turn = turn_notation[move[1]]; - ++t_stats.at( - num_turns * static_cast< size_t >(face) - + static_cast< size_t >(turn) - ); + std::string move_str; + while (std::getline(scramble_stream, move_str, ' ')) { + const CubeMove move(move_str); + ++t_stats.at(num_turns * move.face + move.turn); } } }; diff --git a/src/test/scramblerTest.cpp b/src/test/scramblerTest.cpp index bb146a3..6429b7d 100644 --- a/src/test/scramblerTest.cpp +++ b/src/test/scramblerTest.cpp @@ -1,22 +1,3 @@ #include // for string_view, operator==, bas... #include "catch2/catch_test_macros.hpp" // for operator==, AssertionHandler -#include "enum.hpp" // for BiEnum - -enum class TestEnum { RED = 42, BLUE = 2, YELLOW, GREEN, PURPLE }; -constexpr BiEnum< TestEnum, std::string_view, 5 > color_names{ - {{{TestEnum::RED, "red"}, - {TestEnum::BLUE, "blue"}, - {TestEnum::YELLOW, "yellow"}, - {TestEnum::GREEN, "green"}, - {TestEnum::PURPLE, "purple"}}}}; - -static_assert(color_names[color_names["red"]] == "red"); - -static_assert(color_names[color_names[TestEnum::YELLOW]] == TestEnum::YELLOW); - -TEST_CASE("BiEnums") { - REQUIRE(color_names[TestEnum::BLUE] == "blue"); - REQUIRE(color_names["purple"] == TestEnum::PURPLE); - REQUIRE(color_names[color_names["green"]] == "green"); -} From c1c9cdaa66722f1f56c57ed2d08b5dad9bc5418f Mon Sep 17 00:00:00 2001 From: Brandon Michael Whitchurch Date: Sun, 29 Jan 2023 16:30:53 -0800 Subject: [PATCH 06/12] Fix: check for constexpr string support --- CHANGELOG.md | 6 +++++- src/EnumUtils/enumUtils.hpp | 5 +++++ src/cube.hpp | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3259bc2..5242bc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,15 @@ - feat: reflective enums through preprocessor shenanigans. [`7d34190`](https://github.com/bwhitchurch/CubeTimer/commit/7d34190747b18cabc922cf4bad7c1ce38c7e5b6a) +## Fixes + +- Fix: check for constexpr string support [`0ca7233`](https://github.com/bwhitchurch/CubeTimer/commit/0ca7233df8668e4b7b4aff874934bcf89cd2d74d) + ## Miscellaneous - Change: cube header makes use of enum utils. [`9b0f1a6`](https://github.com/bwhitchurch/CubeTimer/commit/9b0f1a644d1720c5d9cd796d8867fd476183e103) - Enum things [`8320a5e`](https://github.com/bwhitchurch/CubeTimer/commit/8320a5ee7d8ffa4e60959c8de8ed9dcadecc7703) -- Change: Finished reworking scrambler to use new enum and cubeMove classes [`6d0e635`](https://github.com/bwhitchurch/CubeTimer/commit/6d0e6356285ba1b035ebd595113021fc8325caca) +- Change: Finished reworking scrambler to use new enum and cubeMove classes [`e0edfb1`](https://github.com/bwhitchurch/CubeTimer/commit/e0edfb1b2f518fd5ef3a43b6c988eeb1a40dc062) - enum.hpp is dead [`f268ef2`](https://github.com/bwhitchurch/CubeTimer/commit/f268ef27d5541603acb786a228f410548e4e52f0) #[0.1.1](https://github.com/bwhitchurch/CubeTimer/compare/0.1.0...0.1.1) diff --git a/src/EnumUtils/enumUtils.hpp b/src/EnumUtils/enumUtils.hpp index d14975f..90ad414 100644 --- a/src/EnumUtils/enumUtils.hpp +++ b/src/EnumUtils/enumUtils.hpp @@ -7,6 +7,11 @@ #include #include using namespace std::string_view_literals; +#if __cpp_lib_constexpr_string +#define CUBE_TIMER_CONSTEXPR constexpr +#else +#define CUBE_TIMER_CONSTEXPR +#endif template < typename Type > struct IgnoreEquals { Type value; diff --git a/src/cube.hpp b/src/cube.hpp index 637d522..976a31c 100644 --- a/src/cube.hpp +++ b/src/cube.hpp @@ -97,7 +97,7 @@ struct CubeMove { explicit constexpr CubeMove(const std::string_view t_notation) : face(getFace(t_notation[0])), turn(getTurn(t_notation[1])) {} - [[nodiscard]] constexpr std::string getNotation() const { + [[nodiscard]] CUBE_TIMER_CONSTEXPR std::string getNotation() const { const auto* face_ptr = std::find_if( face_notation.begin(), face_notation.end(), makeFirstQuery(face) ); @@ -132,5 +132,5 @@ template <> struct fmt::formatter< CubeMove > { } }; -const std::unordered_map< CubeFace, CubeFace >& getAxisPairs(); +const std::unordered_map< CubeFace, CubeFace >& getAxisPairs(); #endif From 78bdc184ab927b76b3fa0bf987f7267765917c52 Mon Sep 17 00:00:00 2001 From: Brandon Michael Whitchurch Date: Sun, 29 Jan 2023 16:36:26 -0800 Subject: [PATCH 07/12] fix: constexpr string check changed to ifdef --- CHANGELOG.md | 3 ++- src/EnumUtils/enumUtils.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5242bc2..2af7964 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ ## Fixes -- Fix: check for constexpr string support [`0ca7233`](https://github.com/bwhitchurch/CubeTimer/commit/0ca7233df8668e4b7b4aff874934bcf89cd2d74d) +- Fix: check for constexpr string support [`c1c9cda`](https://github.com/bwhitchurch/CubeTimer/commit/c1c9cdaa66722f1f56c57ed2d08b5dad9bc5418f) +- fix: constexpr string check changed to ifdef [`ec65832`](https://github.com/bwhitchurch/CubeTimer/commit/ec658327c1195e5e329f7191aabdbb3ec690a997) ## Miscellaneous diff --git a/src/EnumUtils/enumUtils.hpp b/src/EnumUtils/enumUtils.hpp index 90ad414..2bf1e63 100644 --- a/src/EnumUtils/enumUtils.hpp +++ b/src/EnumUtils/enumUtils.hpp @@ -7,7 +7,7 @@ #include #include using namespace std::string_view_literals; -#if __cpp_lib_constexpr_string +#ifdef __cpp_lib_constexpr_string #define CUBE_TIMER_CONSTEXPR constexpr #else #define CUBE_TIMER_CONSTEXPR From 2c1a04f1fab6c3f10914c418750da9827369b543 Mon Sep 17 00:00:00 2001 From: Brandon Michael Whitchurch Date: Sun, 29 Jan 2023 16:50:47 -0800 Subject: [PATCH 08/12] Fix: checking constexpr string support (again) --- CHANGELOG.md | 3 ++- src/EnumUtils/enumUtils.hpp | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2af7964..9f38754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ ## Fixes - Fix: check for constexpr string support [`c1c9cda`](https://github.com/bwhitchurch/CubeTimer/commit/c1c9cdaa66722f1f56c57ed2d08b5dad9bc5418f) -- fix: constexpr string check changed to ifdef [`ec65832`](https://github.com/bwhitchurch/CubeTimer/commit/ec658327c1195e5e329f7191aabdbb3ec690a997) +- Fix: checking constexpr string support (again) [`cb92232`](https://github.com/bwhitchurch/CubeTimer/commit/cb92232cab8b56503f67cd4ef72bd09006984911) +- fix: constexpr string check changed to ifdef [`78bdc18`](https://github.com/bwhitchurch/CubeTimer/commit/78bdc184ab927b76b3fa0bf987f7267765917c52) ## Miscellaneous diff --git a/src/EnumUtils/enumUtils.hpp b/src/EnumUtils/enumUtils.hpp index 2bf1e63..ce37c5f 100644 --- a/src/EnumUtils/enumUtils.hpp +++ b/src/EnumUtils/enumUtils.hpp @@ -4,13 +4,13 @@ #include #include -#include #include +#include using namespace std::string_view_literals; -#ifdef __cpp_lib_constexpr_string -#define CUBE_TIMER_CONSTEXPR constexpr +#if __cpp_lib_constexpr_string >= 201907L + #define CUBE_TIMER_CONSTEXPR constexpr #else -#define CUBE_TIMER_CONSTEXPR + #define CUBE_TIMER_CONSTEXPR #endif template < typename Type > struct IgnoreEquals { From ccfba2500685859fcfb39d08341ff165bf93febc Mon Sep 17 00:00:00 2001 From: Brandon Michael Whitchurch Date: Sun, 29 Jan 2023 17:17:45 -0800 Subject: [PATCH 09/12] Fix naming in ppUtils --- CHANGELOG.md | 3 ++- src/ppUtils/test/CMakeLists.txt | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f38754..cb4b8be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ ## Fixes - Fix: check for constexpr string support [`c1c9cda`](https://github.com/bwhitchurch/CubeTimer/commit/c1c9cdaa66722f1f56c57ed2d08b5dad9bc5418f) -- Fix: checking constexpr string support (again) [`cb92232`](https://github.com/bwhitchurch/CubeTimer/commit/cb92232cab8b56503f67cd4ef72bd09006984911) +- Fix: checking constexpr string support (again) [`2c1a04f`](https://github.com/bwhitchurch/CubeTimer/commit/2c1a04f1fab6c3f10914c418750da9827369b543) - fix: constexpr string check changed to ifdef [`78bdc18`](https://github.com/bwhitchurch/CubeTimer/commit/78bdc184ab927b76b3fa0bf987f7267765917c52) ## Miscellaneous @@ -18,6 +18,7 @@ - Enum things [`8320a5e`](https://github.com/bwhitchurch/CubeTimer/commit/8320a5ee7d8ffa4e60959c8de8ed9dcadecc7703) - Change: Finished reworking scrambler to use new enum and cubeMove classes [`e0edfb1`](https://github.com/bwhitchurch/CubeTimer/commit/e0edfb1b2f518fd5ef3a43b6c988eeb1a40dc062) - enum.hpp is dead [`f268ef2`](https://github.com/bwhitchurch/CubeTimer/commit/f268ef27d5541603acb786a228f410548e4e52f0) +- Fix naming in ppUtils [`b28aa89`](https://github.com/bwhitchurch/CubeTimer/commit/b28aa893081a47afcbcb8a30d1ed7042725ac2f9) #[0.1.1](https://github.com/bwhitchurch/CubeTimer/compare/0.1.0...0.1.1) ## New Features diff --git a/src/ppUtils/test/CMakeLists.txt b/src/ppUtils/test/CMakeLists.txt index 8151811..30491af 100644 --- a/src/ppUtils/test/CMakeLists.txt +++ b/src/ppUtils/test/CMakeLists.txt @@ -9,12 +9,12 @@ include(Catch) catch_discover_tests( ppUtilsTest TEST_PREFIX - "libEnumUtils." + "ppUtils." REPORTER XML OUTPUT_DIR . OUTPUT_PREFIX - "libEnumUtils." + "ppUtils." OUTPUT_SUFFIX .xml) From e388169d2ee39cc8a97ad0b35b9fd68975d36475 Mon Sep 17 00:00:00 2001 From: Brandon Michael Whitchurch Date: Sun, 29 Jan 2023 18:50:36 -0800 Subject: [PATCH 10/12] Fix: remove findpackage from subdirectory cmakelists --- CHANGELOG.md | 3 ++- src/test/CMakeLists.txt | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb4b8be..6cc598e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Fix: check for constexpr string support [`c1c9cda`](https://github.com/bwhitchurch/CubeTimer/commit/c1c9cdaa66722f1f56c57ed2d08b5dad9bc5418f) - Fix: checking constexpr string support (again) [`2c1a04f`](https://github.com/bwhitchurch/CubeTimer/commit/2c1a04f1fab6c3f10914c418750da9827369b543) - fix: constexpr string check changed to ifdef [`78bdc18`](https://github.com/bwhitchurch/CubeTimer/commit/78bdc184ab927b76b3fa0bf987f7267765917c52) +- Fix: remove findpackage from subdirectory cmakelists [`017a4ed`](https://github.com/bwhitchurch/CubeTimer/commit/017a4ed836219ce7752c944308d93b4fef6be80c) ## Miscellaneous @@ -18,7 +19,7 @@ - Enum things [`8320a5e`](https://github.com/bwhitchurch/CubeTimer/commit/8320a5ee7d8ffa4e60959c8de8ed9dcadecc7703) - Change: Finished reworking scrambler to use new enum and cubeMove classes [`e0edfb1`](https://github.com/bwhitchurch/CubeTimer/commit/e0edfb1b2f518fd5ef3a43b6c988eeb1a40dc062) - enum.hpp is dead [`f268ef2`](https://github.com/bwhitchurch/CubeTimer/commit/f268ef27d5541603acb786a228f410548e4e52f0) -- Fix naming in ppUtils [`b28aa89`](https://github.com/bwhitchurch/CubeTimer/commit/b28aa893081a47afcbcb8a30d1ed7042725ac2f9) +- Fix naming in ppUtils [`ccfba25`](https://github.com/bwhitchurch/CubeTimer/commit/ccfba2500685859fcfb39d08341ff165bf93febc) #[0.1.1](https://github.com/bwhitchurch/CubeTimer/compare/0.1.0...0.1.1) ## New Features diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 5189963..a877986 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,5 +1,3 @@ -find_package(Catch2) - add_executable(scramblerTest scramblerTest.cpp) target_link_libraries(scramblerTest PRIVATE scrambler Catch2::Catch2WithMain project_options project_warnings) From ca63f95caa00c1789124f70bb03410bd7227bea9 Mon Sep 17 00:00:00 2001 From: Brandon Michael Whitchurch Date: Sun, 29 Jan 2023 20:04:17 -0800 Subject: [PATCH 11/12] Fix: dummy test in scramblerTest to ensure valid program --- CHANGELOG.md | 3 ++- src/test/CMakeLists.txt | 12 ++++++++---- src/test/scramblerTest.cpp | 4 ++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cc598e..9f94e4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,11 @@ ## Fixes +- Fix: dummy test in scramblerTest to ensure valid program [`b4b1ec5`](https://github.com/bwhitchurch/CubeTimer/commit/b4b1ec58fc2620da520f6bc95eb0f5b67333afbd) - Fix: check for constexpr string support [`c1c9cda`](https://github.com/bwhitchurch/CubeTimer/commit/c1c9cdaa66722f1f56c57ed2d08b5dad9bc5418f) - Fix: checking constexpr string support (again) [`2c1a04f`](https://github.com/bwhitchurch/CubeTimer/commit/2c1a04f1fab6c3f10914c418750da9827369b543) +- Fix: remove findpackage from subdirectory cmakelists [`e388169`](https://github.com/bwhitchurch/CubeTimer/commit/e388169d2ee39cc8a97ad0b35b9fd68975d36475) - fix: constexpr string check changed to ifdef [`78bdc18`](https://github.com/bwhitchurch/CubeTimer/commit/78bdc184ab927b76b3fa0bf987f7267765917c52) -- Fix: remove findpackage from subdirectory cmakelists [`017a4ed`](https://github.com/bwhitchurch/CubeTimer/commit/017a4ed836219ce7752c944308d93b4fef6be80c) ## Miscellaneous diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index a877986..5c8d60c 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,13 +1,17 @@ add_executable(scramblerTest scramblerTest.cpp) -target_link_libraries(scramblerTest PRIVATE scrambler Catch2::Catch2WithMain - project_options project_warnings) +target_link_libraries( + scramblerTest + PUBLIC scrambler Catch2::Catch2WithMain + PRIVATE project_options project_warnings) set_target_properties(scramblerTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_TEST_OUTPUT_DIRECTORY}") add_executable(cubeTest cubeTest.cpp) -target_link_libraries(cubeTest PRIVATE scrambler Catch2::Catch2WithMain - project_options project_warnings) +target_link_libraries( + cubeTest + PUBLIC scrambler Catch2::Catch2WithMain + PRIVATE project_options project_warnings) set_target_properties(cubeTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_TEST_OUTPUT_DIRECTORY}") diff --git a/src/test/scramblerTest.cpp b/src/test/scramblerTest.cpp index 6429b7d..0853db3 100644 --- a/src/test/scramblerTest.cpp +++ b/src/test/scramblerTest.cpp @@ -1,3 +1,7 @@ #include // for string_view, operator==, bas... #include "catch2/catch_test_macros.hpp" // for operator==, AssertionHandler + +TEST_CASE("Hello World"){ + REQUIRE(true); +} From a108ff3d6ecb07e80bff31e57362d15b9ca5029d Mon Sep 17 00:00:00 2001 From: bwhitchurch <36140721+bwhitchurch@users.noreply.github.com> Date: Sun, 29 Jan 2023 20:54:33 -0800 Subject: [PATCH 12/12] Remove empty File --- src/EnumUtils/enumUtils.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/EnumUtils/enumUtils.cpp diff --git a/src/EnumUtils/enumUtils.cpp b/src/EnumUtils/enumUtils.cpp deleted file mode 100644 index e69de29..0000000