Skip to content

Commit

Permalink
Merge pull request #3 from mguludag/fixed_string_experiment
Browse files Browse the repository at this point in the history
Refactor CMakeLists and update enum handling for improved clarity and…
  • Loading branch information
mguludag authored Feb 9, 2025
2 parents 494e0b5 + a415cdc commit 54b309c
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 119 deletions.
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"usernamehw.errorlens",
"twxs.cmake",
"ms-vscode.cpptools",
"ms-vscode.cmake-tools"
"ms-vscode.cmake-tools",
"GitHub.copilot"
]
}
}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/c-cpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <iostream>
Expand Down
1 change: 1 addition & 0 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ project(
VERSION 0.1
LANGUAGES CXX)


add_executable(${PROJECT_NAME} main.cpp)

target_link_libraries(${PROJECT_NAME} mgutility::enum_name)
62 changes: 33 additions & 29 deletions example/main.cpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
#include <iostream>

#include "mgutility/reflection/enum_name.hpp"
#include "mgutility/std/string_view.hpp"

#if defined(__cpp_lib_print)
#include <print>
#include <ranges>
#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<Position>(mgutility::enum_to_underlying(lhs) |
Expand All @@ -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<Position> {
static constexpr auto min = 0; // Minimum value
Expand All @@ -37,36 +36,41 @@ template <> struct mgutility::enum_range<Position> {
// Specialize individual or all enum names
template <> struct mgutility::custom_enum<Position> {
static constexpr mgutility::flat_map<Position> 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<Position>("CENTER"); // Convert string to enum
auto posLeft = Position::Left;
auto posCenter =
mgutility::to_enum<Position>("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<Position>("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<Position>("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<Position>() |
std::ranges::views::filter([](auto &&pair) {
Expand All @@ -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<Position>()) {
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<Position>()) {
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';
}
2 changes: 1 addition & 1 deletion include/mgutility/_common/definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions include/mgutility/reflection/detail/enum_for_each.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ SOFTWARE.
#include <cstdint>
#include <utility>

#ifndef MGUTILITY_ENUM_NAME_BUFFER_SIZE
#define MGUTILITY_ENUM_NAME_BUFFER_SIZE 512U
#endif

namespace mgutility {
namespace detail {
/**
Expand All @@ -44,7 +48,7 @@ namespace detail {
*/
template <typename T>
using string_or_view_t =
typename std::conditional<has_bit_or<T>::value, std::string,
typename std::conditional<has_bit_or<T>::value, mgutility::fixed_string<MGUTILITY_ENUM_NAME_BUFFER_SIZE>,
mgutility::string_view>::type;

/**
Expand Down Expand Up @@ -89,7 +93,7 @@ template <typename Enum> 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.
Expand Down
54 changes: 26 additions & 28 deletions include/mgutility/reflection/detail/enum_name_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -87,7 +88,7 @@ struct enum_type {
typename Enum, Enum e,
detail::enable_if_t<!detail::is_scoped_enum<Enum>::value, bool> = true>
MGUTILITY_CNSTXPR static auto name() noexcept -> mgutility::string_view {
for (auto &pair : mgutility::custom_enum<Enum>::map) {
for (const auto &pair : mgutility::custom_enum<Enum>::map) {
if (pair.first == e) {
return pair.second;
}
Expand All @@ -113,7 +114,7 @@ struct enum_type {
typename Enum, Enum e,
detail::enable_if_t<detail::is_scoped_enum<Enum>::value, bool> = true>
MGUTILITY_CNSTXPR static auto name() noexcept -> mgutility::string_view {
for (auto &pair : mgutility::custom_enum<Enum>::map) {
for (const auto &pair : mgutility::custom_enum<Enum>::map) {
if (pair.first == e) {
return pair.second;
}
Expand All @@ -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__)
Expand All @@ -147,7 +149,7 @@ struct enum_type {
#else
157,
#endif
5, ' ', ':', '(', ')'};
5, ' ', ':', '(', ')'};
#endif
};

Expand Down Expand Up @@ -194,10 +196,11 @@ template <typename Enum, int Min, int Max>
MGUTILITY_CNSTXPR inline auto to_enum_impl(mgutility::string_view str) noexcept
-> mgutility::optional<Enum> {
MGUTILITY_CNSTXPR_CLANG_WA auto arr = get_enum_array<Enum, Min, Max>();
const auto index{std::find(arr.begin() + 1, arr.end(), str)};
return index == arr.end() ? mgutility::nullopt
: mgutility::optional<Enum>{static_cast<Enum>(
std::distance(arr.begin(), index) + Min - 1)};

const auto index{detail::find(arr, str)};
return index == 0
? mgutility::nullopt
: mgutility::optional<Enum>{static_cast<Enum>(index + Min - 1)};
}

/**
Expand Down Expand Up @@ -256,10 +259,10 @@ MGUTILITY_CNSTXPR auto to_enum_bitmask_impl(mgutility::string_view str) noexcept
*/
template <typename Enum, int Min, int Max,
detail::enable_if_t<!detail::has_bit_or<Enum>::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<Enum, Min, Max>();
const auto index{(Min < 0 ? -Min : Min) + static_cast<int>(e) + 1};
const auto index{(Min < 0 ? -Min : Min) + static_cast<int>(enumValue) + 1};
return arr[(index < Min || index > arr.size() - 1) ? 0 : index];
}

Expand All @@ -275,32 +278,30 @@ MGUTILITY_CNSTXPR auto enum_name_impl(Enum e) noexcept
*/
template <typename Enum, int Min, int Max,
detail::enable_if_t<detail::has_bit_or<Enum>::value, bool> = true>
MGUTILITY_CNSTXPR_CLANG_WA inline auto enum_name_impl(Enum e) noexcept
-> detail::string_or_view_t<Enum> {
MGUTILITY_CNSTXPR_CLANG_WA auto enum_name_impl(Enum enumValue) noexcept
-> mgutility::fixed_string<MGUTILITY_ENUM_NAME_BUFFER_SIZE> {

// Get the array of enum names
MGUTILITY_CNSTXPR_CLANG_WA auto arr = get_enum_array<Enum, Min, Max>();

// Calculate the index in the array
const auto index = (Min < 0 ? -Min : Min) + static_cast<int>(e) + 1;
const auto index = (Min < 0 ? -Min : Min) + static_cast<int>(enumValue) + 1;
const auto name =
arr[(index < Min || index >= static_cast<int>(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<MGUTILITY_ENUM_NAME_BUFFER_SIZE>{}.append(
name);
}

// Construct bitmasked name
std::string bitmasked_name;
mgutility::fixed_string<MGUTILITY_ENUM_NAME_BUFFER_SIZE> bitmasked_name;
for (auto i = Min; i < Max; ++i) {
const auto idx = (Min < 0 ? -Min : Min) + i + 1;
if (idx >= 0 && idx < static_cast<int>(arr.size()) && !arr[idx].empty() &&
!is_digit(arr[idx][0]) &&
(e & static_cast<Enum>(i)) == static_cast<Enum>(i)) {
!detail::is_digit(arr[idx][0]) &&
(enumValue & static_cast<Enum>(i)) == static_cast<Enum>(i)) {
bitmasked_name.append(arr[idx]).append("|");
}
}
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion include/mgutility/reflection/detail/meta.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ template <typename T, typename U> struct pair {
};

template <typename T>
#if MGUTILITY_CPLUSPLUS > 201103L || defined(__GNUC__) && !defined(__clang__)
#if MGUTILITY_CPLUSPLUS > 201402L || defined(__GNUC__) && !defined(__clang__)
using flat_map = std::initializer_list<pair<T, const char *>>;
#else
using flat_map = pair<T, const char *>[];
Expand Down
13 changes: 7 additions & 6 deletions include/mgutility/reflection/enum_name.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ SOFTWARE.

#include "detail/enum_name_impl.hpp"


namespace mgutility {

/**
Expand All @@ -37,10 +38,10 @@ namespace mgutility {
* @return The underlying integer value of the enum.
*/
template <typename Enum>
constexpr auto enum_to_underlying(Enum e) noexcept
constexpr auto enum_to_underlying(Enum enumValue) noexcept
-> detail::underlying_type_t<Enum> {
static_assert(std::is_enum<Enum>::value, "Value is not an Enum type!");
return static_cast<detail::underlying_type_t<Enum>>(e);
return static_cast<detail::underlying_type_t<Enum>>(enumValue);
}

/**
Expand All @@ -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 <int Min, int Max, typename Enum>
MGUTILITY_CNSTXPR auto enum_name(Enum e) noexcept
MGUTILITY_CNSTXPR auto enum_name(Enum enumValue) noexcept
-> detail::string_or_view_t<Enum> {
static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!");
static_assert(std::is_enum<Enum>::value, "Value is not an Enum type!");
return detail::enum_name_impl<Enum, Min, Max>(e);
return detail::enum_name_impl<Enum, Min, Max>(enumValue);
}

/**
Expand All @@ -71,11 +72,11 @@ MGUTILITY_CNSTXPR auto enum_name(Enum e) noexcept
*/
template <typename Enum, int Min = enum_range<Enum>::min,
int Max = enum_range<Enum>::max>
MGUTILITY_CNSTXPR auto enum_name(Enum e) noexcept
MGUTILITY_CNSTXPR auto enum_name(Enum enumValue) noexcept
-> detail::string_or_view_t<Enum> {
static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!");
static_assert(std::is_enum<Enum>::value, "Value is not an Enum type!");
return detail::enum_name_impl<Enum, Min, Max>(e);
return detail::enum_name_impl<Enum, Min, Max>(enumValue);
}

/**
Expand Down
Loading

0 comments on commit 54b309c

Please sign in to comment.