Skip to content

Commit

Permalink
introduce bitmask support and std::format specialization
Browse files Browse the repository at this point in the history
  • Loading branch information
mguludag committed Jun 8, 2024
1 parent 0e5b19f commit d30ef58
Show file tree
Hide file tree
Showing 13 changed files with 1,906 additions and 714 deletions.
17 changes: 13 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
{
"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"
}
}
16 changes: 9 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
cmake_minimum_required(VERSION 3.14)
project(enum_name_example VERSION 0.1 LANGUAGES CXX)
project(
enum_name_example
VERSION 0.1
LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)

include(FetchContent)

FetchContent_Declare(
DocTest
GIT_REPOSITORY "https://github.com/onqtam/doctest"
GIT_TAG "v2.4.11"
)
DocTest
GIT_REPOSITORY "https://github.com/onqtam/doctest"
GIT_TAG "v2.4.11")

FetchContent_MakeAvailable(DocTest)

add_executable(${PROJECT_NAME} example/main.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/include)

enable_testing()
add_subdirectory(test)
add_subdirectory(test)
52 changes: 35 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,44 @@ 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 <sub>(default range: `[0, 256)`)</sub> on each call or with your special function for types or adding specialized `enum_range<Enum>` 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<Enum, EnumValue>() noexcept` function
* Supports bitmasked enums and auto detect them
* Supports iterate over enum (names and values) with `mgutility::enum_for_each<T>()` class and it is compatible with standard ranges and views

## Limitations
* Compiler versions
* 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 <iostream>
#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<rgb_color>(mgutility::enum_to_underlying(lhs) |
mgutility::enum_to_underlying(rhs));
}

auto operator&(rgb_color lhs, rgb_color rhs) -> rgb_color {
return static_cast<rgb_color>(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<rgb_color, rgb_color::unknown>() noexcept
constexpr inline auto
mgutility::detail::enum_type::name<rgb_color, rgb_color::unknown>() noexcept
-> string_view {
return "UNKNOWN";
}
Expand All @@ -38,38 +55,41 @@ constexpr auto mgutility::detail::enum_type::name<rgb_color, rgb_color::unknown>
template <>
struct mgutility::enum_range<rgb_color> {
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 <print>
#include <ranges>
#endif


int main() {
auto x = rgb_color::blue;
auto y = mgutility::to_enum<rgb_color>("greenn");
auto y = mgutility::to_enum<rgb_color>("green|red");

#if defined(__cpp_lib_print)

// enum_for_each<T> can usable with views and range algorithms
auto colors = mgutility::enum_for_each<rgb_color>() |
std::ranges::views::filter(
[](auto &&pair) { return pair.second != "UNKNOWN"; });
// enum_for_each<T> can usable with views and range algorithms
auto colors = mgutility::enum_for_each<rgb_color>() |
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<rgb_color>()) {
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, string_view> {enum_type, name_of_enum}
}
Expand All @@ -78,8 +98,6 @@ auto colors = mgutility::enum_for_each<rgb_color>() |
// default signature: enum_name<min_value = -128, max_value = 128, Enum
// typename>(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
Expand Down
97 changes: 72 additions & 25 deletions example/main.cpp
Original file line number Diff line number Diff line change
@@ -1,33 +1,80 @@
#include <iostream>
#include "enum_name.hpp"
#include <iostream>

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<rgb_color>(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<rgb_color>(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<rgb_color>
{
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<rgb_color, rgb_color::unknown>() noexcept
-> mgutility::string_view {
return "UNKNOWN";
}

// you can specialize enum ranges with overload per enum types (option 1)
template <> struct mgutility::enum_range<rgb_color> {
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<rgb_color>("green");

// default signature: enum_name<min_value = -128, max_value = 128, Enum typename>(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 <print>
#include <ranges>
#endif

int main() {
auto x = rgb_color::blue;
auto y = mgutility::to_enum<rgb_color>("green|red");

#if defined(__cpp_lib_print)

// enum_for_each<T> can usable with views and range algorithms
auto colors = mgutility::enum_for_each<rgb_color>() |
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<rgb_color>()) {
if (!e.second.empty() && e.second != "UNKNOWN") {
std::cout << e.second << " \t: " << mgutility::enum_to_underlying(e.first)
<< '\n';
}
// std::pair<Enum, string_view> {enum_type, name_of_enum}
}
#endif

// default signature: enum_name<min_value = -128, max_value = 128, Enum
// typename>(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';
}
114 changes: 114 additions & 0 deletions include/detail/definitions.hpp
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit d30ef58

Please sign in to comment.