diff --git a/include/mgutility/reflection/detail/enum_name_impl.hpp b/include/mgutility/reflection/detail/enum_name_impl.hpp index 3249c38..e82a9b4 100644 --- a/include/mgutility/reflection/detail/enum_name_impl.hpp +++ b/include/mgutility/reflection/detail/enum_name_impl.hpp @@ -89,14 +89,6 @@ namespace detail { * compile-time. */ struct enum_type { - - template -#ifdef MGUTILITY_ENUM_NAME_OPTIMIZE_FOR_SIZE - using string_type = mgutility::fixed_string::size>; -#else - using string_type = mgutility::string_view; -#endif - private: /** * @brief Extracts raw name from compiler's __PRETTY_FUNCTION__. @@ -106,7 +98,7 @@ struct enum_type { * @return The raw string_view from __PRETTY_FUNCTION__. */ template - MGUTILITY_CNSTXPR static mgutility::string_view raw_name() noexcept { + MGUTILITY_CNSTXPR static auto raw_name() noexcept -> mgutility::string_view { #if defined(__GNUC__) && !defined(__clang__) && MGUTILITY_CPLUSPLUS >= 201402L #define PREFIX \ MGUTILITY_STRLEN("static constexpr mgutility::string_view " \ @@ -142,7 +134,9 @@ struct enum_type { * @return The parsed enum name. */ template - MGUTILITY_CNSTXPR static string_type parse() noexcept { + MGUTILITY_CNSTXPR static auto + parse(mgutility::fixed_string::size> &blob) noexcept + -> mgutility::string_view { auto str = raw_name(); #if defined(__clang__) || defined(__GNUC__) #if defined(__clang__) @@ -185,7 +179,10 @@ struct enum_type { return {}; } - return string_type(result.substr(result.rfind(':') + 1)); + auto view_begin = blob.size(); + blob.append(result.substr(result.rfind(':') + 1)); + + return blob.view().substr(view_begin); } public: @@ -197,8 +194,10 @@ struct enum_type { * @return The name of the enum value. */ template - MGUTILITY_CNSTXPR static string_type name() noexcept { - return parse(); + MGUTILITY_CNSTXPR static auto + name(mgutility::fixed_string::size> &blob) noexcept + -> mgutility::string_view { + return parse(blob); } }; @@ -218,14 +217,16 @@ template struct enum_array_cache; */ template struct enum_array_cache> { + #if MGUTILITY_CPLUSPLUS >= 201402L // C++17+: fully constexpr // NOLINTNEXTLINE [readability-redundant-inline-specifier] - static inline constexpr std::array, - sizeof...(Is) + 1> + static inline constexpr std::array value() { - std::array, sizeof...(Is) + 1> arr{ - "", enum_type::template name()...}; + + auto blob = mgutility::fixed_string::size>{}; + std::array arr{ + "", enum_type::template name(blob)...}; constexpr auto map = mgutility::custom_enum::map; @@ -234,27 +235,33 @@ struct enum_array_cache> { const auto idx = static_cast(raw - Min + 1); if (idx >= 1 && idx < arr.size()) { - arr[idx] = enum_type::string_type(pair.second); + arr[idx] = pair.second; } } return arr; } #else + + static auto get_blob() noexcept + -> mgutility::fixed_string::size> & { + static mgutility::fixed_string::size> blob; + return blob; + } + // C++11: lazy runtime array - static const std::array, sizeof...(Is) + 1> & - value() { - static const std::array, sizeof...(Is) + 1> - arr = [] { - std::array, sizeof...(Is) + 1> tmp{ - "", enum_type::template name()...}; + static const std::array &value() { + static const std::array arr = + [] { + std::array tmp{ + "", enum_type::template name(get_blob())...}; for (const auto &pair : mgutility::custom_enum::map) { auto idx = static_cast(static_cast(pair.first) - Min + 1); if (idx >= 1 && idx < tmp.size()) { - tmp[idx] = enum_type::string_type(pair.second); + tmp[idx] = pair.second; } } @@ -278,11 +285,11 @@ template MGUTILITY_CNSTXPR auto get_enum_array(detail::enum_sequence /*unused*/) noexcept #if MGUTILITY_CPLUSPLUS >= 201402L - -> std::array, sizeof...(Is) + 1> { + -> std::array { return enum_array_cache>::value(); #else - -> const std::array, sizeof...(Is) + 1> & { + -> const std::array & { return enum_array_cache>::value(); #endif @@ -301,11 +308,11 @@ template ::min, int Max = mgutility::enum_range::max> MGUTILITY_CNSTXPR auto get_enum_array() noexcept #if MGUTILITY_CPLUSPLUS >= 201402L - -> std::array, Max - Min + 2> { + -> std::array { return get_enum_array( detail::make_enum_sequence()); #else - -> const std::array, Max - Min + 2> & { + -> const std::array & { return get_enum_array( detail::make_enum_sequence()); #endif diff --git a/include/mgutility/reflection/detail/meta.hpp b/include/mgutility/reflection/detail/meta.hpp index 02c2116..818a456 100644 --- a/include/mgutility/reflection/detail/meta.hpp +++ b/include/mgutility/reflection/detail/meta.hpp @@ -64,6 +64,16 @@ namespace detail { #define MGUTILITY_ENUM_NAME_BUFFER_SIZE 128U #endif +/** + * @brief Defines the MGUTILITY_ENUM_NAME_BUFFER_SIZE macro. + * + * This macro defines the size of the buffer used for enum names. + */ +#ifndef MGUTILITY_ENUM_NAME_BLOB_SIZE +// NOLINTNEXTLINE [cppcoreguidelines-macro-usage] +#define MGUTILITY_ENUM_NAME_BLOB_SIZE 1024U * 4U +#endif + /** * @brief Trait to check if a type is a scoped enumeration. * @@ -235,6 +245,10 @@ template struct enum_name_buffer { static constexpr auto size = MGUTILITY_ENUM_NAME_BUFFER_SIZE; }; +template struct enum_name_blob { + static constexpr auto size = MGUTILITY_ENUM_NAME_BLOB_SIZE; +}; + } // namespace mgutility #endif // DETAIL_META_HPP \ No newline at end of file diff --git a/single_include/mgutility_enum_name.hpp b/single_include/mgutility_enum_name.hpp index 33ad8ba..a486571 100644 --- a/single_include/mgutility_enum_name.hpp +++ b/single_include/mgutility_enum_name.hpp @@ -908,190 +908,210 @@ struct fmt::formatter> : formatter { namespace mgutility { +#if MGUTILITY_CPLUSPLUS < 201703L + /** - * @brief A simple optional implementation for C++11. + * @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 override { return "optional has no value"; } +}; + +struct nullopt_t; + +/** + * @brief A class template that provides optional (nullable) objects. * - * @tparam T The type. + * @tparam T The type of the value. */ template class optional { public: /** - * @brief Default constructor. + * @brief Constructs an empty optional. + */ + MGUTILITY_CNSTXPR inline explicit optional(nullopt_t & /*unused*/) + : dummy_{0}, has_value_{false} {} + + /** + * @brief Constructs an empty optional. */ - MGUTILITY_CNSTXPR optional() noexcept : has_value_(false) {} + MGUTILITY_CNSTXPR inline optional() : dummy_{0}, has_value_{false} {} /** - * @brief Constructor from value. + * @brief Constructs an optional with a value. * - * @param value The value. + * @tparam Args The types of the arguments. + * @param args The arguments to construct the value. */ - MGUTILITY_CNSTXPR optional(const T &value) noexcept : has_value_(true) { - new (&storage_) T(value); - } + template + MGUTILITY_CNSTXPR inline explicit optional(Args &&...args) + : value_{T{std::forward(args)...}}, has_value_{true} {} /** - * @brief Constructor from rvalue. + * @brief Constructs an optional with a value. * - * @param value The value. + * @param value The value to initialize with. */ - MGUTILITY_CNSTXPR optional(T &&value) noexcept : has_value_(true) { - new (&storage_) T(std::move(value)); - } + MGUTILITY_CNSTXPR inline explicit optional(T &&value) + : value_{std::move(value)}, has_value_{true} {} /** * @brief Copy constructor. * - * @param other The other optional. + * @param other The other optional to copy. */ - MGUTILITY_CNSTXPR optional(const optional &other) noexcept - : has_value_(other.has_value_) { - if (has_value_) { - new (&storage_) T(other.value()); - } - } + MGUTILITY_CNSTXPR inline optional(const optional &other) + : value_{other.value_}, has_value_{other.has_value_} {} /** * @brief Move constructor. * - * @param other The other optional. + * @param other The other optional to move. */ - MGUTILITY_CNSTXPR optional(optional &&other) noexcept - : has_value_(other.has_value_) { - if (has_value_) { - new (&storage_) T(std::move(other.value())); - other.reset(); - } + MGUTILITY_CNSTXPR inline optional(optional &&other) noexcept + : value_{other.value_}, has_value_{other.has_value_} { + other.reset(); } /** * @brief Destructor. */ - ~optional() { reset(); } + ~optional() = default; /** - * @brief Copy assignment. + * @brief Copy assignment operator. * - * @param other The other optional. - * @return This. + * @param other The other optional to copy. + * @return A reference to this optional. */ - MGUTILITY_CNSTXPR optional &operator=(const optional &other) noexcept { - if (this != &other) { - reset(); - has_value_ = other.has_value_; - if (has_value_) { - new (&storage_) T(other.value()); - } - } + MGUTILITY_CNSTXPR inline optional &operator=(const optional &other) { + value_ = other.value_; + has_value_ = other.has_value_; return *this; } /** - * @brief Move assignment. + * @brief Move assignment operator. * - * @param other The other optional. - * @return This. + * @param other The other optional to move. + * @return A reference to this optional. */ - MGUTILITY_CNSTXPR optional &operator=(optional &&other) noexcept { - if (this != &other) { - reset(); - has_value_ = other.has_value_; - if (has_value_) { - new (&storage_) T(std::move(other.value())); - other.reset(); - } - } + MGUTILITY_CNSTXPR inline optional &operator=(optional &&other) noexcept { + value_ = other.value_; + has_value_ = other.has_value_; + other.reset(); return *this; } /** - * @brief Assignment from value. + * @brief Swaps the contents of this optional with another. * - * @param value The value. - * @return This. + * @param other The other optional to swap with. */ - MGUTILITY_CNSTXPR optional &operator=(const T &value) noexcept { - reset(); - has_value_ = true; - new (&storage_) T(value); - return *this; + 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 = other.has_value_; + other.has_value_ = has_value_; + has_value_ = hval; } /** - * @brief Assignment from rvalue. + * @brief Dereferences the stored value. * - * @param value The value. - * @return This. + * @return A reference to the stored value. */ - MGUTILITY_CNSTXPR optional &operator=(T &&value) noexcept { - reset(); - has_value_ = true; - new (&storage_) T(std::move(value)); - return *this; - } + MGUTILITY_CNSTXPR inline T &operator*() { return value_; } /** - * @brief Checks if has value. + * @brief Dereferences the stored value (const version). * - * @return True if has value. + * @return A reference to the stored value. */ - MGUTILITY_CNSTXPR bool has_value() const noexcept { return has_value_; } + MGUTILITY_CNSTXPR inline T &operator*() const { return value_; } /** - * @brief Checks if has value. + * @brief Accesses the stored value. * - * @return True if has value. + * @return A reference to the stored value. + * @throws bad_optional_access if the optional has no value. */ - MGUTILITY_CNSTXPR explicit operator bool() const noexcept { - return has_value_; + MGUTILITY_CNSTXPR inline T &value() { + if (!has_value_) { + throw bad_optional_access(); + } + return value_; } /** - * @brief Returns the value. + * @brief Accesses the stored value (const version). * - * @return The value. + * @return A reference to the stored value. + * @throws bad_optional_access if the optional has no value. */ - MGUTILITY_CNSTXPR T &value() noexcept { - return *reinterpret_cast(&storage_); + MGUTILITY_CNSTXPR inline T &value() const { + if (!has_value_) { + throw bad_optional_access(); + } + return value_; } /** - * @brief Returns the const value. + * @brief Returns the stored value or a default value if empty. * - * @return The const value. + * @param value The default value to return if empty. + * @return The stored value or the default value. */ - MGUTILITY_CNSTXPR const T &value() const noexcept { - return *reinterpret_cast(&storage_); + MGUTILITY_CNSTXPR inline T value_or(T &&value) { + return has_value_ ? value_ : std::move(value); } /** - * @brief Returns the value or default. + * @brief Returns the stored value or a default value if empty (const + * version). * - * @param default_value The default value. - * @return The value or default. + * @param value The default value to return if empty. + * @return The stored value or the default value. */ - MGUTILITY_CNSTXPR T value_or(const T &default_value) const noexcept { - return has_value_ ? value() : default_value; + MGUTILITY_CNSTXPR inline T value_or(T &&value) const { + return has_value_ ? value_ : std::move(value); } /** - * @brief Resets the optional. + * @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 void reset() noexcept { - if (has_value_) { - value().~T(); - has_value_ = false; - } + MGUTILITY_CNSTXPR inline T value_or(const T &value) { + return has_value_ ? value_ : value; } /** - * @brief Emplaces a value. + * @brief Returns the stored value or a default value if empty (const + * version). * - * @param args The arguments. - * @return The value. + * @param value The default value to return if empty. + * @return The stored value or the default value. */ - template - MGUTILITY_CNSTXPR T &emplace(Args &&...args) noexcept { - reset(); + 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; } @@ -1960,4 +1980,4 @@ struct fmt::formatter) -#endif // MGUTILITY_ENUM_NAME_HPP +#endif // MGUTILITY_ENUM_NAME_HPP \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ae8e2df..2ef0b85 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,7 +4,7 @@ project( VERSION 0.1 LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 14) add_executable(${PROJECT_NAME} test_enum_name.cpp)