diff --git a/.vscode/settings.json b/.vscode/settings.json index abcce64..2ac6596 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,16 @@ { - "files.associations": { - "cmath": "cpp", - "string_view": "cpp" - } + "files.associations": { + "cmath": "cpp", + "string_view": "cpp", + "sstream": "cpp", + "optional": "cpp", + "memory": "cpp", + "random": "cpp", + "array": "cpp", + "fstream": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "system_error": "cpp" + }, + "cmake.generator": "Unix Makefiles" } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 10a4c73..045fd28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,20 +1,74 @@ cmake_minimum_required(VERSION 3.14) -project(enum_name_example VERSION 0.1 LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 17) +project( + enum_name + VERSION 1.0 + LANGUAGES CXX) -include(FetchContent) +# Define the library +add_library(enum_name INTERFACE) -FetchContent_Declare( - DocTest - GIT_REPOSITORY "https://github.com/onqtam/doctest" - GIT_TAG "v2.4.11" -) +# Specify the include directories for the library +target_include_directories( + enum_name INTERFACE $ + $) + +# Set the C++ standard +# target_compile_features(enum_name INTERFACE cxx_std_11) + +# Install the library +install( + TARGETS enum_name + EXPORT enum_nameTargets + INCLUDES + DESTINATION include) + +install( + EXPORT enum_nameTargets + FILE enum_nameTargets.cmake + NAMESPACE mgutility:: + DESTINATION lib/cmake/enum_name) + +install(DIRECTORY include/ DESTINATION include) + +# Create the package configuration files +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/enum_nameConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion) -FetchContent_MakeAvailable(DocTest) +configure_package_config_file( + ${CMAKE_CURRENT_LIST_DIR}/cmake/enum_nameConfig.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/enum_nameConfig.cmake" + INSTALL_DESTINATION lib/cmake/enum_name) -add_executable(${PROJECT_NAME} example/main.cpp) -target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/include) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/enum_nameConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/enum_nameConfigVersion.cmake" + DESTINATION lib/cmake/enum_name) +export( + EXPORT enum_nameTargets + FILE "${CMAKE_CURRENT_BINARY_DIR}/enum_nameTargets.cmake" + NAMESPACE mgutility::) + +# Enable testing enable_testing() -add_subdirectory(test) \ No newline at end of file + +# FetchContent to get the Doctest testing framework +include(FetchContent) +FetchContent_Declare( + doctest + GIT_REPOSITORY https://github.com/doctest/doctest.git + GIT_TAG v2.4.11) +FetchContent_MakeAvailable(doctest) + +# Add test executable +add_executable(test_enum_name tests/test_enum_name.cpp) + +# Link the test executable with the library and Doctest +target_link_libraries(test_enum_name PRIVATE enum_name doctest::doctest) + +# Add tests +add_test(NAME test_enum_name COMMAND test_enum_name) diff --git a/README.md b/README.md index 523b10c..ac10478 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,11 @@ Converting (scoped)enum values to/from string names written in C++>=11. ## Features * Supports `enum` and `enum class` * Supports enums in namespaces, classes or structs even templated or not -* Supports compile-time as much as possible using with C++14 and later +* Supports compile-time as much as possible using with C++17 and later * Changing enum range with template parameter (default range: `[0, 256)`) on each call or with your special function for types or adding specialized `enum_range` struct * Supports and automatically overloaded `operator<<` for Enum types to direct using with ostream objects * Supports custom enum name output by explicit specialization of `constexpr inline auto mgutility::detail::enum_type::name() noexcept` function +* Supports bitmasked enums and auto detect them * Supports iterate over enum (names and values) with `mgutility::enum_for_each()` class and it is compatible with standard ranges and views ## Limitations @@ -20,16 +21,32 @@ Converting (scoped)enum values to/from string names written in C++>=11. * Wider range can increase compile time so user responsible to adjusting for enum's range -## Usage ([try it!](https://godbolt.org/z/YM5EvY1Y5)) +## Usage ([try it!](https://godbolt.org/z/dfeYxscvT)) ```C++ #include #include "enum_name.hpp" -num class rgb_color { red, green, blue, unknown = -1 }; +enum class rgb_color { + red = 1 << 0, + green = 1 << 1, + blue = 1 << 2, + unknown = -1 +}; + +auto operator|(rgb_color lhs, rgb_color rhs) -> rgb_color { + return static_cast(mgutility::enum_to_underlying(lhs) | + mgutility::enum_to_underlying(rhs)); +} + +auto operator&(rgb_color lhs, rgb_color rhs) -> rgb_color { + return static_cast(mgutility::enum_to_underlying(lhs) & + mgutility::enum_to_underlying(rhs)); +} // specialize rgb_color::unknown to make output "UNKNOWN" instead of "unknown" template <> -constexpr auto mgutility::detail::enum_type::name() noexcept +constexpr inline auto +mgutility::detail::enum_type::name() noexcept -> string_view { return "UNKNOWN"; } @@ -38,38 +55,41 @@ constexpr auto mgutility::detail::enum_type::name template <> struct mgutility::enum_range { static constexpr auto min = -1; - static constexpr auto max = 3; + static constexpr auto max = 5; }; // you can specialize enum ranges with overload per enum types (option 2) -auto enum_name = [](rgb_color c) { return mgutility::enum_name<-1, 3>(c); }; +auto enum_name = [](rgb_color c) { return mgutility::enum_name<-1, 5>(c); }; #if defined(__cpp_lib_print) #include #include #endif - int main() { auto x = rgb_color::blue; - auto y = mgutility::to_enum("greenn"); + auto y = mgutility::to_enum("green|red"); #if defined(__cpp_lib_print) -// enum_for_each can usable with views and range algorithms -auto colors = mgutility::enum_for_each() | - std::ranges::views::filter( - [](auto &&pair) { return pair.second != "UNKNOWN"; }); + // enum_for_each can usable with views and range algorithms + auto colors = mgutility::enum_for_each() | + std::ranges::views::filter([](auto &&pair) { + return !pair.second.empty() && pair.second != "UNKNOWN"; + }); std::ranges::for_each(colors, [](auto &&color) { - std::println("{} \t: {}", color.second, mgutility::enum_to_underlying(color.first)); + std::println("{} \t: {}", color.second, + mgutility::enum_to_underlying(color.first)); }); #else for (auto&& e : mgutility::enum_for_each()) { - if(e.second != "UNKNOWN"){ - std::cout << e.second << " \t: " << mgutility::enum_to_underlying(e.first) << '\n'; + if (!e.second.empty() && e.second != "UNKNOWN") { + std::cout << e.second + << " \t: " << mgutility::enum_to_underlying(e.first) + << '\n'; } // std::pair {enum_type, name_of_enum} } @@ -78,8 +98,6 @@ auto colors = mgutility::enum_for_each() | // default signature: enum_name(Enum&&) Changing max_value to not too much greater than enum's // max value, it will compiles faster - std::cout << mgutility::enum_name(x) << '\n'; // will print "blue" to output - // or std::cout << x << '\n'; // will print "blue" to output // calling specialized enum ranges function for rgb_color type diff --git a/cmake/enum_nameConfig.cmake.in b/cmake/enum_nameConfig.cmake.in new file mode 100644 index 0000000..5fbea33 --- /dev/null +++ b/cmake/enum_nameConfig.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/enum_nameTargets.cmake") \ No newline at end of file diff --git a/example/main.cpp b/example/main.cpp index 4f0b051..771fa1e 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -1,33 +1,80 @@ -#include #include "enum_name.hpp" +#include +enum class rgb_color { + red = 1 << 0, + green = 1 << 1, + blue = 1 << 2, + unknown = -1 +}; + +auto constexpr operator|(rgb_color lhs, rgb_color rhs) -> rgb_color { + return static_cast(mgutility::enum_to_underlying(lhs) | + mgutility::enum_to_underlying(rhs)); +} -enum class rgb_color { red, green, blue, unknown = -1}; +auto constexpr operator&(rgb_color lhs, rgb_color rhs) -> rgb_color { + return static_cast(mgutility::enum_to_underlying(lhs) & + mgutility::enum_to_underlying(rhs)); +} -// you can specialize enum ranges with specialize struct per enum types (option 1) -namespace mgutility{ - template<> - struct enum_range - { - static constexpr auto min = -1; - static constexpr auto max = 3; - }; +// specialize rgb_color::unknown to make output "UNKNOWN" instead of "unknown" +template <> +constexpr auto +mgutility::detail::enum_type::name() noexcept + -> mgutility::string_view { + return "UNKNOWN"; } +// you can specialize enum ranges with overload per enum types (option 1) +template <> struct mgutility::enum_range { + static constexpr auto min = -1; + static constexpr auto max = 5; +}; + // you can specialize enum ranges with overload per enum types (option 2) -auto enum_name = [](rgb_color c){ return mgutility::enum_name<-1, 3>(c); }; - - -int main() -{ - auto x = rgb_color::blue; - auto y = mgutility::to_enum("green"); - - // default signature: enum_name(Enum&&) - // Changing max_value to not too much greater than enum's max value, it will compiles faster - std::cout << mgutility::enum_name(x) << '\n'; // will print "blue" to output - - // calling specialized enum ranges function for rgb_color type - // will print "green" to output, if y can't convert to rgb_color prints "unknown" - std::cout << enum_name(y.value_or(rgb_color::unknown)) << '\n'; +auto enum_name = [](rgb_color c) { return mgutility::enum_name<-1, 5>(c); }; + +#if defined(__cpp_lib_print) +#include +#include +#endif + +int main() { + auto x = rgb_color::blue; + auto y = mgutility::to_enum("green|red"); + +#if defined(__cpp_lib_print) + + // enum_for_each can usable with views and range algorithms + auto colors = mgutility::enum_for_each() | + std::ranges::views::filter([](auto &&pair) { + return !pair.second.empty() && pair.second != "UNKNOWN"; + }); + + std::ranges::for_each(colors, [](auto &&color) { + std::println("{} \t: {}", color.second, + mgutility::enum_to_underlying(color.first)); + }); + +#else + + for (auto &&e : mgutility::enum_for_each()) { + if (!e.second.empty() && e.second != "UNKNOWN") { + std::cout << e.second << " \t: " << mgutility::enum_to_underlying(e.first) + << '\n'; + } + // std::pair {enum_type, name_of_enum} + } +#endif + + // default signature: enum_name(Enum&&) Changing max_value to not too much greater than enum's + // max value, it will compiles faster + std::cout << x << '\n'; // will print "blue" to output + + // calling specialized enum ranges function for rgb_color type + // will print "green" to output, if y can't convert to rgb_color prints + // "UNKNOWN" + std::cout << enum_name(y.value_or(rgb_color::unknown)) << '\n'; } \ No newline at end of file diff --git a/include/detail/definitions.hpp b/include/detail/definitions.hpp new file mode 100644 index 0000000..055a693 --- /dev/null +++ b/include/detail/definitions.hpp @@ -0,0 +1,114 @@ +/* +MIT License + +© 2024 mguludag + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef DETAIL_DEFINITIONS_HPP +#define DETAIL_DEFINITIONS_HPP + +/** + * @file detail_definitions.hpp + * @brief Defines macros for compiler and standard support detection. + */ + +/** + * @brief Checks for MSVC compiler version. + * + * If the MSVC version is less than 2017, an error is raised. + */ +#if defined(_MSC_VER) && _MSC_VER < 1910 +#error "Requires MSVC 2017 or newer!" +/** + * @brief Checks for Clang compiler version. + * + * If the Clang version is less than 6, an error is raised. + */ +#elif defined(__clang__) && __clang_major__ < 6 +#error "Requires clang 6 or newer!" +/** + * @brief Checks for GCC compiler version. + * + * If the GCC version is less than 9 and it is not Clang, an error is raised. + */ +#elif defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 9 +#error "Requires gcc 9 or newer!" +/** + * @brief Checks for unsupported compilers. + * + * If the compiler is not MSVC, Clang, or GCC, an error is raised. + */ +#elif !defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) +#error "Your compiler is not supported!" +#endif + +/** + * @brief Defines the MGUTILITY_CPLUSPLUS macro for MSVC and other compilers. + * + * For MSVC, it uses _MSVC_LANG. For other compilers, it uses __cplusplus. + */ +#ifdef _MSC_VER +#define MGUTILITY_CPLUSPLUS _MSVC_LANG +#define __PRETTY_FUNCTION__ __FUNCSIG__ +#else +#define MGUTILITY_CPLUSPLUS __cplusplus +#endif + +/** + * @brief Defines the MGUTILITY_CNSTXPR macro based on the C++ standard. + * + * If the C++ standard is C++11, MGUTILITY_CNSTXPR is defined as empty. + * If the C++ standard is newer than C++11, MGUTILITY_CNSTXPR is defined as constexpr. + * If the C++ standard is older than C++11, an error is raised. + */ +#if MGUTILITY_CPLUSPLUS == 201103L +#define MGUTILITY_CNSTXPR +#elif MGUTILITY_CPLUSPLUS > 201103L +#define MGUTILITY_CNSTXPR constexpr +#elif MGUTILITY_CPLUSPLUS < 201103L +#error "Standards older than C++11 is not supported!" +#endif + +/** + * @brief Defines the MGUTILITY_CNSTXPR_CLANG_WA macro based on the C++ standard. + * + * If the C++ standard is newer than C++17 and the compiler is not Clang, + * MGUTILITY_CNSTXPR_CLANG_WA is defined as constexpr. Otherwise, it is defined as empty. + */ +#if MGUTILITY_CPLUSPLUS > 201703L && !defined(__clang__) +#define MGUTILITY_CNSTXPR_CLANG_WA constexpr +#else +#define MGUTILITY_CNSTXPR_CLANG_WA +#endif + +/** + * @brief Defines the MGUTILITY_CNSTEVL macro based on the C++ standard. + * + * If the C++ standard is newer than C++17, MGUTILITY_CNSTEVL is defined as consteval. + * Otherwise, it is defined as empty. + */ +#if MGUTILITY_CPLUSPLUS > 201703L +#define MGUTILITY_CNSTEVL consteval +#else +#define MGUTILITY_CNSTEVL +#endif + +#endif // DETAIL_DEFINITIONS_HPP \ No newline at end of file diff --git a/include/detail/enum_for_each.hpp b/include/detail/enum_for_each.hpp new file mode 100644 index 0000000..d40f55c --- /dev/null +++ b/include/detail/enum_for_each.hpp @@ -0,0 +1,190 @@ +/* +MIT License + +Copyright (c) 2024 mguludag + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef DETAIL_ENUM_FOR_EACH_HPP +#define DETAIL_ENUM_FOR_EACH_HPP + +#include "meta.hpp" +#include "string_view.hpp" + +#include +#include + +namespace mgutility +{ + namespace detail + { + /** + * @brief Alias template for a string or string view type based on the presence + * of a bitwise OR operator. + * + * If the type T supports the bitwise OR operator, the alias is a std::string. + * Otherwise, it is a mgutility::string_view. + * + * @tparam T The type to check. + */ + template + using string_or_view_t = + typename std::conditional::value, std::string, + mgutility::string_view>::type; + + /** + * @brief A pair consisting of an enum value and its corresponding string or + * string view. + * + * @tparam Enum The enum type. + */ + template + using enum_pair = std::pair>; + } // namespace detail + + /** + * @brief A class template for iterating over enum values. + * + * @tparam Enum The enum type. + */ + template + class enum_for_each + { + using value_type = const detail::enum_pair; + using size_type = std::size_t; + + /** + * @brief An iterator for enum values. + */ + struct enum_iter + { + using const_iter_type = decltype(enum_range::min); + using iter_type = detail::remove_const_t; + using iterator_category = std::forward_iterator_tag; + using value_type = const detail::enum_pair; + using difference_type = std::ptrdiff_t; + using pointer = value_type *; + using reference = value_type &; + + /** + * @brief Default constructor initializing the iterator to the default + * position. + */ + enum_iter() : m_pos{} {} + + /** + * @brief Constructor initializing the iterator to a specific position. + * + * @param value The initial position of the iterator. + */ + enum_iter(iter_type value) : m_pos{value} {} + + /** + * @brief Pre-increment operator. + * + * @return A reference to the incremented iterator. + */ + auto operator++() -> enum_iter & + { + ++m_pos; + return *this; + } + + /** + * @brief Post-increment operator. + * + * @return A copy of the iterator before incrementing. + */ + auto operator++(int) -> enum_iter + { + m_pos++; + return *this; + } + + /** + * @brief Inequality comparison operator. + * + * @param other The other iterator to compare with. + * @return True if the iterators are not equal, otherwise false. + */ + auto operator!=(const enum_iter &other) const -> bool + { + return m_pos != other.m_pos; + } + + /** + * @brief Equality comparison operator. + * + * @param other The other iterator to compare with. + * @return True if the iterators are equal, otherwise false. + */ + auto operator==(const enum_iter &other) const -> bool + { + return m_pos == other.m_pos; + } + + /** + * @brief Dereference operator. + * + * @return The current enum pair. + */ + auto operator*() const -> value_type; + + private: + iter_type m_pos; /**< The current position of the iterator. */ + }; + + public: + /** + * @brief Default constructor. + */ + enum_for_each() = default; + + /** + * @brief Returns an iterator to the beginning of the enum range. + * + * @return A reference to the beginning iterator. + */ + auto begin() -> enum_iter & { return m_begin; } + + /** + * @brief Returns an iterator to the end of the enum range. + * + * @return A reference to the end iterator. + */ + auto end() -> enum_iter & { return m_end; } + + /** + * @brief Returns the size of the enum range. + * + * @return The size of the enum range. + */ + auto size() -> std::size_t + { + return enum_range::max - enum_range::min; + } + + private: + enum_iter m_begin{enum_range::min}; /**< The beginning iterator. */ + enum_iter m_end{enum_range::max}; /**< The end iterator. */ + }; +} // namespace mgutility + +#endif // DETAIL_ENUM_FOR_EACH_HPP \ No newline at end of file diff --git a/include/detail/enum_name_impl.hpp b/include/detail/enum_name_impl.hpp new file mode 100644 index 0000000..7d7b72c --- /dev/null +++ b/include/detail/enum_name_impl.hpp @@ -0,0 +1,268 @@ +/* +MIT License + +Copyright (c) 2024 mguludag + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef DETAIL_ENUM_NAME_IMPL_HPP +#define DETAIL_ENUM_NAME_IMPL_HPP + +#include "enum_for_each.hpp" +#include "optional.hpp" +#include "string_view.hpp" + +#include +#include + +namespace mgutility +{ + namespace detail + { + + struct enum_type + { + /** + * @brief Gets the name of an unscoped enum value. + * + * @tparam Enum The enum type. + * @tparam e The enum value. + * @return A string view representing the name of the enum value. + */ + template < + typename Enum, Enum e, + detail::enable_if_t::value, bool> = true> + MGUTILITY_CNSTXPR static auto name() noexcept -> mgutility::string_view + { + auto str = mgutility::string_view(__PRETTY_FUNCTION__); + auto offset = lastidxenumname[0] + lastidxenumname[1]; + auto index = std::max(str.rfind(lastidxenumname[2], str.size() - offset), + str.rfind(lastidxenumname[3], str.size() - offset)); + auto result = str.substr(index + 1, str.size() - offset - index); + return result[0] == '(' ? "" : result; + } + + /** + * @brief Gets the name of a scoped enum value. + * + * @tparam Enum The enum type. + * @tparam e The enum value. + * @return A string view representing the name of the enum value. + */ + template < + typename Enum, Enum e, + detail::enable_if_t::value, bool> = true> + MGUTILITY_CNSTXPR static auto name() noexcept -> mgutility::string_view + { + auto str = mgutility::string_view(__PRETTY_FUNCTION__); + auto index = + str.rfind(lastidxenumname[3], str.size() - lastidxenumname[0]) + 1; + auto result = str.substr(index, str.size() - lastidxenumname[0] - index); + return result.size() > 4 && result[4] == lastidxenumname[4] ? "" : result; + } + + private: + static constexpr int lastidxenumname[] = +#if defined(_MSC_VER) + {21, 0, ',', ':', '<'}; +#elif defined(__clang__) + {1, 1, ' ', ':', '('}; +#elif defined(__GNUC__) + { +#if MGUTILITY_CPLUSPLUS < 201703L + 163, +#else + 157, +#endif + 5, ' ', ':', '('}; +#endif + }; + + /** + * @brief Generates an array of enum names. + * + * @tparam Enum The enum type. + * @tparam Is The enumeration values. + * @return An array of string views representing the enum names. + */ + template + MGUTILITY_CNSTXPR inline auto + get_enum_array(detail::enum_sequence /*unused*/) noexcept + -> std::array + { + return std::array{ + "", enum_type::template name()...}; + } + + /** + * @brief Converts a string to an enum value. + * + * @tparam Enum The enum type. + * @tparam Min The minimum enum value. + * @tparam Max The maximum enum value. + * @param str The string view representing the enum name. + * @return An optional enum value. + */ + template + MGUTILITY_CNSTXPR inline auto to_enum_impl(mgutility::string_view str) noexcept + -> mgutility::optional + { + MGUTILITY_CNSTXPR auto arr = + get_enum_array(detail::make_enum_sequence()); + const auto index{std::find(arr.begin() + 1, arr.end(), str)}; + return index == arr.end() ? mgutility::nullopt + : mgutility::optional{static_cast( + std::distance(arr.begin(), index) + Min - 1)}; + } + + /** + * @brief Converts a string to an enum bitmask value. + * + * @tparam Enum The enum type. + * @tparam Min The minimum enum value. + * @tparam Max The maximum enum value. + * @param str The string view representing the enum name. + * @return An optional enum bitmask value. + */ + template + MGUTILITY_CNSTXPR auto to_enum_bitmask_impl(mgutility::string_view str) noexcept + -> mgutility::optional + { + + // Check if the string contains a '|' character + if (str.find('|') == mgutility::string_view::npos) + { + return to_enum_impl(str); + } + + mgutility::optional result{mgutility::nullopt}; + std::size_t index = 0; + + for (std::size_t i = 0; i < str.size(); ++i) + { + if (str[i] == '|') + { + auto name = str.substr(index, i - index); + auto maybe_enum = to_enum_impl(name); + + if (!name.empty() && maybe_enum) + { + result.emplace(result ? static_cast(*result | *maybe_enum) + : *maybe_enum); + } + + index = i + 1; + } + } + + auto maybe_enum = to_enum_impl(str.substr(index)); + if (result && maybe_enum) + { + result.emplace(static_cast(*result | *maybe_enum)); + } + else + { + result.reset(); + } + + return result; + } + + /** + * @brief Gets the name of an enum value. + * + * @tparam Enum The enum type. + * @tparam Min The minimum enum value. + * @tparam Max The maximum enum value. + * @param e The enum value. + * @return A string view representing the name of the enum value. + */ + template ::value, bool> = true> + MGUTILITY_CNSTXPR auto enum_name_impl(Enum e) noexcept + -> mgutility::string_view + { + MGUTILITY_CNSTXPR auto arr = + get_enum_array(detail::make_enum_sequence()); + const auto index{(Min < 0 ? Min * -1 : Min) + static_cast(e) + 1}; + return arr[(index < Min || index > arr.size() - 1) ? 0 : index]; + } + + /** + * @brief Gets the name of an enum bitmask value. + * + * @tparam Enum The enum type. + * @tparam Min The minimum enum value. + * @tparam Max The maximum enum value. + * @param e The enum value. + * @return A string view or string representing the name of the enum bitmask + * value. + */ + template ::value, bool> = true> + MGUTILITY_CNSTXPR_CLANG_WA inline auto enum_name_impl(Enum e) noexcept + -> detail::string_or_view_t + { + + // Get the array of enum names + MGUTILITY_CNSTXPR auto arr = + get_enum_array(detail::make_enum_sequence()); + + // Calculate the index in the array + const auto index = (Min < 0 ? -Min : Min) + static_cast(e) + 1; + const auto name = + arr[(index < Min || index >= static_cast(arr.size())) ? 0 : index]; + + // Lambda to check if a character is a digit + const auto is_digit = [](char c) + { return c >= '0' && c <= '9'; }; + + // Return the name if it's valid + if (!name.empty() && !is_digit(name[0])) + { + return std::string{name}; + } + + // Construct bitmasked name + std::string bitmasked_name; + for (auto i = Min; i < Max; ++i) + { + const auto idx = (Min < 0 ? -Min : Min) + i + 1; + if (idx >= 0 && idx < static_cast(arr.size()) && !arr[idx].empty() && + !is_digit(arr[idx][0]) && + (e & static_cast(i)) == static_cast(i)) + { + bitmasked_name.append(arr[idx]).append("|"); + } + } + + // Remove the trailing '|' if present + if (!bitmasked_name.empty()) + { + bitmasked_name.pop_back(); + } + + return bitmasked_name.find('|') != std::string::npos ? bitmasked_name + : std::string{""}; + } + } // namespace detail +} // namespace mgutility + +#endif // DETAIL_ENUM_NAME_IMPL_HPP \ No newline at end of file diff --git a/include/detail/meta.hpp b/include/detail/meta.hpp new file mode 100644 index 0000000..8cfa451 --- /dev/null +++ b/include/detail/meta.hpp @@ -0,0 +1,165 @@ +/* +MIT License + +© 2024 mguludag + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef DETAIL_META_HPP +#define DETAIL_META_HPP + +#include "definitions.hpp" +#include + +namespace mgutility { + namespace detail { + /** + * @brief Trait to check if a type is a scoped enumeration. + * + * @tparam E The type to check. + */ + template + struct is_scoped_enum { + /** + * @brief Boolean value indicating if the type is a scoped enumeration. + */ + static constexpr auto value = std::is_enum::value && + !std::is_convertible::type>::value; + }; + + /** + * @brief Trait to check if a type supports the bitwise OR operator. + * + * @tparam T The type to check. + * @tparam Enable SFINAE parameter, default is void. + */ + template + struct has_bit_or : std::false_type { + }; + + /** + * @brief Specialization of has_bit_or for types that support the bitwise OR operator. + * + * @tparam T The type to check. + */ + template + struct has_bit_or : std::true_type { + }; + +#if MGUTILITY_CPLUSPLUS > 201103L + /** + * @brief Helper variable template for is_scoped_enum. + * + * @tparam E The type to check. + */ + template + static constexpr bool is_scoped_enum_v = is_scoped_enum::value; +#endif + + /** + * @brief Alias template for std::enable_if. + * + * This template is used to conditionally enable a type `T` if the boolean constant `B` is true. + * It is a shorthand for `typename std::enable_if::type`. + * + * @tparam B The compile-time boolean condition. + * @tparam T The type to be enabled if `B` is true, default is void. + */ + template + using enable_if_t = typename std::enable_if::type; + + /** + * @brief Alias template for std::underlying_type. + * + * @tparam T The enumeration type. + */ + template + using underlying_type_t = typename std::underlying_type::type; + + /** + * @brief Alias template for std::remove_const. + * + * @tparam T The type to remove const from. + */ + template + using remove_const_t = typename std::remove_const::type; + + /** + * @brief Represents a sequence of enumeration values. + * + * @tparam Enum The enumeration type. + * @tparam ...EnumValues The enumeration values in the sequence. + */ + template + struct enum_sequence { + }; + + /** + * @brief Helper for generating a sequence of enumeration values. + * + * @tparam Enum The enumeration type. + * @tparam Min The minimum value in the sequence. + * @tparam Max The maximum value in the sequence. + * @tparam ...Next The next values in the sequence. + */ + template + struct enum_sequence_helper + : enum_sequence_helper { + }; + + /** + * @brief Specialization of enum_sequence_helper for the end of the sequence. + * + * @tparam Enum The enumeration type. + * @tparam Min The minimum value in the sequence. + * @tparam ...Next The next values in the sequence. + */ + template + struct enum_sequence_helper { + /** + * @brief The resulting sequence type. + */ + using type = enum_sequence(Next)...>; + }; + + /** + * @brief Generates a sequence of enumeration values. + * + * @tparam Enum The enumeration type. + * @tparam Min The minimum value in the sequence. + * @tparam Max The maximum value in the sequence. + */ + template + using make_enum_sequence = typename enum_sequence_helper::type; + } // namespace detail + + /** + * @brief Provides the range for an enumeration type. + * + * @tparam T The enumeration type. + */ + template + struct enum_range { + static constexpr auto min{-128}; + static constexpr auto max{128}; + }; +} // namespace mgutility + +#endif // DETAIL_META_HPP \ No newline at end of file diff --git a/include/detail/optional.hpp b/include/detail/optional.hpp new file mode 100644 index 0000000..252ecb8 --- /dev/null +++ b/include/detail/optional.hpp @@ -0,0 +1,355 @@ +/* +MIT License + +Copyright (c) 2024 mguludag + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef DETAIL_OPTIONAL_HPP +#define DETAIL_OPTIONAL_HPP + +#include "definitions.hpp" +#include "meta.hpp" + +#include + +#if MGUTILITY_CPLUSPLUS >= 201703L +#include +#endif + +namespace mgutility +{ + +#if MGUTILITY_CPLUSPLUS < 201703L + + /** + * @brief Exception thrown when accessing an empty optional. + */ + struct bad_optional_access : public std::exception + { + /** + * @brief Returns the exception message. + * + * @return A C-string with the exception message. + */ + const char *what() const noexcept { return "optional has no value"; } + }; + + struct nullopt_t; + + /** + * @brief A class template that provides optional (nullable) objects. + * + * @tparam T The type of the value. + */ + template + class optional + { + public: + /** + * @brief Constructs an empty optional. + */ + MGUTILITY_CNSTXPR inline optional(nullopt_t &) + : dummy_{0}, has_value_{false} {} + + /** + * @brief Constructs an empty optional. + */ + MGUTILITY_CNSTXPR inline optional() : dummy_{0}, has_value_{false} {} + + /** + * @brief Constructs an optional with a value. + * + * @tparam Args The types of the arguments. + * @param args The arguments to construct the value. + */ + template + MGUTILITY_CNSTXPR inline optional(Args &&...args) + : value_{T{std::forward(args)...}}, has_value_{true} {} + + /** + * @brief Constructs an optional with a value. + * + * @param value The value to initialize with. + */ + MGUTILITY_CNSTXPR inline optional(T &&value) + : value_{value}, has_value_{true} {} + + /** + * @brief Copy constructor. + * + * @param other The other optional to copy. + */ + MGUTILITY_CNSTXPR inline optional(const optional &other) + : value_{other.value_}, has_value_{other.has_value_} {} + + /** + * @brief Move constructor. + * + * @param other The other optional to move. + */ + MGUTILITY_CNSTXPR inline optional(optional &&other) + : value_{other.value_}, has_value_{other.has_value_} + { + other.reset(); + } + + /** + * @brief Destructor. + */ + inline ~optional() { has_value_ = false; } + + /** + * @brief Copy assignment operator. + * + * @param other The other optional to copy. + * @return A reference to this optional. + */ + MGUTILITY_CNSTXPR inline optional &operator=(const optional &other) + { + value_ = other.value_; + has_value_ = other.has_value_; + return *this; + } + + /** + * @brief Move assignment operator. + * + * @param other The other optional to move. + * @return A reference to this optional. + */ + MGUTILITY_CNSTXPR inline optional &operator=(optional &&other) + { + value_ = other.value_; + has_value_ = other.has_value_; + other.reset(); + return *this; + } + + /** + * @brief Swaps the contents of this optional with another. + * + * @param other The other optional to swap with. + */ + MGUTILITY_CNSTXPR inline void swap(optional &&other) + { + auto val = std::move(other.value_); + other.value_ = std::move(value_); + value_ = std::move(val); + + auto hval = std::move(other.has_value_); + other.has_value_ = std::move(has_value_); + has_value_ = std::move(hval); + } + + /** + * @brief Dereferences the stored value. + * + * @return A reference to the stored value. + */ + MGUTILITY_CNSTXPR inline T &operator*() { return value_; } + + /** + * @brief Dereferences the stored value (const version). + * + * @return A reference to the stored value. + */ + MGUTILITY_CNSTXPR inline T &operator*() const { return value_; } + + /** + * @brief Accesses the stored value. + * + * @return A reference to the stored value. + * @throws bad_optional_access if the optional has no value. + */ + MGUTILITY_CNSTXPR inline T &value() + { + if (!has_value_) + throw bad_optional_access(); + return value_; + } + + /** + * @brief Accesses the stored value (const version). + * + * @return A reference to the stored value. + * @throws bad_optional_access if the optional has no value. + */ + MGUTILITY_CNSTXPR inline T &value() const + { + if (!has_value_) + throw bad_optional_access(); + return value_; + } + + /** + * @brief Returns the stored value or a default value if empty. + * + * @param value The default value to return if empty. + * @return The stored value or the default value. + */ + MGUTILITY_CNSTXPR inline T value_or(T &&value) + { + return has_value_ ? value_ : value; + } + + /** + * @brief Returns the stored value or a default value if empty (const version). + * + * @param value The default value to return if empty. + * @return The stored value or the default value. + */ + MGUTILITY_CNSTXPR inline T value_or(T &&value) const + { + return has_value_ ? value_ : value; + } + + /** + * @brief Returns the stored value or a default value if empty. + * + * @param value The default value to return if empty. + * @return The stored value or the default value. + */ + MGUTILITY_CNSTXPR inline T value_or(const T &value) + { + return has_value_ ? value_ : value; + } + + /** + * @brief Returns the stored value or a default value if empty (const version). + * + * @param value The default value to return if empty. + * @return The stored value or the default value. + */ + MGUTILITY_CNSTXPR inline T value_or(const T &value) const + { + return has_value_ ? value_ : value; + } + + /** + * @brief Constructs the value in-place. + * + * @param value The value to emplace. + */ + MGUTILITY_CNSTXPR inline void emplace(T value) + { + value_ = std::move(value); + has_value_ = true; + } + + /** + * @brief Constructs the value in-place with arguments. + * + * @tparam Args The types of the arguments. + * @param args The arguments to construct the value. + */ + template + MGUTILITY_CNSTXPR inline void emplace(Args &&...args) + { + value_ = std::move(T{std::forward(args)...}); + has_value_ = true; + } + + /** + * @brief Checks if the optional has a value. + * + * @return True if the optional has a value, otherwise false. + */ + MGUTILITY_CNSTXPR inline bool has_value() const { return has_value_; } + + /** + * @brief Resets the optional, making it empty. + */ + template ::value, bool> = true> + MGUTILITY_CNSTXPR inline void reset() + { + value_ = T{}; + has_value_ = false; + } + + /** + * @brief Resets the optional, making it empty. + */ + template ::value, bool> = true> + MGUTILITY_CNSTXPR inline void reset() + { + value_.~T(); + has_value_ = false; + } + + /** + * @brief Checks if the optional has a value. + * + * @return True if the optional has a value, otherwise false. + */ + MGUTILITY_CNSTXPR operator bool() { return has_value_; } + + private: + union + { + T value_; + char dummy_[sizeof(T)]; + }; + bool has_value_; + }; + + /** + * @brief Represents a null optional. + */ + struct nullopt_t + { + /** + * @brief Converts nullopt_t to an empty optional. + * + * @tparam T The type of the optional. + * @return An empty optional. + */ + template + MGUTILITY_CNSTXPR operator optional() + { + return optional{}; + } + }; + + /** + * @brief A global instance of nullopt_t to represent null optional. + */ + auto nullopt = nullopt_t{}; + +#else + /** + * @brief Alias template for std::optional. + * + * @tparam T The type of the value. + */ + template + using optional = std::optional; + + /** + * @brief A global constant representing a null optional. + */ + inline constexpr auto nullopt{std::nullopt}; + +#endif +} + +#endif // DETAIL_OPTIONAL_HPP \ No newline at end of file diff --git a/include/detail/string_view.hpp b/include/detail/string_view.hpp new file mode 100644 index 0000000..65e62d2 --- /dev/null +++ b/include/detail/string_view.hpp @@ -0,0 +1,323 @@ +/* +MIT License + +Copyright (c) 2024 mguludag + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef STRING_STRING_VIEW_HPP +#define STRING_STRING_VIEW_HPP + +#include +#include +#include + +#include "definitions.hpp" + +namespace mgutility +{ + +#if MGUTILITY_CPLUSPLUS < 201703L + + namespace detail + { + /** + * @brief Computes the length of a C-string at compile-time. + * + * @param str The C-string. + * @param sz The initial size, default is 0. + * @return The length of the C-string. + */ + constexpr auto strlen_constexpr(const char *str, size_t sz = 0) noexcept + -> size_t + { + return str[sz] == '\0' ? sz : strlen_constexpr(str, ++sz); + } + } // namespace detail + + /** + * @brief A basic string view class template. + * + * @tparam Char The character type, default is char. + */ + template + class basic_string_view + { + public: + /** + * @brief Default constructor. + */ + constexpr inline basic_string_view() noexcept : data_(""), size_(0) {} + + /** + * @brief Constructs a basic_string_view from a C-string. + * + * @param str The C-string. + */ + constexpr inline basic_string_view(const Char *str) noexcept + : data_(str), size_(detail::strlen_constexpr(str)) {} + + /** + * @brief Constructs a basic_string_view from a C-string and length. + * + * @param str The C-string. + * @param len The length of the string. + */ + constexpr inline basic_string_view(const Char *str, size_t len) noexcept + : data_(str), size_(len) {} + + /** + * @brief Copy constructor. + * + * @param other The other basic_string_view to copy. + */ + constexpr inline basic_string_view(const basic_string_view &other) + : data_(other.data_), size_(other.size_) {} + + /** + * @brief Move constructor. + * + * @param other The other basic_string_view to move. + */ + constexpr inline basic_string_view(basic_string_view &&other) noexcept + : data_(std::move(other.data_)), size_(std::move(other.size_)) {} + + /** + * @brief Copy assignment operator. + * + * @param other The other basic_string_view to copy. + * @return A reference to this object. + */ + MGUTILITY_CNSTXPR inline basic_string_view & + operator=(const basic_string_view &other) noexcept + { + data_ = other.data_; + size_ = other.size_; + return *this; + } + + /** + * @brief Move assignment operator. + * + * @param other The other basic_string_view to move. + * @return A reference to this object. + */ + MGUTILITY_CNSTXPR inline basic_string_view & + operator=(basic_string_view &&other) noexcept + { + data_ = std::move(other.data_); + size_ = std::move(other.size_); + return *this; + } + + /** + * @brief Accesses the character at the given index. + * + * @param index The index. + * @return The character at the index. + */ + constexpr inline const Char operator[](size_t index) const noexcept + { + return data_[index]; + } + + /** + * @brief Returns an iterator to the beginning of the string. + * + * @return A pointer to the first character. + */ + constexpr inline const Char *begin() const noexcept { return data_; } + + /** + * @brief Returns an iterator to the end of the string. + * + * @return A pointer to one past the last character. + */ + constexpr inline const Char *end() const noexcept { return (data_ + size_); } + + /** + * @brief Checks if the string is empty. + * + * @return True if the string is empty, otherwise false. + */ + constexpr inline bool empty() const noexcept { return size_ < 1; } + + /** + * @brief Returns the size of the string. + * + * @return The size of the string. + */ + constexpr inline size_t size() const noexcept { return size_; } + + /** + * @brief Returns a pointer to the underlying data. + * + * @return A pointer to the data. + */ + constexpr inline const Char *data() const noexcept { return data_; } + + /** + * @brief Returns a substring view. + * + * @param begin The starting position. + * @param len The length of the substring. + * @return A basic_string_view representing the substring. + */ + constexpr inline basic_string_view substr(size_t begin, + size_t len = 0U) const noexcept + { + return basic_string_view(data_ + begin, len == 0U ? size_ - begin : len); + } + + /** + * @brief Finds the last occurrence of a character. + * + * @param c The character to find. + * @param pos The position to start from, default is npos. + * @return The position of the character or npos if not found. + */ + constexpr inline size_t rfind(Char c, size_t pos = npos) const noexcept + { + return (pos == npos ? pos = size_ : pos = pos), c == data_[pos] ? pos + : pos == 0U + ? npos + : rfind(c, --pos); + } + + /** + * @brief Finds the first occurrence of a character. + * + * @param c The character to find. + * @param pos The position to start from, default is 0. + * @return The position of the character or npos if not found. + */ + constexpr inline size_t find(Char c, size_t pos = 0) const noexcept + { + return c == data_[pos] ? pos : pos < size_ ? find(c, ++pos) + : npos; + } + + /** + * @brief Equality operator. + * + * @param lhs The left-hand side basic_string_view. + * @param rhs The right-hand side basic_string_view. + * @return True if the strings are equal, otherwise false. + */ + constexpr friend inline bool + operator==(basic_string_view lhs, + basic_string_view rhs) noexcept + { + return (lhs.size_ == rhs.size_) && + std::strncmp(lhs.data_, rhs.data_, lhs.size_) == 0; + } + + /** + * @brief Equality operator. + * + * @param lhs The left-hand side basic_string_view. + * @param rhs The right-hand side C-string. + * @return True if the strings are equal, otherwise false. + */ + constexpr friend inline bool operator==(basic_string_view lhs, + const Char *rhs) noexcept + { + return (lhs.size_ == detail::strlen_constexpr(rhs)) && + std::strncmp(lhs.data_, rhs, lhs.size_) == 0; + } + + /** + * @brief Inequality operator. + * + * @param lhs The left-hand side basic_string_view. + * @param rhs The right-hand side basic_string_view. + * @return True if the strings are not equal, otherwise false. + */ + constexpr friend inline bool + operator!=(basic_string_view lhs, + basic_string_view rhs) noexcept + { + return !(lhs == rhs); + } + + /** + * @brief Inequality operator. + * + * @param lhs The left-hand side basic_string_view. + * @param rhs The right-hand side C-string. + * @return True if the strings are not equal, otherwise false. + */ + constexpr friend inline bool operator!=(basic_string_view lhs, + const Char *rhs) noexcept + { + return !(lhs == rhs); + } + + /** + * @brief Converts the string view to an std::string. + * + * @return An std::string representing the same string. + */ + inline operator std::string() { return std::string(data_, size_); } + + /** + * @brief Converts the string view to an std::string (const version). + * + * @return An std::string representing the same string. + */ + inline operator std::string() const { return std::string(data_, size_); } + + /** + * @brief Stream insertion operator. + * + * @param os The output stream. + * @param sv The basic_string_view. + * @return A reference to the output stream. + */ + friend inline std::ostream &operator<<(std::ostream &os, + const basic_string_view &sv) + { + for (auto c : sv) + { + os << c; + } + return os; + } + + static constexpr auto npos = -1; + + private: + size_t size_; + const Char *data_; + }; + + using string_view = basic_string_view; + +#else +#include + + using string_view = std::string_view; + +#endif + + +} // namespace mgutility + +#endif // STRING_STRING_VIEW_HPP \ No newline at end of file diff --git a/include/enum_name.hpp b/include/enum_name.hpp index c7ed6ab..9b896a1 100644 --- a/include/enum_name.hpp +++ b/include/enum_name.hpp @@ -25,480 +25,176 @@ #ifndef MGUTILITY_ENUM_NAME_HPP #define MGUTILITY_ENUM_NAME_HPP -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1910 -#error "Requires MSVC 2017 or newer!" -#elif defined(__clang__) && __clang_major__ < 6 -#error "Requires clang 6 or newer!" -#elif defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 9 -#error "Requires gcc 9 or newer!" -#elif !defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) -#error "Your compiler is not supported!" -#endif - -#ifdef _MSC_VER -#define MG_ENUM_NAME_CPLUSPLUS _MSVC_LANG -#else -#define MG_ENUM_NAME_CPLUSPLUS __cplusplus -#endif - -#if MG_ENUM_NAME_CPLUSPLUS == 201103L -#define MG_ENUM_NAME_CNSTXPR -#elif MG_ENUM_NAME_CPLUSPLUS > 201103L -#define MG_ENUM_NAME_CNSTXPR constexpr -#elif MG_ENUM_NAME_CPLUSPLUS < 201103L -#error "Standards older than C++11 is not supported!" -#endif - -#if MG_ENUM_NAME_CPLUSPLUS > 201702L -#include -#endif - -namespace mgutility { -namespace detail { - -template -struct is_scoped_enum { - static constexpr auto value = - std::is_enum::value && - !std::is_convertible::type>::value; -}; -#if MG_ENUM_NAME_CPLUSPLUS > 201103L -template -static constexpr bool is_scoped_enum_v = is_scoped_enum::value; -#endif - -template -using underlying_type_t = typename std::underlying_type::type; - -template -using remove_const_t = typename std::remove_const::type; - -#if MG_ENUM_NAME_CPLUSPLUS < 201703L - -template -using enable_if_t = typename std::enable_if::type; - -constexpr auto strlen_constexpr(const char* str, - size_t sz = 0) noexcept -> size_t { - return str[sz] == '\0' ? sz : strlen_constexpr(str, ++sz); -} - -template -class basic_string_view { - public: - constexpr inline basic_string_view() noexcept : data_(""), size_(0) {} - constexpr inline basic_string_view(const Char* str) noexcept - : data_(str), size_(strlen_constexpr(str)) {} - constexpr inline basic_string_view(const Char* str, size_t len) noexcept - : data_(str), size_(len) {} - constexpr inline basic_string_view(const basic_string_view& other) - : data_(other.data_), size_(other.size_) {} - constexpr inline basic_string_view(basic_string_view&& other) noexcept - : data_(std::move(other.data_)), size_(std::move(other.size_)) {} - MG_ENUM_NAME_CNSTXPR inline basic_string_view& operator=( - const basic_string_view& other) noexcept { - data_ = other.data_; - size_ = other.size_; - return *this; - } - MG_ENUM_NAME_CNSTXPR inline basic_string_view& operator=( - basic_string_view&& other) noexcept { - data_ = std::move(other.data_); - size_ = std::move(other.size_); - return *this; - } - constexpr inline const Char operator[](size_t index) const noexcept { - return data_[index]; - } - constexpr inline const Char* begin() const noexcept { return data_; } - constexpr inline const Char* end() const noexcept { - return (data_ + size_); - } - constexpr inline bool empty() const noexcept { return size_ < 1; } - constexpr inline size_t size() const noexcept { return size_; } - constexpr inline const Char* data() const noexcept { return data_; } - constexpr inline basic_string_view substr(size_t begin, - size_t len) const noexcept { - return basic_string_view(data_ + begin, len); - } - constexpr inline size_t rfind(Char c, size_t pos = 0) const noexcept { - return c == data_[pos] ? pos : rfind(c, --pos); - } - - constexpr friend inline bool operator==( - basic_string_view lhs, basic_string_view rhs) noexcept { - return (lhs.size_ == rhs.size_) && - std::strncmp(lhs.data_, rhs.data_, lhs.size_) == 0; - } - - constexpr friend inline bool operator==(basic_string_view lhs, - const Char* rhs) noexcept { - return (lhs.size_ == strlen_constexpr(rhs)) && - std::strncmp(lhs.data_, rhs, lhs.size_) == 0; - } - - constexpr friend inline bool operator!=( - basic_string_view lhs, basic_string_view rhs) noexcept { - return !(lhs == rhs); - } - - constexpr friend inline bool operator!=(basic_string_view lhs, - const Char* rhs) noexcept { - return !(lhs == rhs); - } - - inline operator std::string() { return std::string(data_, size_); } - inline operator std::string() const { return std::string(data_, size_); } - - friend inline std::ostream& operator<<(std::ostream& os, - const basic_string_view& sv) { - for (auto c : sv) { - os << c; - } - return os; - } - - private: - size_t size_; - const Char* data_; -}; - -using string_view = basic_string_view; - -struct bad_optional_access : public std::exception { - const char* what() const noexcept { return "optional has no value"; } -}; - -struct nullopt_t; - -template -class optional { - public: - MG_ENUM_NAME_CNSTXPR inline optional(nullopt_t&) - : dummy_{0}, has_value_{false} {} - MG_ENUM_NAME_CNSTXPR inline optional() : dummy_{0}, has_value_{false} {} - template - MG_ENUM_NAME_CNSTXPR inline optional(Args&&... args) - : value_{T{std::forward(args)...}}, has_value_{true} {} - MG_ENUM_NAME_CNSTXPR inline optional(T&& value) - : value_{value}, has_value_{true} {} - MG_ENUM_NAME_CNSTXPR inline optional(const optional& other) - : value_{other.value_}, has_value_{other.has_value_} {} - MG_ENUM_NAME_CNSTXPR inline optional(optional&& other) - : value_{other.value_}, has_value_{other.has_value_} { - other.reset(); - } - inline ~optional() { has_value_ = false; } - MG_ENUM_NAME_CNSTXPR inline optional& operator=(const optional& other) { - value_ = other.value_; - has_value_ = other.has_value_; - return *this; - } - MG_ENUM_NAME_CNSTXPR inline optional& operator=(optional&& other) { - value_ = other.value_; - has_value_ = other.has_value_; - other.reset(); - return *this; - } - MG_ENUM_NAME_CNSTXPR inline void swap(optional&& other) { - auto val = std::move(other.value_); - other.value_ = std::move(value_); - value_ = std::move(val); - - auto hval = std::move(other.has_value_); - other.has_value_ = std::move(has_value_); - has_value_ = std::move(hval); - } - MG_ENUM_NAME_CNSTXPR inline T& operator*() { return value_; } - MG_ENUM_NAME_CNSTXPR inline T& operator*() const { return value_; } - MG_ENUM_NAME_CNSTXPR inline T& value() { - if (!has_value_) throw detail::bad_optional_access(); - return value_; - } - MG_ENUM_NAME_CNSTXPR inline T& value() const { - if (!has_value_) throw detail::bad_optional_access(); - return value_; - } - MG_ENUM_NAME_CNSTXPR inline T value_or(T&& value) { - return has_value_ ? value_ : value; - } - MG_ENUM_NAME_CNSTXPR inline T value_or(T&& value) const { - return has_value_ ? value_ : value; - } - MG_ENUM_NAME_CNSTXPR inline T value_or(const T& value) { - return has_value_ ? value_ : value; - } - MG_ENUM_NAME_CNSTXPR inline T value_or(const T& value) const { - return has_value_ ? value_ : value; - } - MG_ENUM_NAME_CNSTXPR inline void emplace(T value) { - value_ = std::move(value); - has_value_ = true; - } - template - MG_ENUM_NAME_CNSTXPR inline void emplace(Args&&... args) { - value_ = std::move(T{std::forward(args)...}); - has_value_ = true; - } - MG_ENUM_NAME_CNSTXPR inline bool has_value() const { return has_value_; } - -#if !(defined(__clang__) && __clang_major__ < 11) - template ::value, bool> = true> - MG_ENUM_NAME_CNSTXPR inline void reset() { - T::~T(); - has_value_ = false; - } -#endif - template ::value, bool> = true> - MG_ENUM_NAME_CNSTXPR inline void reset() { - value_ = T{}; - has_value_ = false; - } - - MG_ENUM_NAME_CNSTXPR operator bool() { return has_value_; } - - private: - union { - T value_; - char dummy_[sizeof(T)]; - }; - bool has_value_; -}; - -struct nullopt_t { - template - MG_ENUM_NAME_CNSTXPR operator optional() { - return optional{}; - } -}; - -auto nullopt = nullopt_t{}; - -#else - -template -using optional = std::optional; -inline constexpr auto nullopt{std::nullopt}; -using string_view = std::string_view; -template -using enable_if_t = std::enable_if_t; - -#endif - -template -struct enum_sequence {}; - -template -struct enum_sequence_helper - : enum_sequence_helper {}; - -template -struct enum_sequence_helper { - using type = enum_sequence(Next)...>; -}; - -template -using make_enum_sequence = typename enum_sequence_helper::type; - -struct enum_type { -#if defined(_MSC_VER) -#define __PRETTY_FUNCTION__ __FUNCSIG__ -#endif - template < - typename Enum, Enum e, - detail::enable_if_t::value, bool> = true> - MG_ENUM_NAME_CNSTXPR static inline auto name() noexcept - -> detail::string_view { - auto str = detail::string_view(__PRETTY_FUNCTION__); - auto offset{lastidxenumname[0] + lastidxenumname[1]}; - auto index = - std::max(str.rfind(lastidxenumname[2], str.size() - offset), - str.rfind(lastidxenumname[3], str.size() - offset)); - auto result = str.substr(index + 1, str.size() - offset - index); - return result[0] == '(' ? "" : result; - } - - template < - typename Enum, Enum e, - detail::enable_if_t::value, bool> = true> - MG_ENUM_NAME_CNSTXPR static inline auto name() noexcept - -> detail::string_view { - auto str = detail::string_view(__PRETTY_FUNCTION__); - auto index = - str.rfind(lastidxenumname[3], str.size() - lastidxenumname[0]) + 1; - auto result = - str.substr(index, str.size() - lastidxenumname[0] - index); - return result.size() > 4 ? result[4] == lastidxenumname[4] ? "" : result - : result; - } - - private: - static constexpr int lastidxenumname[] = -#if defined(_MSC_VER) - {22, 0, ',', ':', '<'}; -#elif defined(__clang__) - {1, 1, ' ', ':', '('}; -#elif defined(__GNUC__) - { -#if MG_ENUM_NAME_CPLUSPLUS < 201703L - 179, -#else - 165, -#endif - 5, ' ', ':', '('}; -#endif -}; - -template -using enum_pair = std::pair; - -template -inline auto get_enum_array(detail::enum_sequence) noexcept - -> std::array { - static std::array - arr{"", enum_type::template name()...}; - return arr; -} - -template -inline auto to_enum_impl(detail::string_view str) noexcept - -> detail::optional { - auto arr = get_enum_array(detail::make_enum_sequence()); - const auto index{std::find(arr.begin() + 1, arr.end(), str)}; - return index == arr.end() - ? detail::nullopt - : detail::optional{static_cast( - std::distance(arr.begin(), index) + Min - 1)}; -} - -template -inline auto enum_name_impl(Enum e) noexcept -> detail::string_view { - auto arr = get_enum_array(detail::make_enum_sequence()); - const auto index{std::abs(Min) + static_cast(e) + (Min < 0 ? 1 : 1)}; - return arr[(index < Min || index > arr.size() - 1) ? 0 : index]; -} -} // namespace detail -} // namespace mgutility - -namespace mgutility { -template -struct enum_range { - static constexpr auto min{-128}; - static constexpr auto max{128}; -}; - -template -class enum_for_each { - using value_type = const detail::enum_pair; - using size_type = std::size_t; - - struct enum_iter { - using const_iter_type = decltype(enum_range::min); - using iter_type = detail::remove_const_t; - using iterator_category = std::forward_iterator_tag; - using value_type = const detail::enum_pair; - using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; - - enum_iter() : m_pos{} {} - enum_iter(iter_type value) : m_pos{value} {} - - auto operator++() -> enum_iter& { - ++m_pos; - return *this; - } - - auto operator++(int) -> enum_iter { - m_pos++; - return *this; - } - - auto operator!=(const enum_iter& other) const -> bool { - return m_pos != other.m_pos; - } - - auto operator==(const enum_iter& other) const -> bool { - return m_pos == other.m_pos; - } - - auto operator*() const -> value_type; - - private: - iter_type m_pos; - }; - - public: - enum_for_each() {} - auto begin() -> enum_iter& { return m_begin; } - auto end() -> enum_iter& { return m_end; } - auto size() -> std::size_t { - return enum_range::max - enum_range::min; - } - - private: - enum_iter m_begin{enum_range::min}; - enum_iter m_end{enum_range::max}; -}; - -template -constexpr inline auto enum_to_underlying(Enum e) noexcept - -> detail::underlying_type_t { +#include + +namespace mgutility +{ + + /** + * @brief Converts an enum value to its underlying integer value. + * + * @tparam Enum The enum type. + * @param e The enum value. + * @return The underlying integer value of the enum. + */ + template + constexpr auto enum_to_underlying(Enum e) noexcept + -> detail::underlying_type_t + { static_assert(std::is_enum::value, "Value is not an Enum type!"); return static_cast>(e); -} - -template -MG_ENUM_NAME_CNSTXPR inline auto enum_name(Enum e) noexcept - -> detail::string_view { + } + + /** + * @brief Gets the name of an enum value. + * + * @tparam Min The minimum enum value. + * @tparam Max The maximum enum value. + * @tparam Enum The enum type. + * @param e The enum value. + * @return A string view or string representing the name of the enum value. + */ + template + MGUTILITY_CNSTXPR auto enum_name(Enum e) noexcept + -> detail::string_or_view_t + { static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!"); static_assert(std::is_enum::value, "Value is not an Enum type!"); return detail::enum_name_impl(e); -} - -template ::min, - int Max = enum_range::max> -MG_ENUM_NAME_CNSTXPR inline auto enum_name(Enum e) noexcept - -> detail::string_view { + } + + /** + * @brief Gets the name of an enum value. + * + * @tparam Enum The enum type. + * @tparam Min The minimum enum value, default is enum_range::min. + * @tparam Max The maximum enum value, default is enum_range::max. + * @param e The enum value. + * @return A string view or string representing the name of the enum value. + */ + template ::min, + int Max = enum_range::max> + MGUTILITY_CNSTXPR auto enum_name(Enum e) noexcept + -> detail::string_or_view_t + { static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!"); static_assert(std::is_enum::value, "Value is not an Enum type!"); return detail::enum_name_impl(e); -} - -template -auto enum_for_each::enum_iter::operator*() const -> value_type { + } + + /** + * @brief Gets the enum value and its name. + * + * @tparam Enum The enum type. + * @return A pair of the enum value and its name. + */ + template + auto enum_for_each::enum_iter::operator*() const -> value_type + { auto value = static_cast(m_pos); return detail::enum_pair{value, enum_name(value)}; -} - -template ::min, - int Max = enum_range::max> -MG_ENUM_NAME_CNSTXPR inline auto to_enum(detail::string_view str) noexcept - -> detail::optional { + } + + /** + * @brief Converts a string to an enum value. + * + * @tparam Enum The enum type. + * @tparam Min The minimum enum value, default is enum_range::min. + * @tparam Max The maximum enum value, default is enum_range::max. + * @param str The string view representing the enum name. + * @return An optional enum value. + */ + template ::min, + int Max = enum_range::max, + detail::enable_if_t::value, bool> = true> + MGUTILITY_CNSTXPR auto to_enum(mgutility::string_view str) noexcept + -> mgutility::optional + { static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!"); static_assert(std::is_enum::value, "Type is not an Enum type!"); return detail::to_enum_impl(str); -} -} // namespace mgutility + } + + /** + * @brief Converts a string to an enum bitmask value. + * + * @tparam Enum The enum type. + * @tparam Min The minimum enum value, default is enum_range::min. + * @tparam Max The maximum enum value, default is enum_range::max. + * @param str The string view representing the enum name. + * @return An optional enum bitmask value. + */ + template ::min, + int Max = enum_range::max, + detail::enable_if_t::value, bool> = true> + MGUTILITY_CNSTXPR auto to_enum(mgutility::string_view str) noexcept + -> mgutility::optional + { + static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!"); + static_assert(std::is_enum::value, "Type is not an Enum type!"); + return detail::to_enum_bitmask_impl(str); + } + + /** + * @brief Casts an integer value to an enum value. + * + * @tparam Enum The enum type. + * @tparam Min The minimum enum value, default is enum_range::min. + * @tparam Max The maximum enum value, default is enum_range::max. + * @param value The integer value to cast. + * @return An optional enum value. + */ + template ::min, + int Max = enum_range::max> + MGUTILITY_CNSTXPR auto enum_cast(int value) noexcept + -> mgutility::optional + { + static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!"); + static_assert(std::is_enum::value, "Type is not an Enum type!"); + if (enum_name(static_cast(value)).empty()) + { + return mgutility::nullopt; + } + return static_cast(value); + } +} // namespace mgutility -template ::value, bool>::type = true> -auto operator<<(std::ostream& os, Enum e) -> std::ostream& { - static_assert(std::is_enum::value, "Value is not an Enum type!"); - os << mgutility::enum_name(e); - return os; +/** + * @brief Outputs the name of an enum value to an output stream. + * + * @tparam Enum The enum type. + * @param os The output stream. + * @param e The enum value. + * @return The output stream. + */ +template ::value, bool> = true> +auto operator<<(std::ostream &os, Enum e) -> std::ostream & +{ + static_assert(std::is_enum::value, "Value is not an Enum type!"); + os << mgutility::enum_name(e); + return os; } -#endif // MGUTILITY_ENUM_NAME_HPP +#if defined(__cpp_lib_format) + +#include + +/** + * @brief Formatter for enum types for use with std::format. + * + * @tparam Enum The enum type. + */ +template + requires std::is_enum_v +struct std::formatter : formatter +{ + auto constexpr format(Enum e, format_context &ctx) const + { + return formatter::format(mgutility::enum_name(e), ctx); + } +}; + +#endif + +#endif // MGUTILITY_ENUM_NAME_HPP \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 index 3d68426..0000000 --- a/test/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(enum_name_test VERSION 0.1 LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 17) - -include_directories(AFTER PUBLIC ${CMAKE_SOURCE_DIR}/include) - -add_executable(${PROJECT_NAME} enum_name_test.cpp) -target_link_libraries(${PROJECT_NAME} doctest) - -add_test(NAME enum_name_test COMMAND enum_name_test) \ No newline at end of file diff --git a/test/enum_name_test.cpp b/test/enum_name_test.cpp deleted file mode 100644 index 266ddfc..0000000 --- a/test/enum_name_test.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include "doctest/doctest.h" -#include "enum_name.hpp" - - -enum class color : uint32_t { - alice_blue, // rgb(240,248,255) - antique_white, // rgb(250,235,215) - aqua, // rgb(0,255,255) - aquamarine, // rgb(127,255,212) - azure, // rgb(240,255,255) - beige, // rgb(245,245,220) - bisque, // rgb(255,228,196) - black, // rgb(0,0,0) - blanched_almond, // rgb(255,235,205) - blue, // rgb(0,0,255) - blue_violet, // rgb(138,43,226) - brown, // rgb(165,42,42) - burly_wood, // rgb(222,184,135) - cadet_blue, // rgb(95,158,160) - chartreuse, // rgb(127,255,0) - chocolate, // rgb(210,105,30) - coral, // rgb(255,127,80) - cornflower_blue, // rgb(100,149,237) - cornsilk, // rgb(255,248,220) - crimson, // rgb(220,20,60) - cyan, // rgb(0,255,255) - dark_blue, // rgb(0,0,139) - dark_cyan, // rgb(0,139,139) - dark_golden_rod, // rgb(184,134,11) - dark_gray, // rgb(169,169,169) - dark_green, // rgb(0,100,0) - dark_khaki, // rgb(189,183,107) - dark_magenta, // rgb(139,0,139) - dark_olive_green, // rgb(85,107,47) - dark_orange, // rgb(255,140,0) - dark_orchid, // rgb(153,50,204) - dark_red, // rgb(139,0,0) - dark_salmon, // rgb(233,150,122) - dark_sea_green, // rgb(143,188,143) - dark_slate_blue, // rgb(72,61,139) - dark_slate_gray, // rgb(47,79,79) - dark_turquoise, // rgb(0,206,209) - dark_violet, // rgb(148,0,211) - deep_pink, // rgb(255,20,147) - deep_sky_blue, // rgb(0,191,255) - dim_gray, // rgb(105,105,105) - dodger_blue, // rgb(30,144,255) - fire_brick, // rgb(178,34,34) - floral_white, // rgb(255,250,240) - forest_green, // rgb(34,139,34) - fuchsia, // rgb(255,0,255) - gainsboro, // rgb(220,220,220) - ghost_white, // rgb(248,248,255) - gold, // rgb(255,215,0) - golden_rod, // rgb(218,165,32) - gray, // rgb(128,128,128) - green, // rgb(0,128,0) - green_yellow, // rgb(173,255,47) - honey_dew, // rgb(240,255,240) - hot_pink, // rgb(255,105,180) - indian_red, // rgb(205,92,92) - indigo, // rgb(75,0,130) - ivory, // rgb(255,255,240) - khaki, // rgb(240,230,140) - lavender, // rgb(230,230,250) - lavender_blush, // rgb(255,240,245) - lawn_green, // rgb(124,252,0) - lemon_chiffon, // rgb(255,250,205) - light_blue, // rgb(173,216,230) - light_coral, // rgb(240,128,128) - light_cyan, // rgb(224,255,255) - light_golden_rod_yellow, // rgb(250,250,210) - light_gray, // rgb(211,211,211) - light_green, // rgb(144,238,144) - light_pink, // rgb(255,182,193) - light_salmon, // rgb(255,160,122) - light_sea_green, // rgb(32,178,170) - light_sky_blue, // rgb(135,206,250) - light_slate_gray, // rgb(119,136,153) - light_steel_blue, // rgb(176,196,222) - light_yellow, // rgb(255,255,224) - lime, // rgb(0,255,0) - lime_green, // rgb(50,205,50) - linen, // rgb(250,240,230) - magenta, // rgb(255,0,255) - maroon, // rgb(128,0,0) - medium_aquamarine, // rgb(102,205,170) - medium_blue, // rgb(0,0,205) - medium_orchid, // rgb(186,85,211) - medium_purple, // rgb(147,112,219) - medium_sea_green, // rgb(60,179,113) - medium_slate_blue, // rgb(123,104,238) - medium_spring_green, // rgb(0,250,154) - medium_turquoise, // rgb(72,209,204) - medium_violet_red, // rgb(199,21,133) - midnight_blue, // rgb(25,25,112) - mint_cream, // rgb(245,255,250) - misty_rose, // rgb(255,228,225) - moccasin, // rgb(255,228,181) - navajo_white, // rgb(255,222,173) - navy, // rgb(0,0,128) - old_lace, // rgb(253,245,230) - olive, // rgb(128,128,0) - olive_drab, // rgb(107,142,35) - orange, // rgb(255,165,0) - orange_red, // rgb(255,69,0) - orchid, // rgb(218,112,214) - pale_golden_rod, // rgb(238,232,170) - pale_green, // rgb(152,251,152) - pale_turquoise, // rgb(175,238,238) - pale_violet_red, // rgb(219,112,147) - papaya_whip, // rgb(255,239,213) - peach_puff, // rgb(255,218,185) - peru, // rgb(205,133,63) - pink, // rgb(255,192,203) - plum, // rgb(221,160,221) - powder_blue, // rgb(176,224,230) - purple, // rgb(128,0,128) - rebecca_purple, // rgb(102,51,153) - red, // rgb(255,0,0) - rosy_brown, // rgb(188,143,143) - royal_blue, // rgb(65,105,225) - saddle_brown, // rgb(139,69,19) - salmon, // rgb(250,128,114) - sandy_brown, // rgb(244,164,96) - sea_green, // rgb(46,139,87) - sea_shell, // rgb(255,245,238) - sienna, // rgb(160,82,45) - silver, // rgb(192,192,192) - sky_blue, // rgb(135,206,235) - slate_blue, // rgb(106,90,205) - slate_gray, // rgb(112,128,144) - snow, // rgb(255,250,250) - spring_green, // rgb(0,255,127) - steel_blue, // rgb(70,130,180) - tan, // rgb(210,180,140) - teal, // rgb(0,128,128) - thistle, // rgb(216,191,216) - tomato, // rgb(255,99,71) - turquoise, // rgb(64,224,208) - violet, // rgb(238,130,238) - wheat, // rgb(245,222,179) - white, // rgb(255,255,255) - white_smoke, // rgb(245,245,245) - yellow, // rgb(255,255,0) - yellow_green // rgb(154,205,50) -}; // enum class color - -namespace mgutility{ - template<> - struct enum_range - { - static constexpr auto min = 0; - static constexpr auto max = 150; - }; -} - - - -TEST_CASE("testing the enum name serialization") { - CHECK(mgutility::enum_name(color::blue) == "blue"); - CHECK(mgutility::enum_name(color::white_smoke) == "white_smoke"); - CHECK(mgutility::enum_name(color::yellow_green) == "yellow_green"); - CHECK(mgutility::enum_name(color::steel_blue) == "steel_blue"); - CHECK(mgutility::enum_name(color::pale_turquoise) == "pale_turquoise"); - CHECK(mgutility::enum_name(color::white_smoke) == "white_smoke"); - CHECK(mgutility::enum_name(color::light_golden_rod_yellow) == "light_golden_rod_yellow"); - CHECK(mgutility::enum_name(color::dark_olive_green) == "dark_olive_green"); - CHECK(mgutility::enum_name(color::azure) == "azure"); - CHECK(mgutility::enum_name(color::ghost_white) == "ghost_white"); - CHECK(mgutility::enum_name(color::maroon) == "maroon"); - CHECK(mgutility::enum_name(color::navy) == "navy"); - CHECK(mgutility::enum_name(color::spring_green) == "spring_green"); - CHECK(mgutility::enum_name(color::rebecca_purple) == "rebecca_purple"); - CHECK(mgutility::enum_name(color::red) == "red"); - CHECK(mgutility::enum_name(color::tan) == "tan"); - CHECK(mgutility::enum_name(color::mint_cream) == "mint_cream"); - CHECK(mgutility::enum_name(color::light_green) == "light_green"); - CHECK(mgutility::enum_name(color::dodger_blue) == "dodger_blue"); - CHECK(mgutility::enum_name(color::cornflower_blue) == "cornflower_blue"); - CHECK(mgutility::enum_name<-5, 0>(color::thistle) == ""); -} - -TEST_CASE("testing the enum name deserialization") { - CHECK(mgutility::to_enum("blue").value() == color::blue); - CHECK(mgutility::to_enum("white_smoke").value() == color::white_smoke); - CHECK(mgutility::to_enum("yellow_green").value() == color::yellow_green); - CHECK(mgutility::to_enum("steel_blue").value() == color::steel_blue); - CHECK(mgutility::to_enum( "pale_turquoise").value() == color::pale_turquoise); - CHECK(mgutility::to_enum("white_smoke").value() == color::white_smoke); - CHECK(mgutility::to_enum("light_golden_rod_yellow").value() == color::light_golden_rod_yellow); - CHECK(mgutility::to_enum("dark_olive_green").value() == color::dark_olive_green); - CHECK(mgutility::to_enum("azure").value() == color::azure); - CHECK(mgutility::to_enum("ghost_white").value() == color::ghost_white); - CHECK(mgutility::to_enum("maroon").value() == color::maroon); - CHECK(mgutility::to_enum("navy").value() == color::navy); - CHECK(mgutility::to_enum("spring_green").value() == color::spring_green); - CHECK(mgutility::to_enum("rebecca_purple").value() == color::rebecca_purple); - CHECK(mgutility::to_enum("red").value() == color::red); - CHECK(mgutility::to_enum("tan").value() == color::tan); - CHECK(mgutility::to_enum("mint_cream").value() == color::mint_cream); - CHECK(mgutility::to_enum("light_green").value() == color::light_green); - CHECK(mgutility::to_enum("dodger_blue").value() == color::dodger_blue); - CHECK(mgutility::to_enum("cornflower_blue").value() == color::cornflower_blue); - REQUIRE_THROWS(mgutility::to_enum("thistle").value()); - -} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..a52bc7e --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.14) +project( + enum_name_test + VERSION 0.1 + LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) + +include_directories(AFTER PUBLIC ${CMAKE_SOURCE_DIR}/include) + +add_executable(${PROJECT_NAME} test_enum_name.cpp) +target_link_libraries(${PROJECT_NAME} doctest) + +add_test(NAME enum_name_test COMMAND enum_name_test) diff --git a/tests/test_enum_name.cpp b/tests/test_enum_name.cpp new file mode 100644 index 0000000..3397995 --- /dev/null +++ b/tests/test_enum_name.cpp @@ -0,0 +1,210 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "doctest/doctest.h" +#include "enum_name.hpp" + +enum class color : uint32_t { + alice_blue, // rgb(240,248,255) + antique_white, // rgb(250,235,215) + aqua, // rgb(0,255,255) + aquamarine, // rgb(127,255,212) + azure, // rgb(240,255,255) + beige, // rgb(245,245,220) + bisque, // rgb(255,228,196) + black, // rgb(0,0,0) + blanched_almond, // rgb(255,235,205) + blue, // rgb(0,0,255) + blue_violet, // rgb(138,43,226) + brown, // rgb(165,42,42) + burly_wood, // rgb(222,184,135) + cadet_blue, // rgb(95,158,160) + chartreuse, // rgb(127,255,0) + chocolate, // rgb(210,105,30) + coral, // rgb(255,127,80) + cornflower_blue, // rgb(100,149,237) + cornsilk, // rgb(255,248,220) + crimson, // rgb(220,20,60) + cyan, // rgb(0,255,255) + dark_blue, // rgb(0,0,139) + dark_cyan, // rgb(0,139,139) + dark_golden_rod, // rgb(184,134,11) + dark_gray, // rgb(169,169,169) + dark_green, // rgb(0,100,0) + dark_khaki, // rgb(189,183,107) + dark_magenta, // rgb(139,0,139) + dark_olive_green, // rgb(85,107,47) + dark_orange, // rgb(255,140,0) + dark_orchid, // rgb(153,50,204) + dark_red, // rgb(139,0,0) + dark_salmon, // rgb(233,150,122) + dark_sea_green, // rgb(143,188,143) + dark_slate_blue, // rgb(72,61,139) + dark_slate_gray, // rgb(47,79,79) + dark_turquoise, // rgb(0,206,209) + dark_violet, // rgb(148,0,211) + deep_pink, // rgb(255,20,147) + deep_sky_blue, // rgb(0,191,255) + dim_gray, // rgb(105,105,105) + dodger_blue, // rgb(30,144,255) + fire_brick, // rgb(178,34,34) + floral_white, // rgb(255,250,240) + forest_green, // rgb(34,139,34) + fuchsia, // rgb(255,0,255) + gainsboro, // rgb(220,220,220) + ghost_white, // rgb(248,248,255) + gold, // rgb(255,215,0) + golden_rod, // rgb(218,165,32) + gray, // rgb(128,128,128) + green, // rgb(0,128,0) + green_yellow, // rgb(173,255,47) + honey_dew, // rgb(240,255,240) + hot_pink, // rgb(255,105,180) + indian_red, // rgb(205,92,92) + indigo, // rgb(75,0,130) + ivory, // rgb(255,255,240) + khaki, // rgb(240,230,140) + lavender, // rgb(230,230,250) + lavender_blush, // rgb(255,240,245) + lawn_green, // rgb(124,252,0) + lemon_chiffon, // rgb(255,250,205) + light_blue, // rgb(173,216,230) + light_coral, // rgb(240,128,128) + light_cyan, // rgb(224,255,255) + light_golden_rod_yellow, // rgb(250,250,210) + light_gray, // rgb(211,211,211) + light_green, // rgb(144,238,144) + light_pink, // rgb(255,182,193) + light_salmon, // rgb(255,160,122) + light_sea_green, // rgb(32,178,170) + light_sky_blue, // rgb(135,206,250) + light_slate_gray, // rgb(119,136,153) + light_steel_blue, // rgb(176,196,222) + light_yellow, // rgb(255,255,224) + lime, // rgb(0,255,0) + lime_green, // rgb(50,205,50) + linen, // rgb(250,240,230) + magenta, // rgb(255,0,255) + maroon, // rgb(128,0,0) + medium_aquamarine, // rgb(102,205,170) + medium_blue, // rgb(0,0,205) + medium_orchid, // rgb(186,85,211) + medium_purple, // rgb(147,112,219) + medium_sea_green, // rgb(60,179,113) + medium_slate_blue, // rgb(123,104,238) + medium_spring_green, // rgb(0,250,154) + medium_turquoise, // rgb(72,209,204) + medium_violet_red, // rgb(199,21,133) + midnight_blue, // rgb(25,25,112) + mint_cream, // rgb(245,255,250) + misty_rose, // rgb(255,228,225) + moccasin, // rgb(255,228,181) + navajo_white, // rgb(255,222,173) + navy, // rgb(0,0,128) + old_lace, // rgb(253,245,230) + olive, // rgb(128,128,0) + olive_drab, // rgb(107,142,35) + orange, // rgb(255,165,0) + orange_red, // rgb(255,69,0) + orchid, // rgb(218,112,214) + pale_golden_rod, // rgb(238,232,170) + pale_green, // rgb(152,251,152) + pale_turquoise, // rgb(175,238,238) + pale_violet_red, // rgb(219,112,147) + papaya_whip, // rgb(255,239,213) + peach_puff, // rgb(255,218,185) + peru, // rgb(205,133,63) + pink, // rgb(255,192,203) + plum, // rgb(221,160,221) + powder_blue, // rgb(176,224,230) + purple, // rgb(128,0,128) + rebecca_purple, // rgb(102,51,153) + red, // rgb(255,0,0) + rosy_brown, // rgb(188,143,143) + royal_blue, // rgb(65,105,225) + saddle_brown, // rgb(139,69,19) + salmon, // rgb(250,128,114) + sandy_brown, // rgb(244,164,96) + sea_green, // rgb(46,139,87) + sea_shell, // rgb(255,245,238) + sienna, // rgb(160,82,45) + silver, // rgb(192,192,192) + sky_blue, // rgb(135,206,235) + slate_blue, // rgb(106,90,205) + slate_gray, // rgb(112,128,144) + snow, // rgb(255,250,250) + spring_green, // rgb(0,255,127) + steel_blue, // rgb(70,130,180) + tan, // rgb(210,180,140) + teal, // rgb(0,128,128) + thistle, // rgb(216,191,216) + tomato, // rgb(255,99,71) + turquoise, // rgb(64,224,208) + violet, // rgb(238,130,238) + wheat, // rgb(245,222,179) + white, // rgb(255,255,255) + white_smoke, // rgb(245,245,245) + yellow, // rgb(255,255,0) + yellow_green // rgb(154,205,50) +}; // enum class color + +namespace mgutility { +template <> struct enum_range { + static constexpr auto min = 0; + static constexpr auto max = 150; +}; +} // namespace mgutility + +TEST_CASE("testing the enum name serialization") { + CHECK(mgutility::enum_name(color::blue) == "blue"); + CHECK(mgutility::enum_name(color::white_smoke) == "white_smoke"); + CHECK(mgutility::enum_name(color::yellow_green) == "yellow_green"); + CHECK(mgutility::enum_name(color::steel_blue) == "steel_blue"); + CHECK(mgutility::enum_name(color::pale_turquoise) == "pale_turquoise"); + CHECK(mgutility::enum_name(color::white_smoke) == "white_smoke"); + CHECK(mgutility::enum_name(color::light_golden_rod_yellow) == + "light_golden_rod_yellow"); + CHECK(mgutility::enum_name(color::dark_olive_green) == "dark_olive_green"); + CHECK(mgutility::enum_name(color::azure) == "azure"); + CHECK(mgutility::enum_name(color::ghost_white) == "ghost_white"); + CHECK(mgutility::enum_name(color::maroon) == "maroon"); + CHECK(mgutility::enum_name(color::navy) == "navy"); + CHECK(mgutility::enum_name(color::spring_green) == "spring_green"); + CHECK(mgutility::enum_name(color::rebecca_purple) == "rebecca_purple"); + CHECK(mgutility::enum_name(color::red) == "red"); + CHECK(mgutility::enum_name(color::tan) == "tan"); + CHECK(mgutility::enum_name(color::mint_cream) == "mint_cream"); + CHECK(mgutility::enum_name(color::light_green) == "light_green"); + CHECK(mgutility::enum_name(color::dodger_blue) == "dodger_blue"); + CHECK(mgutility::enum_name(color::cornflower_blue) == "cornflower_blue"); + CHECK(mgutility::enum_name<-5, 0>(color::thistle) == ""); +} + +TEST_CASE("testing the enum name deserialization") { + CHECK(mgutility::to_enum("blue").value() == color::blue); + CHECK(mgutility::to_enum("white_smoke").value() == color::white_smoke); + CHECK(mgutility::to_enum("yellow_green").value() == + color::yellow_green); + CHECK(mgutility::to_enum("steel_blue").value() == color::steel_blue); + CHECK(mgutility::to_enum("pale_turquoise").value() == + color::pale_turquoise); + CHECK(mgutility::to_enum("white_smoke").value() == color::white_smoke); + CHECK(mgutility::to_enum("light_golden_rod_yellow").value() == + color::light_golden_rod_yellow); + CHECK(mgutility::to_enum("dark_olive_green").value() == + color::dark_olive_green); + CHECK(mgutility::to_enum("azure").value() == color::azure); + CHECK(mgutility::to_enum("ghost_white").value() == color::ghost_white); + CHECK(mgutility::to_enum("maroon").value() == color::maroon); + CHECK(mgutility::to_enum("navy").value() == color::navy); + CHECK(mgutility::to_enum("spring_green").value() == + color::spring_green); + CHECK(mgutility::to_enum("rebecca_purple").value() == + color::rebecca_purple); + CHECK(mgutility::to_enum("red").value() == color::red); + CHECK(mgutility::to_enum("tan").value() == color::tan); + CHECK(mgutility::to_enum("mint_cream").value() == color::mint_cream); + CHECK(mgutility::to_enum("light_green").value() == color::light_green); + CHECK(mgutility::to_enum("dodger_blue").value() == color::dodger_blue); + CHECK(mgutility::to_enum("cornflower_blue").value() == + color::cornflower_blue); + REQUIRE_THROWS(mgutility::to_enum("thistle").value()); +} \ No newline at end of file