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 9, 2024
1 parent 0e5b19f commit 08f75ca
Show file tree
Hide file tree
Showing 17 changed files with 2,051 additions and 782 deletions.
18 changes: 14 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -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"
}
101 changes: 88 additions & 13 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,95 @@
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)
add_library(mgutility::${PROJECT_NAME} ALIAS ${PROJECT_NAME})

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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)

FetchContent_MakeAvailable(DocTest)
# Set the C++ standard
target_compile_features(enum_name INTERFACE cxx_std_11)

add_executable(${PROJECT_NAME} example/main.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/include)
if(CMAKE_SYSTEM_NAME STREQUAL Linux)
include(GNUInstallDirs)
set(include_install_dir ${CMAKE_INSTALL_INCLUDEDIR})
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
else()
set(include_install_dir "include")
set(config_install_dir "lib/cmake/${PROJECT_NAME}")
endif()

enable_testing()
add_subdirectory(test)
# Create the package configuration files
include(CMakePackageConfigHelpers)

write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/enum_nameConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion)

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)

if(NOT ${ENUM_NAME_NO_INSTALL})
# 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)

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::)
endif()

# Add example executable
add_executable(example_enum_name example/main.cpp)

# Link the example executable with the library
target_link_libraries(example_enum_name PRIVATE mgutility::enum_name)

if(NOT ${ENUM_NAME_NO_TESTS})
# Enable testing
enable_testing()

# 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 mgutility::enum_name
doctest::doctest)

# Add tests
add_test(NAME test_enum_name COMMAND test_enum_name)
endif()
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
3 changes: 3 additions & 0 deletions cmake/enum_nameConfig.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@PACKAGE_INIT@

include("${CMAKE_CURRENT_LIST_DIR}/enum_nameTargets.cmake")
97 changes: 72 additions & 25 deletions example/main.cpp
Original file line number Diff line number Diff line change
@@ -1,33 +1,80 @@
#include "mgutility/reflection/enum_name.hpp"
#include <iostream>
#include "enum_name.hpp"

enum class rgb_color {
red = 1 << 0,
green = 1 << 1,
blue = 1 << 2,
unknown = -1
};

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));
}

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';
}
Loading

0 comments on commit 08f75ca

Please sign in to comment.