From a415cdc21a587023417ea6806098f0468d73726a Mon Sep 17 00:00:00 2001 From: mguludag Date: Sat, 8 Feb 2025 16:18:58 +0000 Subject: [PATCH] Refactor CMakeLists and update enum handling for improved clarity and consistency --- .devcontainer/devcontainer.json | 3 +- .github/workflows/c-cpp.yml | 1 + README.md | 2 +- example/CMakeLists.txt | 1 + example/main.cpp | 62 ++--- include/mgutility/_common/definitions.hpp | 2 +- .../reflection/detail/enum_for_each.hpp | 8 +- .../reflection/detail/enum_name_impl.hpp | 54 ++--- include/mgutility/reflection/detail/meta.hpp | 2 +- include/mgutility/reflection/enum_name.hpp | 13 +- include/mgutility/std/optional.hpp | 34 +-- include/mgutility/std/string_view.hpp | 225 +++++++++++++++--- 12 files changed, 288 insertions(+), 119 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6806fc4..e9fc04c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -49,7 +49,8 @@ "usernamehw.errorlens", "twxs.cmake", "ms-vscode.cpptools", - "ms-vscode.cmake-tools" + "ms-vscode.cmake-tools", + "GitHub.copilot" ] } } diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 1d38173..884e323 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -40,6 +40,7 @@ jobs: - { compiler: gcc, version: 9, build_type: Release, cppstd: 17 } - { compiler: gcc, version: 10, build_type: Debug, cppstd: 20 } - { compiler: gcc, version: 12, build_type: Release, cppstd: 20 } + - { compiler: gcc, version: 13, build_type: Release, cppstd: 20 } - { compiler: clang, version: 11, build_type: Release, cppstd: 11 } - { compiler: clang, version: 11, build_type: Release, cppstd: 14 } - { compiler: clang, version: 11, build_type: Release, cppstd: 17 } diff --git a/README.md b/README.md index d161fdb..c5bfcbc 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ FetchContent_MakeAvailable(enum_name) target_link_libraries(${PROJECT_NAME} PRIVATE mgutility::enum_name) ``` -## Example usage ([try it!](https://godbolt.org/z/5Ye185MWa)) +## Example usage ([try it!](https://godbolt.org/z/1nqrj78vb)) ```C++ #include diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 2f6401c..855d60d 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -4,6 +4,7 @@ project( VERSION 0.1 LANGUAGES CXX) + add_executable(${PROJECT_NAME} main.cpp) target_link_libraries(${PROJECT_NAME} mgutility::enum_name) diff --git a/example/main.cpp b/example/main.cpp index 1a4c853..dc4fd7a 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -1,20 +1,20 @@ #include #include "mgutility/reflection/enum_name.hpp" +#include "mgutility/std/string_view.hpp" #if defined(__cpp_lib_print) #include #include #endif -enum class Position : std::uint8_t { +enum class Position { Top = 1 << 0, Right = 1 << 1, Bottom = 1 << 2, Left = 1 << 3 }; - // Define bitwise OR operator for Position constexpr static auto operator|(Position lhs, Position rhs) -> Position { return static_cast(mgutility::enum_to_underlying(lhs) | @@ -27,7 +27,6 @@ constexpr static auto operator&(Position lhs, Position rhs) -> Position { mgutility::enum_to_underlying(rhs)); } - // Define the range for Position enum values (Option 1) template <> struct mgutility::enum_range { static constexpr auto min = 0; // Minimum value @@ -37,36 +36,41 @@ template <> struct mgutility::enum_range { // Specialize individual or all enum names template <> struct mgutility::custom_enum { static constexpr mgutility::flat_map map{ - {.first = Position::Top, .second = "TOP"}, - {.first = Position::Right, .second = "RIGHT"}, - {.first = Position::Bottom, .second = "BOTTOM"}, - {.first = Position::Left, .second = "LEFT"}, - {.first = - Position::Top | Position::Right | Position::Bottom | Position::Left, - .second = "CENTER"}}; + {Position::Top, "TOP"}, + {Position::Right, "RIGHT"}, + {Position::Bottom, "BOTTOM"}, + {Position::Left, "LEFT"}, + {Position::Top | Position::Right | Position::Bottom | Position::Left, + "CENTER"}}; }; int main() { - int aaa = 0; - // Specify enum range when call enum_name function (Option 2) // Lambda function to get enum name - auto enum_name = [](Position c) { return mgutility::enum_name<0, 16>(c); }; + auto enum_name = [](Position pos) { + return mgutility::enum_name<0, 16>(pos); + }; - auto x = Position::Left; - auto y = mgutility::to_enum("CENTER"); // Convert string to enum + auto posLeft = Position::Left; + auto posCenter = + mgutility::to_enum("CENTER"); // Convert string to enum -#if defined(__cpp_lib_constexpr_string) && defined(__GNUC__) && \ - !defined(__clang__) && __GNUC__ > 11 +#if MGUTILITY_CPLUSPLUS > 201402L && \ + ((defined(__clang__) && __clang_major__ > 11) || (defined(__GNUC__) && __GNUC__ > 11)) static_assert(mgutility::enum_name(Position::Top | Position::Right) == - "TOP|RIGHT"); // Compile-time check - static_assert(mgutility::to_enum("BOTTOM|LEFT") == - (Position::Bottom | Position::Left)); // Compile-time check + "TOP|RIGHT", + "Compile-time check failed: TOP|RIGHT"); +#if MGUTILITY_CPLUSPLUS > 201703L && \ + (defined(__clang__) || (defined(__GNUC__) && __GNUC__ > 11)) + static_assert(mgutility::to_enum("BOTTOM|LEFT").value() == + (Position::Bottom | Position::Left), + "Compile-time check failed"); +#endif #endif -#if defined(__cpp_lib_print) || __GNUC__ > 12 +#if defined(__cpp_lib_print) - // Print each Position and its underlying value using ranges +// Print each Position and its underlying value using ranges auto positions = mgutility::enum_for_each() | std::ranges::views::filter([](auto &&pair) { @@ -82,18 +86,18 @@ int main() { #else // Print each Position and its underlying value using a for loop - for (auto &&e : mgutility::enum_for_each()) { - if (!e.second.empty() && - e.second.find('|') == mgutility::string_view::npos) { - std::cout << mgutility::enum_to_underlying(e.first) << " \t: " << e.second - << '\n'; + for (auto &&elem : mgutility::enum_for_each()) { + if (!elem.second.empty() && + elem.second.find('|') == mgutility::string_view::npos) { + std::cout << mgutility::enum_to_underlying(elem.first) + << " \t: " << elem.second << '\n'; } } #endif // Print the value of x - std::cout << '\n' << x << '\n'; + std::cout << '\n' << posLeft << '\n'; // Print the name of y or "TOP" if y is not valid - std::cout << y.value_or(Position::Top) << '\n'; + std::cout << posCenter.value_or(Position::Top) << '\n'; } \ No newline at end of file diff --git a/include/mgutility/_common/definitions.hpp b/include/mgutility/_common/definitions.hpp index cfa242d..413111a 100644 --- a/include/mgutility/_common/definitions.hpp +++ b/include/mgutility/_common/definitions.hpp @@ -64,7 +64,7 @@ SOFTWARE. * MGUTILITY_CNSTXPR_CLANG_WA is defined as constexpr. Otherwise, it is defined * as empty. */ -#if MGUTILITY_CPLUSPLUS > 201703L && defined(__cpp_lib_constexpr_string) + #if (MGUTILITY_CPLUSPLUS >= 201703L && !defined(__clang__)) || (defined(__clang__) && __clang_major__ > 11 && MGUTILITY_CPLUSPLUS >= 201703L) #define MGUTILITY_CNSTXPR_CLANG_WA constexpr #else #define MGUTILITY_CNSTXPR_CLANG_WA diff --git a/include/mgutility/reflection/detail/enum_for_each.hpp b/include/mgutility/reflection/detail/enum_for_each.hpp index 1365189..49b54ee 100644 --- a/include/mgutility/reflection/detail/enum_for_each.hpp +++ b/include/mgutility/reflection/detail/enum_for_each.hpp @@ -31,6 +31,10 @@ SOFTWARE. #include #include +#ifndef MGUTILITY_ENUM_NAME_BUFFER_SIZE +#define MGUTILITY_ENUM_NAME_BUFFER_SIZE 512U +#endif + namespace mgutility { namespace detail { /** @@ -44,7 +48,7 @@ namespace detail { */ template using string_or_view_t = - typename std::conditional::value, std::string, + typename std::conditional::value, mgutility::fixed_string, mgutility::string_view>::type; /** @@ -89,7 +93,7 @@ template class enum_for_each { * * @param value The initial position of the iterator. */ - enum_iter(iter_type value) : m_pos{value} {} + explicit enum_iter(iter_type value) : m_pos{value} {} /** * @brief Pre-increment operator. diff --git a/include/mgutility/reflection/detail/enum_name_impl.hpp b/include/mgutility/reflection/detail/enum_name_impl.hpp index 115455b..14040d6 100644 --- a/include/mgutility/reflection/detail/enum_name_impl.hpp +++ b/include/mgutility/reflection/detail/enum_name_impl.hpp @@ -27,6 +27,7 @@ SOFTWARE. #include "enum_for_each.hpp" #include "meta.hpp" +#include "mgutility/_common/definitions.hpp" #include "mgutility/std/optional.hpp" #include "mgutility/std/string_view.hpp" @@ -87,7 +88,7 @@ struct enum_type { typename Enum, Enum e, detail::enable_if_t::value, bool> = true> MGUTILITY_CNSTXPR static auto name() noexcept -> mgutility::string_view { - for (auto &pair : mgutility::custom_enum::map) { + for (const auto &pair : mgutility::custom_enum::map) { if (pair.first == e) { return pair.second; } @@ -113,7 +114,7 @@ struct enum_type { typename Enum, Enum e, detail::enable_if_t::value, bool> = true> MGUTILITY_CNSTXPR static auto name() noexcept -> mgutility::string_view { - for (auto &pair : mgutility::custom_enum::map) { + for (const auto &pair : mgutility::custom_enum::map) { if (pair.first == e) { return pair.second; } @@ -124,20 +125,21 @@ struct enum_type { MGUTILITY_CNSTXPR auto result = str.substr(index, str.size() - lastidxenumname[0] - index); MGUTILITY_CNSTXPR auto is_invalid = - result.rfind(lastidxenumname[5]) != result.npos || (result.size() > 4 && result[4] == lastidxenumname[4]); + result.rfind(lastidxenumname[5]) != mgutility::string_view::npos || + (result.size() > 4 && result[4] == lastidxenumname[4]); return is_invalid ? "" : result; } private: static constexpr int lastidxenumname[] = #if defined(__clang__) - {1, 1, ' ', ':', '(', + {1, 1, ' ', ':', '(', #if __clang_major__ < 13 - ',' + ',' #else - ')' + ')' #endif - }; + }; #elif defined(_MSC_VER) {21, 0, ',', ':', '<', ')'}; #elif defined(__GNUC__) @@ -147,7 +149,7 @@ struct enum_type { #else 157, #endif - 5, ' ', ':', '(', ')'}; + 5, ' ', ':', '(', ')'}; #endif }; @@ -194,10 +196,11 @@ template MGUTILITY_CNSTXPR inline auto to_enum_impl(mgutility::string_view str) noexcept -> mgutility::optional { MGUTILITY_CNSTXPR_CLANG_WA auto arr = get_enum_array(); - 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)}; + + const auto index{detail::find(arr, str)}; + return index == 0 + ? mgutility::nullopt + : mgutility::optional{static_cast(index + Min - 1)}; } /** @@ -256,10 +259,10 @@ MGUTILITY_CNSTXPR auto to_enum_bitmask_impl(mgutility::string_view str) noexcept */ template ::value, bool> = true> -MGUTILITY_CNSTXPR auto enum_name_impl(Enum e) noexcept +MGUTILITY_CNSTXPR auto enum_name_impl(Enum enumValue) noexcept -> mgutility::string_view { MGUTILITY_CNSTXPR auto arr = get_enum_array(); - const auto index{(Min < 0 ? -Min : Min) + static_cast(e) + 1}; + const auto index{(Min < 0 ? -Min : Min) + static_cast(enumValue) + 1}; return arr[(index < Min || index > arr.size() - 1) ? 0 : index]; } @@ -275,32 +278,30 @@ MGUTILITY_CNSTXPR auto enum_name_impl(Enum e) noexcept */ template ::value, bool> = true> -MGUTILITY_CNSTXPR_CLANG_WA inline auto enum_name_impl(Enum e) noexcept - -> detail::string_or_view_t { + MGUTILITY_CNSTXPR_CLANG_WA auto enum_name_impl(Enum enumValue) noexcept + -> mgutility::fixed_string { // Get the array of enum names MGUTILITY_CNSTXPR_CLANG_WA auto arr = get_enum_array(); // Calculate the index in the array - const auto index = (Min < 0 ? -Min : Min) + static_cast(e) + 1; + const auto index = (Min < 0 ? -Min : Min) + static_cast(enumValue) + 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}; + return mgutility::fixed_string{}.append( + name); } // Construct bitmasked name - std::string bitmasked_name; + mgutility::fixed_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)) { + !detail::is_digit(arr[idx][0]) && + (enumValue & static_cast(i)) == static_cast(i)) { bitmasked_name.append(arr[idx]).append("|"); } } @@ -310,10 +311,7 @@ MGUTILITY_CNSTXPR_CLANG_WA inline auto enum_name_impl(Enum e) noexcept bitmasked_name.pop_back(); } - if (bitmasked_name.find('|') != std::string::npos) { - return bitmasked_name; - } - return std::string{""}; + return bitmasked_name; } } // namespace detail } // namespace mgutility diff --git a/include/mgutility/reflection/detail/meta.hpp b/include/mgutility/reflection/detail/meta.hpp index 976e7ba..6f28844 100644 --- a/include/mgutility/reflection/detail/meta.hpp +++ b/include/mgutility/reflection/detail/meta.hpp @@ -164,7 +164,7 @@ template struct pair { }; template -#if MGUTILITY_CPLUSPLUS > 201103L || defined(__GNUC__) && !defined(__clang__) +#if MGUTILITY_CPLUSPLUS > 201402L || defined(__GNUC__) && !defined(__clang__) using flat_map = std::initializer_list>; #else using flat_map = pair[]; diff --git a/include/mgutility/reflection/enum_name.hpp b/include/mgutility/reflection/enum_name.hpp index 47c2df7..430ef7f 100644 --- a/include/mgutility/reflection/enum_name.hpp +++ b/include/mgutility/reflection/enum_name.hpp @@ -27,6 +27,7 @@ SOFTWARE. #include "detail/enum_name_impl.hpp" + namespace mgutility { /** @@ -37,10 +38,10 @@ namespace mgutility { * @return The underlying integer value of the enum. */ template -constexpr auto enum_to_underlying(Enum e) noexcept +constexpr auto enum_to_underlying(Enum enumValue) noexcept -> detail::underlying_type_t { static_assert(std::is_enum::value, "Value is not an Enum type!"); - return static_cast>(e); + return static_cast>(enumValue); } /** @@ -53,11 +54,11 @@ constexpr auto enum_to_underlying(Enum e) noexcept * @return A string view or string representing the name of the enum value. */ template -MGUTILITY_CNSTXPR auto enum_name(Enum e) noexcept +MGUTILITY_CNSTXPR auto enum_name(Enum enumValue) 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); + return detail::enum_name_impl(enumValue); } /** @@ -71,11 +72,11 @@ MGUTILITY_CNSTXPR auto enum_name(Enum e) noexcept */ template ::min, int Max = enum_range::max> -MGUTILITY_CNSTXPR auto enum_name(Enum e) noexcept +MGUTILITY_CNSTXPR auto enum_name(Enum enumValue) 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); + return detail::enum_name_impl(enumValue); } /** diff --git a/include/mgutility/std/optional.hpp b/include/mgutility/std/optional.hpp index ef74634..d32fe12 100644 --- a/include/mgutility/std/optional.hpp +++ b/include/mgutility/std/optional.hpp @@ -47,7 +47,7 @@ struct bad_optional_access : public std::exception { * * @return A C-string with the exception message. */ - const char *what() const noexcept { return "optional has no value"; } + const char *what() const noexcept override { return "optional has no value"; } }; struct nullopt_t; @@ -62,7 +62,7 @@ template class optional { /** * @brief Constructs an empty optional. */ - MGUTILITY_CNSTXPR inline optional(nullopt_t &) + MGUTILITY_CNSTXPR inline explicit optional(nullopt_t & /*unused*/) : dummy_{0}, has_value_{false} {} /** @@ -77,7 +77,7 @@ template class optional { * @param args The arguments to construct the value. */ template - MGUTILITY_CNSTXPR inline optional(Args &&...args) + MGUTILITY_CNSTXPR inline explicit optional(Args &&...args) : value_{T{std::forward(args)...}}, has_value_{true} {} /** @@ -85,8 +85,8 @@ template class optional { * * @param value The value to initialize with. */ - MGUTILITY_CNSTXPR inline optional(T &&value) - : value_{value}, has_value_{true} {} + MGUTILITY_CNSTXPR inline explicit optional(T &&value) + : value_{std::move(value)}, has_value_{true} {} /** * @brief Copy constructor. @@ -102,14 +102,14 @@ template class optional { * @param other The other optional to move. */ MGUTILITY_CNSTXPR inline optional(optional &&other) - : value_{other.value_}, has_value_{other.has_value_} { + noexcept : value_{other.value_}, has_value_{other.has_value_} { other.reset(); } /** * @brief Destructor. */ - inline ~optional() { has_value_ = false; } + ~optional() = default; /** * @brief Copy assignment operator. @@ -129,7 +129,7 @@ template class optional { * @param other The other optional to move. * @return A reference to this optional. */ - MGUTILITY_CNSTXPR inline optional &operator=(optional &&other) { + MGUTILITY_CNSTXPR inline optional &operator=(optional &&other) noexcept { value_ = other.value_; has_value_ = other.has_value_; other.reset(); @@ -141,14 +141,14 @@ template class optional { * * @param other The other optional to swap with. */ - MGUTILITY_CNSTXPR inline void swap(optional &&other) { + MGUTILITY_CNSTXPR inline void swap(optional &other) noexcept { 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); + auto hval = other.has_value_; + other.has_value_ = has_value_; + has_value_ = hval; } /** @@ -172,8 +172,9 @@ template class optional { * @throws bad_optional_access if the optional has no value. */ MGUTILITY_CNSTXPR inline T &value() { - if (!has_value_) + if (!has_value_) { throw bad_optional_access(); +} return value_; } @@ -184,8 +185,9 @@ template class optional { * @throws bad_optional_access if the optional has no value. */ MGUTILITY_CNSTXPR inline T &value() const { - if (!has_value_) + if (!has_value_) { throw bad_optional_access(); +} return value_; } @@ -196,7 +198,7 @@ template class optional { * @return The stored value or the default value. */ MGUTILITY_CNSTXPR inline T value_or(T &&value) { - return has_value_ ? value_ : value; + return has_value_ ? value_ : std::move(value); } /** @@ -207,7 +209,7 @@ template class optional { * @return The stored value or the default value. */ MGUTILITY_CNSTXPR inline T value_or(T &&value) const { - return has_value_ ? value_ : value; + return has_value_ ? value_ : std::move(value); } /** diff --git a/include/mgutility/std/string_view.hpp b/include/mgutility/std/string_view.hpp index a839770..149d799 100644 --- a/include/mgutility/std/string_view.hpp +++ b/include/mgutility/std/string_view.hpp @@ -32,10 +32,8 @@ SOFTWARE. #include "mgutility/_common/definitions.hpp" namespace mgutility { - -#if MGUTILITY_CPLUSPLUS < 201703L - namespace detail { + /** * @brief Computes the length of a C-string at compile-time. * @@ -43,12 +41,52 @@ namespace detail { * @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 { +constexpr auto strlen_constexpr(const char *str, size_t sz = 0) noexcept -> size_t { return str[sz] == '\0' ? sz : strlen_constexpr(str, ++sz); } + +/** + * @brief Checks if a character is a digit. + * + * @param character The character to check. + * @return True if the character is a digit, otherwise false. + */ +constexpr auto is_digit(char character) noexcept -> bool { + return character >= '0' && character <= '9'; +} + +/** + * @brief Compares two C-strings up to a given number of characters. + * + * @param lhs The left-hand side C-string. + * @param rhs The right-hand side C-string. + * @param count The maximum number of characters to compare. + * @return An integer less than, equal to, or greater than zero if lhs is found, + * respectively, to be less than, to match, or be greater than rhs. + */ + MGUTILITY_CNSTXPR int strncmp_constexpr(const char *lhs, const char *rhs, size_t count) noexcept { + for (size_t i = 0; i < count; ++i) { + if (lhs[i] != rhs[i] || lhs[i] == '\0' || rhs[i] == '\0') { + return static_cast(lhs[i]) - static_cast(rhs[i]); + } + } + return 0; +} + +template +MGUTILITY_CNSTXPR auto find(const Range& rng, const Pred& pred) -> size_t { + for (int i = 1; i < rng.size(); ++i) { + if (pred == rng[i]) { + return i; + } + } + return 0; +}; + } // namespace detail +#if MGUTILITY_CPLUSPLUS < 201703L + /** * @brief A basic string view class template. * @@ -74,7 +112,7 @@ template class basic_string_view { * * @param str The std::string. */ - constexpr inline basic_string_view( + constexpr basic_string_view( const std::basic_string &str) noexcept : data_(str.c_str()), size_(str.size()) {} @@ -84,7 +122,7 @@ template class basic_string_view { * @param str The C-string. * @param len The length of the string. */ - constexpr inline basic_string_view(const Char *str, size_t len) noexcept + constexpr basic_string_view(const Char *str, size_t len) noexcept : data_(str), size_(len) {} /** @@ -92,7 +130,7 @@ template class basic_string_view { * * @param other The other basic_string_view to copy. */ - constexpr inline basic_string_view(const basic_string_view &other) + constexpr basic_string_view(const basic_string_view &other) : data_(other.data_), size_(other.size_) {} /** @@ -100,7 +138,7 @@ template class basic_string_view { * * @param other The other basic_string_view to move. */ - constexpr inline basic_string_view(basic_string_view &&other) noexcept + constexpr basic_string_view(basic_string_view &&other) noexcept : data_(std::move(other.data_)), size_(std::move(other.size_)) {} /** @@ -110,11 +148,7 @@ template class basic_string_view { * @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; - } + operator=(const basic_string_view &other) noexcept = default; /** * @brief Move assignment operator. @@ -125,7 +159,7 @@ template class basic_string_view { MGUTILITY_CNSTXPR inline basic_string_view & operator=(basic_string_view &&other) noexcept { data_ = std::move(other.data_); - size_ = std::move(other.size_); + size_ = other.size_; return *this; } @@ -135,7 +169,7 @@ template class basic_string_view { * @param index The index. * @return The character at the index. */ - constexpr inline const Char operator[](size_t index) const noexcept { + constexpr const Char operator[](size_t index) const noexcept { return data_[index]; } @@ -144,35 +178,35 @@ template class basic_string_view { * * @return A pointer to the first character. */ - constexpr inline const Char *begin() const noexcept { return data_; } + constexpr 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_); } + constexpr 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; } + constexpr 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_; } + constexpr 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_; } + constexpr const Char *data() const noexcept { return data_; } /** * @brief Returns a substring view. @@ -181,7 +215,7 @@ template class basic_string_view { * @param len The length of the substring. * @return A basic_string_view representing the substring. */ - constexpr inline basic_string_view + constexpr basic_string_view substr(size_t begin, size_t len = 0U) const noexcept { return basic_string_view(data_ + begin, len == 0U ? size_ - begin : len); @@ -194,7 +228,7 @@ template class basic_string_view { * @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 { + constexpr 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 @@ -208,7 +242,7 @@ template class basic_string_view { * @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 { + constexpr size_t find(Char c, size_t pos = 0) const noexcept { return c == data_[pos] ? pos : pos < size_ ? find(c, ++pos) : npos; } @@ -219,11 +253,11 @@ template class 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 + MGUTILITY_CNSTXPR friend 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; + detail::strncmp_constexpr(lhs.data_, rhs.data_, lhs.size_) == 0; } /** @@ -233,10 +267,10 @@ template class 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, + MGUTILITY_CNSTXPR friend 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; + return ((lhs.size_ == detail::strlen_constexpr(rhs)) && + (detail::strncmp_constexpr(lhs.data_, rhs, lhs.size_) == 0)); } /** @@ -246,7 +280,7 @@ template class 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 + MGUTILITY_CNSTXPR friend bool operator!=(basic_string_view lhs, basic_string_view rhs) noexcept { return !(lhs == rhs); @@ -259,7 +293,7 @@ template class 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, + MGUTILITY_CNSTXPR friend bool operator!=(basic_string_view lhs, const Char *rhs) noexcept { return !(lhs == rhs); } @@ -269,14 +303,14 @@ template class basic_string_view { * * @return An std::string representing the same string. */ - inline operator std::string() { return std::string(data_, size_); } + 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_); } + operator std::string() const { return std::string(data_, size_); } /** * @brief Stream insertion operator. @@ -285,7 +319,7 @@ template class basic_string_view { * @param sv The basic_string_view. * @return A reference to the output stream. */ - friend inline std::ostream &operator<<(std::ostream &os, + friend std::ostream &operator<<(std::ostream &os, const basic_string_view &sv) { for (auto c : sv) { os << c; @@ -309,6 +343,129 @@ using string_view = std::string_view; #endif +template class fixed_string { +public: + template + MGUTILITY_CNSTXPR static auto make(const char (&str)[M]) -> fixed_string { + return fixed_string{str}; + } + + MGUTILITY_CNSTXPR fixed_string() = default; + + // Constructor to initialize from a string literal + MGUTILITY_CNSTXPR explicit fixed_string(const char (&str)[N]) { + for (size_t i = 0; i < N - 1; ++i) { + data[i] = str[i]; + } + cursor = N - 1; + data[cursor] = '\0'; + } + + // Concatenation operator + template + MGUTILITY_CNSTXPR auto operator+(const fixed_string &other) const + -> fixed_string { + fixed_string result{}; + size_t idx = 0; + for (; idx < N - 1; ++idx) { + result.data[idx] = data[idx]; + } + for (size_t j = 0; j < M; ++j) { + result.data[idx + j] = other.data[j]; + } + result.cursor = N + M - 2; + result.data[result.cursor] = '\0'; + return result; + } + + // Concatenation operator + template + MGUTILITY_CNSTXPR auto operator+(const char (&str)[M]) const + -> fixed_string { + return *this + fixed_string{str}; + } + + template + MGUTILITY_CNSTXPR auto append(const char (&str)[M]) -> fixed_string & { + static_assert(N > M, + "Capacity needs to be greater than string to be appended!"); + for (size_t i = 0; i < M - 1; ++i) { + data[cursor++] = str[i]; + } + data[cursor] = '\0'; + return *this; + } + + MGUTILITY_CNSTXPR auto append(string_view str) -> fixed_string & { + for (char chr : str) { + data[cursor++] = chr; + } + data[cursor] = '\0'; + return *this; + } + + MGUTILITY_CNSTXPR auto pop_back() -> void { + if (cursor > 0) { + data[--cursor] = '\0'; + } + } + + MGUTILITY_CNSTXPR auto size() const -> size_t { return cursor; } + + constexpr size_t find(char c, size_t pos = 0) const noexcept { + return c == data[pos] ? pos : (pos < cursor ? find(c, ++pos) : npos); + } + + // Conversion to std::string_view for easy printing + MGUTILITY_CNSTXPR explicit operator string_view() const { + return string_view(data, cursor); + } + + constexpr bool empty() const noexcept { return cursor == 0; } + + constexpr const char &operator[](size_t index) const noexcept { + return data[index]; + } + + MGUTILITY_CNSTXPR inline bool operator==(const char *rhs) const { + return string_view(*this) == rhs; + } + + friend std::ostream &operator<<(std::ostream &os, + const fixed_string &str) { + for (size_t i = 0; i < str.cursor; ++i) { + os << str.data[i]; + } + return os; + } + + static constexpr auto npos = -1; + +private: + char data[N]{'\0'}; + size_t cursor{}; +}; + } // namespace mgutility +#if defined(__cpp_lib_format) + +#include + +/** + * @brief Formatter for enum types for use with std::format. + * + * @tparam Enum The enum type. + */ +template +struct std::formatter> + : formatter { + auto constexpr format(const mgutility::fixed_string &str, + format_context &ctx) const { + return formatter::format(std::string_view(str), ctx); + } +}; + +#endif + #endif // STRING_STRING_VIEW_HPP \ No newline at end of file