diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index d087591..59b58fd 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -10,24 +10,27 @@ jobs: fail-fast: false matrix: compiler: - - { cc: "gcc-11", cxx: "g++-11", os: "ubuntu-22.04", nonascii: "TRUE" } - - { cc: "gcc-11", cxx: "g++-11", os: "ubuntu-22.04", nonascii: "FALSE" } - - { cc: "gcc-12", cxx: "g++-12", os: "ubuntu-22.04", nonascii: "FALSE" } - - { cc: "gcc-13", cxx: "g++-13", os: "ubuntu-24.04", nonascii: "FALSE" } - - { cc: "gcc-14", cxx: "g++-14", os: "ubuntu-24.04", nonascii: "FALSE" } - - { cc: "gcc-15", cxx: "g++-15", os: "ubuntu-24.04", nonascii: "FALSE" } - - { cc: "clang-13", cxx: "clang++-13", os: "ubuntu-22.04", nonascii: "FALSE" } - - { cc: "clang-14", cxx: "clang++-14", os: "ubuntu-22.04", nonascii: "FALSE" } - - { cc: "clang-15", cxx: "clang++-15", os: "ubuntu-22.04", nonascii: "FALSE" } - - { cc: "clang-16", cxx: "clang++-16", os: "ubuntu-24.04", nonascii: "FALSE" } - - { cc: "clang-17", cxx: "clang++-17", os: "ubuntu-24.04", nonascii: "FALSE" } - - { cc: "clang-18", cxx: "clang++-18", os: "ubuntu-24.04", nonascii: "FALSE" } - - { cc: "clang-19", cxx: "clang++-19", os: "ubuntu-24.04", nonascii: "FALSE" } - - { cc: "clang-20", cxx: "clang++-20", os: "ubuntu-24.04", nonascii: "FALSE" } - - { cc: "clang-21", cxx: "clang++-21", os: "ubuntu-24.04", nonascii: "FALSE" } - - { cc: "clang-22", cxx: "clang++-22", os: "ubuntu-24.04", nonascii: "FALSE" } + - { cc: "gcc-11", cxx: "g++-11", os: "ubuntu-22.04", nonascii: "TRUE", modules: "FALSE", import_std: "FALSE" } + - { cc: "gcc-11", cxx: "g++-11", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "gcc-12", cxx: "g++-12", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "gcc-13", cxx: "g++-13", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "gcc-14", cxx: "g++-14", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "gcc-15", cxx: "g++-15", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "clang-13", cxx: "clang++-13", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "clang-14", cxx: "clang++-14", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "clang-15", cxx: "clang++-15", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "clang-16", cxx: "clang++-16", os: "ubuntu-22.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "clang-17", cxx: "clang++-17", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "clang-18", cxx: "clang++-18", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "clang-19", cxx: "clang++-19", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "clang-19", cxx: "clang++-19", os: "ubuntu-24.04", nonascii: "FALSE", modules: "TRUE", import_std: "FALSE" } + - { cc: "clang-19", cxx: "clang++-19", os: "ubuntu-24.04", nonascii: "FALSE", modules: "TRUE", import_std: "TRUE" } + - { cc: "clang-20", cxx: "clang++-20", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "clang-21", cxx: "clang++-21", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "clang-22", cxx: "clang++-22", os: "ubuntu-24.04", nonascii: "FALSE", modules: "FALSE", import_std: "FALSE" } + - { cc: "clang-22", cxx: "clang++-22", os: "ubuntu-24.04", nonascii: "FALSE", modules: "TRUE", import_std: "FALSE" } - name: "${{ format('{0} NONASCII={1}', matrix.compiler.cc, matrix.compiler.nonascii) }}" + name: "${{ format('{0} NONASCII={1} MODULES={2} IMPORT_STD={3}', matrix.compiler.cc, matrix.compiler.nonascii, matrix.compiler.modules, matrix.compiler.import_std) }}" runs-on: ${{ matrix.compiler.os }} steps: - uses: actions/checkout@v6 @@ -36,24 +39,23 @@ jobs: run: | if [[ "${{ matrix.compiler.cc }}" == "clang"* ]]; then wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - # Add repositories based on Ubuntu version - if [[ "${{ matrix.compiler.os }}" == "ubuntu-22.04" ]]; then - sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-13 main" - sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-14 main" - sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main" - sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main" - sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main" - sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" - sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy main" - elif [[ "${{ matrix.compiler.os }}" == "ubuntu-24.04" ]]; then - sudo apt-add-repository "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main" - sudo apt-add-repository "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main" - sudo apt-add-repository "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main" - sudo apt-add-repository "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-22 main" - sudo apt-add-repository "deb http://apt.llvm.org/noble/ llvm-toolchain-noble main" - fi + + clang_version="${{ matrix.compiler.cc }}" + clang_version="${clang_version#clang-}" + ubuntu_codename="$(lsb_release -cs)" + + sudo apt-add-repository "deb http://apt.llvm.org/${ubuntu_codename}/ llvm-toolchain-${ubuntu_codename}-${clang_version} main" sudo apt update sudo apt install ${{ matrix.compiler.cc }} -y + + if [[ "${{ matrix.compiler.import_std }}" == "TRUE" ]]; then + # Note: std module is currently supported only with libc++ + sudo apt install -y \ + "libclang-rt-${clang_version}-dev" \ + "libc++-${clang_version}-dev" \ + "libc++abi-${clang_version}-dev" \ + "clang-tools-${clang_version}" + fi fi - name: Configure gcc @@ -67,20 +69,42 @@ jobs: - name: Build Release run: | rm -rf build - mkdir build - cd build - cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} -DMAGIC_ENUM_OPT_ENABLE_NONASCII:BOOL=${{ matrix.compiler.nonascii }} - cmake --build . -j 4 --config Release - ctest --output-on-failure -C Release + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} \ + -DMAGIC_ENUM_OPT_ENABLE_NONASCII:BOOL=${{ matrix.compiler.nonascii }} \ + -DMAGIC_ENUM_USE_MODULES:BOOL=${{ matrix.compiler.modules }} \ + ${{ matrix.compiler.modules == 'TRUE' && '-G Ninja' || '' }} + cmake --build build -j 4 --config Release + ctest --test-dir build --output-on-failure -C Release - name: Build Debug run: | rm -rf build - mkdir build - cd build - cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} -DMAGIC_ENUM_OPT_ENABLE_NONASCII:BOOL=${{ matrix.compiler.nonascii }} - cmake --build . -j 4 --config Debug - ctest --output-on-failure -C Debug + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} \ + -DMAGIC_ENUM_OPT_ENABLE_NONASCII:BOOL=${{ matrix.compiler.nonascii }} \ + -DMAGIC_ENUM_USE_MODULES:BOOL=${{ matrix.compiler.modules }} \ + ${{ matrix.compiler.modules == 'TRUE' && '-G Ninja' || '' }} + cmake --build build -j 4 --config Debug + ctest --test-dir build --output-on-failure -C Debug + + - name: Build with `import std;` + if: ${{matrix.compiler.import_std == 'TRUE'}} + run: | + rm -rf build + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} \ + -DMAGIC_ENUM_USE_MODULES:BOOL=TRUE \ + -GNinja \ + -DCMAKE_CXX_MODULE_STD:BOOL=TRUE \ + -DCMAKE_EXPERIMENTAL_CXX_IMPORT_STD:STRING=0e5b6991-d74f-4b3d-a41c-cf096e0b2508 \ + -DCMAKE_CXX_FLAGS:STRING="-stdlib=libc++" \ + -DCMAKE_CXX_STANDARD=23 + cmake --build build -j 4 --config Debug + ctest --test-dir build --output-on-failure -C Debug - name: Bazel Test working-directory: test diff --git a/.github/workflows/ubuntu_test_installed_version.yml b/.github/workflows/ubuntu_test_installed_version.yml index 1c47401..df8eab3 100644 --- a/.github/workflows/ubuntu_test_installed_version.yml +++ b/.github/workflows/ubuntu_test_installed_version.yml @@ -45,6 +45,42 @@ jobs: cd build2 ctest --build-config Debug -j$(nproc) + - name: clear + run: rm -rf build2 + + - name: configure clang + run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo apt-add-repository "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main" + sudo apt update + sudo apt install clang-19 -y + + - name: install module library + run: | + cmake -S. -Bbuild \ + -GNinja \ + --log-level=DEBUG \ + -DCMAKE_INSTALL_PREFIX=./install_dir_module \ + -DMAGIC_ENUM_USE_MODULES=ON \ + -DCMAKE_CXX_COMPILER=clang++-19 + cmake --build build --target magic_enum + cmake --install build + rm -rf build + + - name: configure with installed module version via cmake + run: | + CMAKE_PREFIX_PATH="./install_dir_module" cmake -S. -Bbuild2 \ + -GNinja \ + -DMAGIC_ENUM_OPT_TEST_INSTALLED_VERSION=1 \ + -DCMAKE_CXX_COMPILER=clang++-19 \ + --log-level=DEBUG + + - name: build with installed module version via cmake + run: cmake --build build2 -j$(nproc) --target test_flags-cpp20 --verbose + + - name: test + run: ctest --test-dir build2 -VV -R test_flags-cpp20 + - name: clear run: rm -rf build2 diff --git a/CMakeLists.txt b/CMakeLists.txt index ffcd192..2ae9e1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,11 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.14...4.20) + +if(MAGIC_ENUM_USE_MODULES) + set(CMAKE_CXX_EXTENSIONS OFF) + if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 20) + endif() +endif() project( magic_enum @@ -35,20 +42,65 @@ a local magic_enum via pkgconfig" NO ) if(${MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION} OR ${MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION_PKGCONFIG}) + enable_testing() add_subdirectory(test) return() endif() set(INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/include/magic_enum") +set(MODULE "${CMAKE_CURRENT_SOURCE_DIR}/module") set(EXPORT_NAMESPACE "${PROJECT_NAME}::") -add_library(${PROJECT_NAME} INTERFACE) -add_library(${EXPORT_NAMESPACE}${PROJECT_NAME} ALIAS ${PROJECT_NAME}) +function(magic_enum_make_library name) + +if(MAGIC_ENUM_USE_MODULES) + add_library(${name}) + target_sources(${name} + PUBLIC + FILE_SET CXX_MODULES + BASE_DIRS "${MODULE}" + FILES "${MODULE}/magic_enum.cppm" + PUBLIC + FILE_SET HEADERS + BASE_DIRS "${INCLUDES}" + FILES + "${INCLUDES}/magic_enum.hpp" + "${INCLUDES}/magic_enum_all.hpp" + "${INCLUDES}/magic_enum_containers.hpp" + "${INCLUDES}/magic_enum_flags.hpp" + "${INCLUDES}/magic_enum_format.hpp" + "${INCLUDES}/magic_enum_fuse.hpp" + "${INCLUDES}/magic_enum_iostream.hpp" + "${INCLUDES}/magic_enum_switch.hpp" + "${INCLUDES}/magic_enum_utility.hpp" + ) + target_compile_features(${name} PUBLIC cxx_std_20) + target_compile_definitions(${name} + PUBLIC MAGIC_ENUM_USE_MODULES + PRIVATE ${ARGN} + ) + if((CMAKE_CXX_STANDARD IN_LIST CMAKE_CXX_COMPILER_IMPORT_STD) AND CMAKE_CXX_MODULE_STD) + message("${name}: using import std") + target_compile_features(${name} PRIVATE cxx_std_23) + target_compile_definitions(${name} PRIVATE MAGIC_ENUM_USE_STD_MODULE) + endif() + + set(__scope PUBLIC) +else() + add_library(${name} INTERFACE) + set(__scope INTERFACE) +endif() + +add_library(${EXPORT_NAMESPACE}${name} ALIAS ${name}) target_include_directories( - ${PROJECT_NAME} INTERFACE $ + ${name} ${__scope} $ $ ) +endfunction() + +magic_enum_make_library(${PROJECT_NAME}) + if(MAGIC_ENUM_OPT_BUILD_EXAMPLES) add_subdirectory(example) endif() @@ -64,11 +116,13 @@ if(MAGIC_ENUM_OPT_INSTALL) include(CPackComponent) include(CMakePackageConfigHelpers) + if(NOT MAGIC_ENUM_USE_MODULES) + install( TARGETS "${PROJECT_NAME}" EXPORT ${PROJECT_NAME} INCLUDES - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" # COMPONENT "${SDK_COMPONENT_NAME}" # component is not allowed for # includes! # Headers are installed separately! Includes only marks the headers for @@ -86,6 +140,23 @@ if(MAGIC_ENUM_OPT_INSTALL) PATTERN "${PROJECT_INCLUDE_HEADER_PATTERN}" ) + else() + + install( + TARGETS "${PROJECT_NAME}" + EXPORT ${PROJECT_NAME} + INCLUDES + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + FILE_SET HEADERS + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}" + FILE_SET CXX_MODULES + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}" + CXX_MODULES_BMI + DESTINATION "" + ) + + endif() + set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") set(CPACK_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}") set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "all") diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 1fa78d0..154938e 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,6 +1,8 @@ include(CheckCXXCompilerFlag) -set(CMAKE_CXX_STANDARD 17) +if(NOT MAGIC_ENUM_USE_MODULES) + set(CMAKE_CXX_STANDARD 17) +endif() if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) set(OPTIONS -Wall -Wextra -Wshadow -pedantic-errors -Werror) elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") @@ -28,3 +30,6 @@ make_example(example_switch) if(MAGIC_ENUM_OPT_ENABLE_NONASCII) make_example(example_nonascii_name) endif() +if(MAGIC_ENUM_USE_MODULES) + make_example(example_module_usage) +endif() diff --git a/example/enum_flag_example.cpp b/example/enum_flag_example.cpp index 49718ea..92fae93 100644 --- a/example/enum_flag_example.cpp +++ b/example/enum_flag_example.cpp @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include #include #include diff --git a/example/example_containers_array.cpp b/example/example_containers_array.cpp index 6f9c01c..dfc4ee6 100644 --- a/example/example_containers_array.cpp +++ b/example/example_containers_array.cpp @@ -21,6 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include #include #include diff --git a/example/example_module_usage.cpp b/example/example_module_usage.cpp new file mode 100644 index 0000000..e5337cb --- /dev/null +++ b/example/example_module_usage.cpp @@ -0,0 +1,44 @@ +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2024 Daniil Goncharov . +// +// 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. + +#include + +import magic_enum; + +enum class Color : int { RED = -10, BLUE = 0, GREEN = 10 }; + +int main() { + for (const auto c : magic_enum::enum_values()) { + std::cout << magic_enum::enum_name(c) << '\n'; + } + + constexpr auto result = magic_enum::enum_integer(Color::RED) + magic_enum::enum_integer(Color::GREEN); + if (result == magic_enum::enum_integer(Color::BLUE)) { + std::cout << "RED + GREEN == BLUE\n"; + } + + using magic_enum::iostream_operators::operator<<; + constexpr auto c = Color::GREEN; + std::cout << "Color: " << Color::RED << " " << c << '\n'; + + return 0; +} diff --git a/example/example_switch.cpp b/example/example_switch.cpp index 217f036..a3f7400 100644 --- a/example/example_switch.cpp +++ b/example/example_switch.cpp @@ -21,7 +21,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include #include +#include #define MAGIC_ENUM_ENABLE_HASH #include diff --git a/include/magic_enum/magic_enum.hpp b/include/magic_enum/magic_enum.hpp index d5bb559..80436d2 100644 --- a/include/magic_enum/magic_enum.hpp +++ b/include/magic_enum/magic_enum.hpp @@ -36,6 +36,10 @@ #define MAGIC_ENUM_VERSION_MINOR 9 #define MAGIC_ENUM_VERSION_PATCH 8 +#if defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) +import magic_enum; +#else + #ifndef MAGIC_ENUM_USE_STD_MODULE #include #include @@ -1546,4 +1550,6 @@ constexpr E& operator^=(E& lhs, E rhs) noexcept { #undef MAGIC_ENUM_ARRAY_CONSTEXPR #undef MAGIC_ENUM_FOR_EACH_256 +#endif // defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) + #endif // NEARGYE_MAGIC_ENUM_HPP diff --git a/include/magic_enum/magic_enum_all.hpp b/include/magic_enum/magic_enum_all.hpp index 428ce16..ef43a04 100644 --- a/include/magic_enum/magic_enum_all.hpp +++ b/include/magic_enum/magic_enum_all.hpp @@ -32,6 +32,10 @@ #ifndef NEARGYE_MAGIC_ENUM_ALL_HPP #define NEARGYE_MAGIC_ENUM_ALL_HPP +#if defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) +import magic_enum; +#else + #include "magic_enum.hpp" #include "magic_enum_containers.hpp" #include "magic_enum_flags.hpp" @@ -41,4 +45,6 @@ #include "magic_enum_switch.hpp" #include "magic_enum_utility.hpp" +#endif // defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) + #endif // NEARGYE_MAGIC_ENUM_ALL_HPP diff --git a/include/magic_enum/magic_enum_containers.hpp b/include/magic_enum/magic_enum_containers.hpp index 31e2598..c2bcc53 100644 --- a/include/magic_enum/magic_enum_containers.hpp +++ b/include/magic_enum/magic_enum_containers.hpp @@ -33,6 +33,10 @@ #ifndef NEARGYE_MAGIC_ENUM_CONTAINERS_HPP #define NEARGYE_MAGIC_ENUM_CONTAINERS_HPP +#if defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) +import magic_enum; +#else + #include "magic_enum.hpp" #if !defined(MAGIC_ENUM_NO_EXCEPTION) && (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) @@ -1171,4 +1175,6 @@ constexpr std::enable_if_t && enum_contains(En } // namespace magic_enum::containers +#endif // defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) + #endif // NEARGYE_MAGIC_ENUM_CONTAINERS_HPP diff --git a/include/magic_enum/magic_enum_flags.hpp b/include/magic_enum/magic_enum_flags.hpp index 18d4e32..f3ecddf 100644 --- a/include/magic_enum/magic_enum_flags.hpp +++ b/include/magic_enum/magic_enum_flags.hpp @@ -32,6 +32,10 @@ #ifndef NEARGYE_MAGIC_ENUM_FLAGS_HPP #define NEARGYE_MAGIC_ENUM_FLAGS_HPP +#if defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) +import magic_enum; +#else + #include "magic_enum.hpp" #if defined(__clang__) @@ -219,4 +223,6 @@ constexpr auto enum_flags_test_any(E lhs, E rhs) noexcept -> detail::enable_if_t # pragma warning(pop) #endif +#endif // defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) + #endif // NEARGYE_MAGIC_ENUM_FLAGS_HPP diff --git a/include/magic_enum/magic_enum_format.hpp b/include/magic_enum/magic_enum_format.hpp index 6b6ed61..337d4b6 100644 --- a/include/magic_enum/magic_enum_format.hpp +++ b/include/magic_enum/magic_enum_format.hpp @@ -32,6 +32,10 @@ #ifndef NEARGYE_MAGIC_ENUM_FORMAT_HPP #define NEARGYE_MAGIC_ENUM_FORMAT_HPP +#if defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) +import magic_enum; +#else + #include "magic_enum.hpp" #include "magic_enum_flags.hpp" @@ -55,7 +59,7 @@ std::string format_as(E e) { return std::to_string(magic_enum::enum_integer(e)); } -} // namespace magic_enum::format +} // namespace magic_enum::detail #if defined(__cpp_lib_format) @@ -85,4 +89,6 @@ struct fmt::formatter>, char> #endif +#endif // defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) + #endif // NEARGYE_MAGIC_ENUM_FORMAT_HPP diff --git a/include/magic_enum/magic_enum_fuse.hpp b/include/magic_enum/magic_enum_fuse.hpp index 27b7135..0ddf913 100644 --- a/include/magic_enum/magic_enum_fuse.hpp +++ b/include/magic_enum/magic_enum_fuse.hpp @@ -32,6 +32,10 @@ #ifndef NEARGYE_MAGIC_ENUM_FUSE_HPP #define NEARGYE_MAGIC_ENUM_FUSE_HPP +#if defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) +import magic_enum; +#else + #include "magic_enum.hpp" namespace magic_enum { @@ -86,4 +90,6 @@ template } // namespace magic_enum +#endif // defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) + #endif // NEARGYE_MAGIC_ENUM_FUSE_HPP diff --git a/include/magic_enum/magic_enum_iostream.hpp b/include/magic_enum/magic_enum_iostream.hpp index ac5809c..50a4422 100644 --- a/include/magic_enum/magic_enum_iostream.hpp +++ b/include/magic_enum/magic_enum_iostream.hpp @@ -32,6 +32,10 @@ #ifndef NEARGYE_MAGIC_ENUM_IOSTREAM_HPP #define NEARGYE_MAGIC_ENUM_IOSTREAM_HPP +#if defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) +import magic_enum; +#else + #include "magic_enum.hpp" #include "magic_enum_flags.hpp" @@ -114,4 +118,6 @@ using magic_enum::istream_operators::operator>>; } // namespace magic_enum +#endif // defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) + #endif // NEARGYE_MAGIC_ENUM_IOSTREAM_HPP diff --git a/include/magic_enum/magic_enum_switch.hpp b/include/magic_enum/magic_enum_switch.hpp index 749d2fc..4559a20 100644 --- a/include/magic_enum/magic_enum_switch.hpp +++ b/include/magic_enum/magic_enum_switch.hpp @@ -32,6 +32,10 @@ #ifndef NEARGYE_MAGIC_ENUM_SWITCH_HPP #define NEARGYE_MAGIC_ENUM_SWITCH_HPP +#if defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) +import magic_enum; +#else + #include "magic_enum.hpp" namespace magic_enum { @@ -192,4 +196,6 @@ struct std::common_type : magic_enum::detail::i template struct std::common_type : magic_enum::detail::identity {}; +#endif // defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) + #endif // NEARGYE_MAGIC_ENUM_SWITCH_HPP diff --git a/include/magic_enum/magic_enum_utility.hpp b/include/magic_enum/magic_enum_utility.hpp index 7341453..2293110 100644 --- a/include/magic_enum/magic_enum_utility.hpp +++ b/include/magic_enum/magic_enum_utility.hpp @@ -32,6 +32,10 @@ #ifndef NEARGYE_MAGIC_ENUM_UTILITY_HPP #define NEARGYE_MAGIC_ENUM_UTILITY_HPP +#if defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) +import magic_enum; +#else + #include "magic_enum.hpp" namespace magic_enum { @@ -135,4 +139,6 @@ template > } // namespace magic_enum +#endif // defined(MAGIC_ENUM_USE_MODULES) && !defined(MAGIC_ENUM_INTERFACE_UNIT) + #endif // NEARGYE_MAGIC_ENUM_UTILITY_HPP diff --git a/module/magic_enum.cppm b/module/magic_enum.cppm index acf4d84..03ed5db 100644 --- a/module/magic_enum.cppm +++ b/module/magic_enum.cppm @@ -1,19 +1,44 @@ module; +#define MAGIC_ENUM_INTERFACE_UNIT + #include #if __has_include() #include #endif + #ifndef MAGIC_ENUM_USE_STD_MODULE + #if defined(__cpp_lib_format) #include #endif -#include + +#include +#ifndef MAGIC_ENUM_USING_ALIAS_STRING +#include #endif +#include +#include +#include +#include +#include +#include + +#else // MAGIC_ENUM_USE_STD_MODULE +/* +Note: MAGIC_ENUM_CONFIG_FILE may include STL headers, + it is required to attach them to global module fragement + otherwise there can be conflicting declarations with std module. +*/ +#ifdef MAGIC_ENUM_CONFIG_FILE +# include MAGIC_ENUM_CONFIG_FILE +#endif // MAGIC_ENUM_CONFIG_FILE + +#endif // MAGIC_ENUM_USE_STD_MODULE export module magic_enum; -#if defined(MAGIC_ENUM_USE_STD_MODULE) +#ifdef MAGIC_ENUM_USE_STD_MODULE import std; extern "C++" { @@ -25,7 +50,16 @@ extern "C++" { #pragma warning(disable : 5244) #endif -#include +#include +#ifndef MAGIC_ENUM_USING_ALIAS_STRING +#include +#endif +#include +#include +#include +#include +#include +#include #if defined(__clang__) #pragma clang diagnostic pop @@ -37,7 +71,20 @@ extern "C++" { export namespace magic_enum { namespace customize { + using customize::adl_info; + using customize::customize_t; + using customize::enum_name; using customize::enum_range; + using customize::default_tag; + using customize::invalid_tag; +} + +namespace istream_operators { + using istream_operators::operator>>; +} + +namespace ostream_operators { + using ostream_operators::operator<<; } namespace iostream_operators { @@ -55,12 +102,18 @@ namespace bitwise_operators { using bitwise_operators::operator^=; } +#ifndef MAGIC_ENUM_USING_ALIAS_STRING namespace containers { using containers::array; using containers::bitset; + using containers::get; using containers::set; + using containers::to_array; } +#endif + using magic_enum::is_magic_enum_supported; + using magic_enum::Enum; using magic_enum::enum_name; using magic_enum::enum_cast; using magic_enum::enum_value; @@ -74,19 +127,33 @@ namespace containers { using magic_enum::enum_for_each; using magic_enum::enum_contains; using magic_enum::enum_index; + using magic_enum::enum_prev_value; + using magic_enum::enum_prev_value_circular; + using magic_enum::enum_next_value; + using magic_enum::enum_next_value_circular; + using magic_enum::enum_constant; using magic_enum::enum_flags_name; using magic_enum::enum_flags_contains; using magic_enum::enum_flags_test; using magic_enum::enum_flags_test_any; using magic_enum::enum_flags_cast; using magic_enum::enum_type_name; + using magic_enum::enum_reflected; + using magic_enum::as_flags; + using magic_enum::as_common; + using magic_enum::case_insensitive; using magic_enum::is_unscoped_enum; using magic_enum::is_unscoped_enum_v; using magic_enum::is_scoped_enum; using magic_enum::is_scoped_enum_v; using magic_enum::underlying_type; using magic_enum::underlying_type_t; - using magic_enum::case_insensitive; + +#ifdef MAGIC_ENUM_EXPORT_DETAIL +namespace detail { + MAGIC_ENUM_EXPORT_DETAIL +} +#endif } #if defined(__cpp_lib_format) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4312d52..df0fcbf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -74,12 +74,39 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") endif() endif() +if(MAGIC_ENUM_USE_MODULES AND NOT MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION) + +magic_enum_make_library(magic_enum_module_test-cpp${CMAKE_CXX_STANDARD} + MAGIC_ENUM_NO_CHECK_REFLECTED_ENUM + MAGIC_ENUM_RANGE_MIN=-120 + MAGIC_ENUM_RANGE_MAX=120 + "MAGIC_ENUM_EXPORT_DETAIL=\ + using detail::reflected_min\\$\ + using detail::reflected_max\\$\ + using detail::min_v\\$\ + using detail::max_v\\$\ + using detail::is_reflected_v\\$\ + using detail::cmp_less\\$\ + " +) +magic_enum_make_library(magic_enum_module_test_aliases-cpp${CMAKE_CXX_STANDARD} + "MAGIC_ENUM_CONFIG_FILE=\"../../test/aliases.hpp\"" +) +magic_enum_make_library(magic_enum_module_test_wchar_t-cpp${CMAKE_CXX_STANDARD} + "MAGIC_ENUM_USING_ALIAS_STRING_VIEW=using string_view = std::wstring_view\\$" + "MAGIC_ENUM_USING_ALIAS_STRING=using string = std::wstring\\$" +) + +endif() + function(make_test src target std) add_executable(${target} ${src}) target_compile_options(${target} PRIVATE ${OPTIONS}) target_include_directories(${target} PRIVATE 3rdparty/Catch2/include) if(${MAGIC_ENUM_OPT_TEST_INSTALLED_VERSION_PKGCONFIG}) target_include_directories(${target} PRIVATE ${magic_enum_INCLUDE_DIRS}) + elseif(TARGET magic_enum_module_${target}) + target_link_libraries(${target} PRIVATE magic_enum_module_${target}) else() target_link_libraries(${target} PRIVATE magic_enum::magic_enum) endif() @@ -94,6 +121,8 @@ function(make_test src target std) add_test(NAME ${target} COMMAND ${target}) endfunction() +if(NOT MAGIC_ENUM_USE_MODULES) + make_test(test.cpp test-cpp17 c++17) make_test(test_flags.cpp test_flags-cpp17 c++17) make_test(test_aliases.cpp test_aliases-cpp17 c++17) @@ -104,7 +133,9 @@ if(MAGIC_ENUM_OPT_ENABLE_NONASCII) make_test(test_nonascii.cpp test_nonascii-cpp17 c++17) endif() -if(HAS_CPP20_FLAG) +endif() + +if(HAS_CPP20_FLAG AND (NOT MAGIC_ENUM_USE_MODULES OR (CMAKE_CXX_STANDARD EQUAL 20))) make_test(test.cpp test-cpp20 c++20) make_test(test_flags.cpp test_flags-cpp20 c++20) make_test(test_aliases.cpp test_aliases-cpp20 c++20) @@ -115,7 +146,7 @@ if(HAS_CPP20_FLAG) endif() endif() -if(HAS_CPP23_FLAG) +if(HAS_CPP23_FLAG AND (NOT MAGIC_ENUM_USE_MODULES OR (CMAKE_CXX_STANDARD EQUAL 23))) make_test(test.cpp test-cpp23 c++23) make_test(test_flags.cpp test_flags-cpp23 c++23) make_test(test_aliases.cpp test_aliases-cpp23 c++23) diff --git a/test/aliases.hpp b/test/aliases.hpp new file mode 100644 index 0000000..503b00a --- /dev/null +++ b/test/aliases.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include + +template +struct MyOpt { + constexpr MyOpt() : mHasValue{false}, val{} {} // required + constexpr MyOpt(const T& v) : mHasValue{true}, val{v} {} // required + constexpr const T& operator*() const { return val; } // required + constexpr explicit operator bool() const { return mHasValue; } // required + + constexpr bool has_value() const { return mHasValue; } + constexpr const T& value() const { return val; } + + private: + bool mHasValue; + T val; +}; + +struct MyString { + using value_type = char; // required + static constexpr auto npos = std::string_view::npos; // required + + MyString() : str{} {} // required + MyString(const char* s, std::size_t l) : str{s, l} {} // required + bool empty() const { return str.empty(); } // required + auto begin() const { return str.begin(); } // required + auto end() const { return str.end(); } // required + void append(std::size_t count, char c) { str.append(count, c); } // required + void append(const char* s, std::size_t size) { str.append(s, size); } // required + + std::size_t size() const { return str.size(); } + int compare(const char* s) const { return str.compare(s); } + + private: + std::string str; +}; + +struct MyStringView { + using value_type = char; // required + static constexpr auto npos = std::string_view::npos; // required + using size_type = std::string::size_type; + + constexpr MyStringView() : str{} {} // required + constexpr MyStringView(const char* cstr, std::size_t size) : str{cstr, size} {} // required + constexpr bool empty() const { return str.empty(); } // required + constexpr std::size_t size() const { return str.size(); } // required + constexpr const char* data() const { return str.data(); } // required + constexpr const char& operator[](std::size_t i) const { return str[i]; } // required + constexpr auto begin() const { return str.begin(); } // required + constexpr auto end() const { return str.end(); } // required + constexpr std::size_t find(char c) const { return str.find(c); } // required + constexpr MyStringView substr(std::size_t p, std::size_t n) { return str.substr(p, n); } // required + constexpr void remove_prefix(std::size_t n) { str.remove_prefix(n); } // required + constexpr void remove_suffix(std::size_t n) { str.remove_suffix(n); } // required + friend constexpr bool operator==(MyStringView lhs, MyStringView rhs); // required + + constexpr MyStringView(const char* cstr) : str{ cstr } {} + constexpr int compare(const char* s) const { return str.compare(s); } + + private: + std::string_view str; + + constexpr MyStringView(std::string_view s) : str{s} {} +}; + +constexpr bool operator==(MyStringView lhs, MyStringView rhs) { + return lhs.str == rhs.str; +} + +#define MAGIC_ENUM_USING_ALIAS_OPTIONAL template using optional = MyOpt; +#define MAGIC_ENUM_USING_ALIAS_STRING using string = MyString; +#define MAGIC_ENUM_USING_ALIAS_STRING_VIEW using string_view = MyStringView; diff --git a/test/test.cpp b/test/test.cpp index 292392d..a32bc6c 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include #define CATCH_CONFIG_MAIN #include diff --git a/test/test_aliases.cpp b/test/test_aliases.cpp index 2a012e7..e8518b5 100644 --- a/test/test_aliases.cpp +++ b/test/test_aliases.cpp @@ -20,81 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include -#include -#include - +#include #define CATCH_CONFIG_MAIN #include -template -struct MyOpt { - constexpr MyOpt() : mHasValue{false}, val{} {} // required - constexpr MyOpt(const T& v) : mHasValue{true}, val{v} {} // required - constexpr const T& operator*() const { return val; } // required - constexpr explicit operator bool() const { return mHasValue; } // required - - constexpr bool has_value() const { return mHasValue; } - constexpr const T& value() const { return val; } - - private: - bool mHasValue; - T val; -}; - -struct MyString { - using value_type = char; // required - static constexpr auto npos = std::string_view::npos; // required - - MyString() : str{} {} // required - MyString(const char* s, std::size_t l) : str{s, l} {} // required - bool empty() const { return str.empty(); } // required - auto begin() const { return str.begin(); } // required - auto end() const { return str.end(); } // required - void append(std::size_t count, char c) { str.append(count, c); } // required - void append(const char* s, std::size_t size) { str.append(s, size); } // required - - std::size_t size() const { return str.size(); } - int compare(const char* s) const { return str.compare(s); } - - private: - std::string str; -}; - -struct MyStringView { - using value_type = char; // required - static constexpr auto npos = std::string_view::npos; // required - - constexpr MyStringView() : str{} {} // required - constexpr MyStringView(const char* cstr, std::size_t size) : str{cstr, size} {} // required - constexpr bool empty() const { return str.empty(); } // required - constexpr std::size_t size() const { return str.size(); } // required - constexpr const char* data() const { return str.data(); } // required - constexpr const char& operator[](std::size_t i) const { return str[i]; } // required - constexpr auto begin() const { return str.begin(); } // required - constexpr auto end() const { return str.end(); } // required - constexpr std::size_t find(char c) const { return str.find(c); } // required - constexpr MyStringView substr(std::size_t p, std::size_t n) { return str.substr(p, n); } // required - constexpr void remove_prefix(std::size_t n) { str.remove_prefix(n); } // required - constexpr void remove_suffix(std::size_t n) { str.remove_suffix(n); } // required - friend constexpr bool operator==(MyStringView lhs, MyStringView rhs); // required - - constexpr MyStringView(const char* cstr) : str{ cstr } {} - constexpr int compare(const char* s) const { return str.compare(s); } - - private: - std::string_view str; - - constexpr MyStringView(std::string_view s) : str{s} {} -}; - -constexpr bool operator==(MyStringView lhs, MyStringView rhs) { - return lhs.str == rhs.str; -} - -#define MAGIC_ENUM_USING_ALIAS_OPTIONAL template using optional = MyOpt; -#define MAGIC_ENUM_USING_ALIAS_STRING using string = MyString; -#define MAGIC_ENUM_USING_ALIAS_STRING_VIEW using string_view = MyStringView; +#include "aliases.hpp" #include #include diff --git a/test/test_containers.cpp b/test/test_containers.cpp index 33f1cd4..bd07e84 100644 --- a/test/test_containers.cpp +++ b/test/test_containers.cpp @@ -30,6 +30,7 @@ # pragma warning(disable : 4244) // warning C4244: 'argument': conversion from 'const T' to 'unsigned int', possible loss of data. #endif +#include #define CATCH_CONFIG_MAIN #include diff --git a/test/test_flags.cpp b/test/test_flags.cpp index 35a7034..65a70bf 100644 --- a/test/test_flags.cpp +++ b/test/test_flags.cpp @@ -29,6 +29,7 @@ # pragma warning(disable : 4244) // warning C4244: 'argument': conversion from 'const T' to 'unsigned int', possible loss of data. #endif +#include #define CATCH_CONFIG_MAIN #include diff --git a/test/test_wchar_t.cpp b/test/test_wchar_t.cpp index f372627..388e46a 100644 --- a/test/test_wchar_t.cpp +++ b/test/test_wchar_t.cpp @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include #define CATCH_CONFIG_MAIN #include