diff --git a/.clang-format b/.clang-format index 52f456e..2b5c39c 100644 --- a/.clang-format +++ b/.clang-format @@ -136,3 +136,7 @@ WhitespaceSensitiveMacros: - PRAGMA_CLANG_WARNING_PUSH_OFF - PRAGMA_GCC_WARNING_PUSH_OFF - PRAGMA_GCC_WARNING_OFF + - throw_if_fail + - throw_on_fail + - BOOST_TEST + - BOOST_AUTO_TEST_CASE_TEMPLATE diff --git a/.cmake-format.yaml b/.cmake-format.yaml new file mode 100644 index 0000000..2b91b09 --- /dev/null +++ b/.cmake-format.yaml @@ -0,0 +1,245 @@ +_help_parse: Options affecting listfile parsing +parse: + _help_additional_commands: + - Specify structure for custom cmake functions + additional_commands: + foo: + flags: + - BAR + - BAZ + kwargs: + HEADERS: '*' + SOURCES: '*' + DEPENDS: '*' + _help_override_spec: + - Override configurations per-command where available + override_spec: {} + _help_vartags: + - Specify variable tags. + vartags: [] + _help_proptags: + - Specify property tags. + proptags: [] +_help_format: Options affecting formatting. +format: + _help_disable: + - Disable formatting entirely, making cmake-format a no-op + disable: false + _help_line_width: + - How wide to allow formatted cmake files + line_width: 120 + _help_tab_size: + - How many spaces to tab for indent + tab_size: 2 + _help_use_tabchars: + - If true, lines are indented using tab characters (utf-8 + - 0x09) instead of space characters (utf-8 0x20). + - In cases where the layout would require a fractional tab + - character, the behavior of the fractional indentation is + - governed by + use_tabchars: false + _help_fractional_tab_policy: + - If is True, then the value of this variable + - indicates how fractional indentions are handled during + - whitespace replacement. If set to 'use-space', fractional + - indentation is left as spaces (utf-8 0x20). If set to + - '`round-up` fractional indentation is replaced with a single' + - tab character (utf-8 0x09) effectively shifting the column + - to the next tabstop + fractional_tab_policy: use-space + _help_max_subgroups_hwrap: + - If an argument group contains more than this many sub-groups + - (parg or kwarg groups) then force it to a vertical layout. + max_subgroups_hwrap: 2 + _help_max_pargs_hwrap: + - If a positional argument group contains more than this many + - arguments, then force it to a vertical layout. + max_pargs_hwrap: 2 + _help_max_rows_cmdline: + - If a cmdline positional group consumes more than this many + - lines without nesting, then invalidate the layout (and nest) + max_rows_cmdline: 1 + _help_separate_ctrl_name_with_space: + - If true, separate flow control names from their parentheses + - with a space + separate_ctrl_name_with_space: false + _help_separate_fn_name_with_space: + - If true, separate function names from parentheses with a + - space + separate_fn_name_with_space: false + _help_dangle_parens: + - If a statement is wrapped to more than one line, than dangle + - the closing parenthesis on its own line. + dangle_parens: false + _help_dangle_align: + - If the trailing parenthesis must be 'dangled' on its on + - 'line, then align it to this reference: `prefix`: the start' + - 'of the statement, `prefix-indent`: the start of the' + - 'statement, plus one indentation level, `child`: align to' + - the column of the arguments + dangle_align: prefix + _help_min_prefix_chars: + - If the statement spelling length (including space and + - parenthesis) is smaller than this amount, then force reject + - nested layouts. + min_prefix_chars: 4 + _help_max_prefix_chars: + - If the statement spelling length (including space and + - parenthesis) is larger than the tab width by more than this + - amount, then force reject un-nested layouts. + max_prefix_chars: 10 + _help_max_lines_hwrap: + - If a candidate layout is wrapped horizontally but it exceeds + - this many lines, then reject the layout. + max_lines_hwrap: 2 + _help_line_ending: + - What style line endings to use in the output. + line_ending: unix + _help_command_case: + - Format command names consistently as 'lower' or 'upper' case + command_case: canonical + _help_keyword_case: + - Format keywords consistently as 'lower' or 'upper' case + keyword_case: unchanged + _help_always_wrap: + - A list of command names which should always be wrapped + always_wrap: [] + _help_enable_sort: + - If true, the argument lists which are known to be sortable + - will be sorted lexicographicall + enable_sort: true + _help_autosort: + - If true, the parsers may infer whether or not an argument + - list is sortable (without annotation). + autosort: false + _help_require_valid_layout: + - By default, if cmake-format cannot successfully fit + - everything into the desired linewidth it will apply the + - last, most agressive attempt that it made. If this flag is + - True, however, cmake-format will print error, exit with non- + - zero status code, and write-out nothing + require_valid_layout: false + _help_layout_passes: + - A dictionary mapping layout nodes to a list of wrap + - decisions. See the documentation for more information. + layout_passes: {} +_help_markup: Options affecting comment reflow and formatting. +markup: + _help_bullet_char: + - What character to use for bulleted lists + bullet_char: '*' + _help_enum_char: + - What character to use as punctuation after numerals in an + - enumerated list + enum_char: . + _help_first_comment_is_literal: + - If comment markup is enabled, don't reflow the first comment + - block in each listfile. Use this to preserve formatting of + - your copyright/license statements. + first_comment_is_literal: false + _help_literal_comment_pattern: + - If comment markup is enabled, don't reflow any comment block + - which matches this (regex) pattern. Default is `None` + - (disabled). + literal_comment_pattern: null + _help_fence_pattern: + - Regular expression to match preformat fences in comments + - default= ``r'^\s*([`~]{3}[`~]*)(.*)$'`` + fence_pattern: ^\s*([`~]{3}[`~]*)(.*)$ + _help_ruler_pattern: + - Regular expression to match rulers in comments default= + - '``r''^\s*[^\w\s]{3}.*[^\w\s]{3}$''``' + ruler_pattern: ^\s*[^\w\s]{3}.*[^\w\s]{3}$ + _help_explicit_trailing_pattern: + - If a comment line matches starts with this pattern then it + - is explicitly a trailing comment for the preceeding + - argument. Default is '#<' + explicit_trailing_pattern: '#<' + _help_hashruler_min_length: + - If a comment line starts with at least this many consecutive + - hash characters, then don't lstrip() them off. This allows + - for lazy hash rulers where the first hash char is not + - separated by space + hashruler_min_length: 10 + _help_canonicalize_hashrulers: + - If true, then insert a space between the first hash char and + - remaining hash chars in a hash ruler, and normalize its + - length to fill the column + canonicalize_hashrulers: true + _help_enable_markup: + - enable comment markup parsing and reflow + enable_markup: true +_help_lint: Options affecting the linter +lint: + _help_disabled_codes: + - a list of lint codes to disable + disabled_codes: [] + _help_function_pattern: + - regular expression pattern describing valid function names + function_pattern: '[0-9a-z_]+' + _help_macro_pattern: + - regular expression pattern describing valid macro names + macro_pattern: '[0-9A-Z_]+' + _help_global_var_pattern: + - regular expression pattern describing valid names for + - variables with global (cache) scope + global_var_pattern: '[A-Z][0-9A-Z_]+' + _help_internal_var_pattern: + - regular expression pattern describing valid names for + - variables with global scope (but internal semantic) + internal_var_pattern: _[A-Z][0-9A-Z_]+ + _help_local_var_pattern: + - regular expression pattern describing valid names for + - variables with local scope + local_var_pattern: '[a-z][a-z0-9_]+' + _help_private_var_pattern: + - regular expression pattern describing valid names for + - privatedirectory variables + private_var_pattern: _[0-9a-z_]+ + _help_public_var_pattern: + - regular expression pattern describing valid names for public + - directory variables + public_var_pattern: '[A-Z][0-9A-Z_]+' + _help_argument_var_pattern: + - regular expression pattern describing valid names for + - function/macro arguments and loop variables. + argument_var_pattern: '[a-z][a-z0-9_]+' + _help_keyword_pattern: + - regular expression pattern describing valid names for + - keywords used in functions or macros + keyword_pattern: '[A-Z][0-9A-Z_]+' + _help_max_conditionals_custom_parser: + - In the heuristic for C0201, how many conditionals to match + - within a loop in before considering the loop a parser. + max_conditionals_custom_parser: 2 + _help_min_statement_spacing: + - Require at least this many newlines between statements + min_statement_spacing: 1 + _help_max_statement_spacing: + - Require no more than this many newlines between statements + max_statement_spacing: 2 + max_returns: 6 + max_branches: 12 + max_arguments: 5 + max_localvars: 15 + max_statements: 50 +_help_encode: Options affecting file encoding +encode: + _help_emit_byteorder_mark: + - If true, emit the unicode byte-order mark (BOM) at the start + - of the file + emit_byteorder_mark: false + _help_input_encoding: + - Specify the encoding of the input file. Defaults to utf-8 + input_encoding: utf-8 + _help_output_encoding: + - Specify the encoding of the output file. Defaults to utf-8. + - Note that cmake only claims to support utf-8 so be careful + - when using anything else + output_encoding: utf-8 +_help_misc: Miscellaneous configurations options. +misc: + _help_per_command: + - A dictionary containing any per-command configuration + - overrides. Currently only `command_case` is supported. + per_command: {} diff --git a/CMakeLists.txt b/CMakeLists.txt index 14e377f..bf7b109 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,11 +3,15 @@ cmake_policy(SET CMP0167 NEW) cmake_policy(SET CMP0175 NEW) include(${CMAKE_CURRENT_LIST_DIR}/cmake/extract_version.cmake) -project(simple_enum - VERSION ${SIMPLE_ENUM_VERSION} - LANGUAGES CXX - ) - + +project( + simple_enum + VERSION ${SIMPLE_ENUM_VERSION} + LANGUAGES CXX +) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 23) endif() @@ -20,122 +24,73 @@ set(CMAKE_CXX_EXTENSIONS OFF) message(STATUS "simple_enum version v${SIMPLE_ENUM_VERSION}") include(cmake/get_cpm.cmake) -CPMAddPackage("gh:TheLartians/PackageProject.cmake@1.11.2") option(SIMPLE_ENUM_USE_GLAZE_3_1 "Use Glaze version 3.1" OFF) option(SIMPLE_ENUM_USE_GLAZE_4_0 "Use Glaze version 4.2" ON) -option(SIMPLE_ENUM_ENABLE_TESTS "Enable unit tests" ON ) +option(SIMPLE_ENUM_ENABLE_TESTS "Enable unit tests" ON) option(SIMPLE_ENUM_EXPORT_CMAKE_TARGETS "Enable cmake targets" ON) if(SIMPLE_ENUM_USE_GLAZE_3_1) - set(GLAZE_GIT_TAG "v3.4.3") + set(GLAZE_GIT_TAG "v3.4.3") elseif(SIMPLE_ENUM_USE_GLAZE_4_0) - set(GLAZE_GIT_TAG "v4.2.2") + set(GLAZE_GIT_TAG "v4.2.2") else() - message(FATAL_ERROR "Please select a Glaze version by setting USE_GLAZE_3_1, or SIMPLE_ENUM_USE_GLAZE_4_0") -endif() - -if(PROJECT_IS_TOP_LEVEL AND SIMPLE_ENUM_ENABLE_TESTS) - #---------------------------------------------------------------- - # boost-ext/ut - #---------------------------------------------------------------- - CPMAddPackage( - ut - GITHUB_REPOSITORY arturbac/ut-ext - GIT_TAG v2.0.1_7 - ) - find_package(ut-ext REQUIRED) - if( SIMPLE_ENUM_ENABLE_TIME_TRACE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "AppleClang")) - # external dependencies only for comparison benchmarking - CPMAddPackage( - NAME magic_enum - GITHUB_REPOSITORY Neargye/magic_enum - GIT_TAG v0.9.5 - GIT_SHALLOW TRUE - ) - find_package(magic_enum REQUIRED) - - CPMAddPackage( - Name reflect - GITHUB_REPOSITORY boost-ext/reflect - GIT_TAG main - GIT_SHALLOW TRUE - ) - - add_library(reflect INTERFACE) - target_include_directories(reflect SYSTEM INTERFACE ${reflect_SOURCE_DIR}) - target_compile_definitions(reflect INTERFACE REFLECT_DISABLE_STATIC_ASSERT_TESTS) - add_library(reflect::reflect ALIAS reflect) - find_package(reflect REQUIRED) - endif() - - #---------------------------------------------------------------- - # glaze - #---------------------------------------------------------------- - CPMAddPackage( - glaze - GITHUB_REPOSITORY stephenberry/glaze - GIT_TAG ${GLAZE_GIT_TAG} - GIT_SHALLOW TRUE - ) + message(FATAL_ERROR "Please select a Glaze version by setting USE_GLAZE_3_1, or SIMPLE_ENUM_USE_GLAZE_4_0") endif() - - add_library(simple_enum INTERFACE) -if(PROJECT_IS_TOP_LEVEL) - add_library(simple_enum::simple_enum ALIAS simple_enum ) -endif() if(SIMPLE_ENUM_USE_GLAZE_3_1) - target_compile_definitions( simple_enum INTERFACE SIMPLE_ENUM_GLZ_3_1_x) + target_compile_definitions(simple_enum INTERFACE SIMPLE_ENUM_GLZ_3_1_x) elseif(SIMPLE_ENUM_USE_GLAZE_4_0) - target_compile_definitions( simple_enum INTERFACE SIMPLE_ENUM_GLZ_3_1_x) + target_compile_definitions(simple_enum INTERFACE SIMPLE_ENUM_GLZ_3_1_x) else() - message(FATAL_ERROR "Future, unimplemented") + message(FATAL_ERROR "Future, unimplemented") endif() +target_compile_features(simple_enum INTERFACE cxx_std_23) if(SIMPLE_ENUM_OPT_IN_STATIC_ASSERTS) - target_compile_definitions( simple_enum INTERFACE SIMPLE_ENUM_OPT_IN_STATIC_ASSERTS=1) + target_compile_definitions(simple_enum INTERFACE SIMPLE_ENUM_OPT_IN_STATIC_ASSERTS=1) endif() -target_include_directories(simple_enum - INTERFACE $ - $ - ) -include(GNUInstallDirs) -include(CMakePackageConfigHelpers) - -install(TARGETS simple_enum - EXPORT simple_enum_targets - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +target_sources( + simple_enum INTERFACE FILE_SET HEADERS BASE_DIRS $ + $ ) -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +install( + TARGETS simple_enum + EXPORT simple_enum_targets + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILE_SET HEADERS ) -if(SIMPLE_ENUM_EXPORT_CMAKE_TARGETS) - install(EXPORT simple_enum_targets - FILE simple_enumTargets.cmake - NAMESPACE simple_enum:: - DESTINATION lib/cmake/simple_enum - ) -endif() - +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) configure_package_config_file( cmake/simple_enumConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/simple_enumConfig.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/simple_enum) + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/simple_enum +) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/presetsConfigVersion.cmake VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMajorVersion) + COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT +) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/simple_enumConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/simple_enumConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/simple_enum +) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/simple_enumConfig.cmake - ${CMAKE_CURRENT_BINARY_DIR}/simple_enumConfigVersion.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/simple_enum) +if(SIMPLE_ENUM_EXPORT_CMAKE_TARGETS) + install( + EXPORT simple_enum_targets + FILE simple_enumTargets.cmake + NAMESPACE simple_enum:: + DESTINATION lib/cmake/simple_enum + ) +endif() if(PROJECT_IS_TOP_LEVEL AND SIMPLE_ENUM_ENABLE_TESTS) include(cmake/unit_test_integration.cmake) @@ -144,15 +99,9 @@ if(PROJECT_IS_TOP_LEVEL AND SIMPLE_ENUM_ENABLE_TESTS) add_subdirectory(tests) add_subdirectory(examples) - if( SIMPLE_ENUM_ENABLE_TIME_TRACE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "AppleClang")) - add_subdirectory(time_trace) + if(SIMPLE_ENUM_ENABLE_TIME_TRACE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "AppleClang")) + add_subdirectory(time_trace) endif() - execute_process( - COMMAND ${CMAKE_COMMAND} --list-presets - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) - execute_process( - COMMAND ${CMAKE_COMMAND} --workflow --list-presets - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) + execute_process(COMMAND ${CMAKE_COMMAND} --list-presets WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process(COMMAND ${CMAKE_COMMAND} --workflow --list-presets WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) endif() diff --git a/cmake/simple_enumConfig.cmake.in b/cmake/simple_enumConfig.cmake.in index af65310..96f10a8 100644 --- a/cmake/simple_enumConfig.cmake.in +++ b/cmake/simple_enumConfig.cmake.in @@ -1,4 +1,3 @@ -# cmake/your_library_nameConfig.cmake.in @PACKAGE_INIT@ include(CMakeFindDependencyMacro) diff --git a/create_git_tag.sh b/create_git_tag.sh index eba753c..c3ee876 100755 --- a/create_git_tag.sh +++ b/create_git_tag.sh @@ -3,7 +3,7 @@ # SPDX-License-Identifier: BSL-1.0 # SPDX-PackageHomePage: https://github.com/arturbac/simple_enum # Path to the header file -file_path="include/simple_enum/simple_enum.hpp" +file_path="include/simple_enum/core.hpp" # Check if the file exists if [ -f "$file_path" ]; then diff --git a/include/simple_enum/core.hpp b/include/simple_enum/core.hpp index 11e8536..df51d6a 100644 --- a/include/simple_enum/core.hpp +++ b/include/simple_enum/core.hpp @@ -6,7 +6,7 @@ #include #include -#define SIMPLE_ENUM_NAME_VERSION "0.8.7" +#define SIMPLE_ENUM_NAME_VERSION "0.8.8" namespace simple_enum::inline v0_8 { diff --git a/include/simple_enum/expected.h b/include/simple_enum/expected.h index 2845c2a..302bb13 100644 --- a/include/simple_enum/expected.h +++ b/include/simple_enum/expected.h @@ -1,11 +1,17 @@ -// SPDX-FileCopyrightText: 2024 Artur Bać +// SPDX-FileCopyrightText: 2024-2025 Artur Bać // SPDX-License-Identifier: BSL-1.0 // SPDX-PackageHomePage: https://github.com/arturbac/simple_enum #pragma once +#if defined(SMALL_VECTORS_EXPECTED) && (!defined(SMALL_VECTORS_EXPECTED_API) || SMALL_VECTORS_EXPECTED_API != 2) +#error "mixing different expected implementations in single TU" +#endif + #ifndef SMALL_VECTORS_EXPECTED #define SMALL_VECTORS_EXPECTED +#define SMALL_VECTORS_EXPECTED_API 2 + #include #if !defined(SMALL_VECTORS_ENABLE_CUSTOM_EXCPECTED) && defined(__cpp_lib_expected) && __cpp_lib_expected >= 202211L @@ -21,6 +27,7 @@ using std::unexpect; using std::unexpect_t; using std::unexpected; } // namespace cxx23 + #else #include @@ -30,8 +37,6 @@ using std::unexpected; #include #include -#include - namespace cxx23 { template @@ -107,7 +112,42 @@ namespace detail inline constexpr bool swap_no_throw = (std::is_nothrow_move_constructible_v || std::is_void_v) && std::is_nothrow_move_constructible_v && (std::is_nothrow_swappable_v || std::is_void_v) && std::is_nothrow_swappable_v; - } + + template + struct revert_if_except_t + { + struct empty_t + { + }; + + T value; + std::conditional_t release_address; + + constexpr explicit revert_if_except_t(T && v, T * release_addr) : value{std::move(v)} + { + if constexpr(!use_noexcept) + release_address = release_addr; + } + + constexpr ~revert_if_except_t() + requires use_noexcept + = default; + + constexpr T && release() noexcept + { + if constexpr(!use_noexcept) + release_address = nullptr; + return std::move(value); + } + + constexpr ~revert_if_except_t() + { + if constexpr(!use_noexcept) + if(release_address != nullptr) + std::construct_at(release_address, std::move(value)); + } + }; + } // namespace detail template class [[clang::trivial_abi]] unexpected @@ -147,13 +187,13 @@ class [[clang::trivial_abi]] unexpected { } - constexpr error_type const & error() const & noexcept { return error_; } + constexpr auto error() const & noexcept -> error_type const & { return error_; } - constexpr error_type & error() & noexcept { return error_; } + constexpr auto error() & noexcept -> error_type & { return error_; } - constexpr error_type const && error() const && noexcept { return std::move(error_); } + constexpr auto error() const && noexcept -> error_type const && { return std::move(error_); } - constexpr error_type && error() && noexcept { return std::move(error_); } + constexpr auto error() && noexcept -> error_type && { return std::move(error_); } constexpr void swap(unexpected & other) noexcept(std::is_nothrow_swappable_v) requires std::swappable @@ -180,10 +220,7 @@ unexpected(E) -> unexpected; template class bad_expected_access; -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wweak-vtables" -#endif + template<> class bad_expected_access : public std::exception { @@ -207,9 +244,6 @@ class bad_expected_access : public std::exception } #endif }; -#if defined(__clang__) -#pragma clang diagnostic pop -#endif template class bad_expected_access : public bad_expected_access @@ -222,25 +256,25 @@ class bad_expected_access : public bad_expected_access explicit bad_expected_access(E e) : error_{std::move(e)} {} [[nodiscard]] - error_type const & error() const & noexcept + auto error() const & noexcept -> error_type const & { return error_; } [[nodiscard]] - error_type & error() & noexcept + auto error() & noexcept -> error_type & { return error_; } [[nodiscard]] - error_type const && error() const && noexcept + auto error() const && noexcept -> error_type const && { return std::move(error_); } [[nodiscard]] - error_type && error() && noexcept + auto error() && noexcept -> error_type && { return std::move(error_); } @@ -323,11 +357,30 @@ class [[nodiscard, clang::trivial_abi]] expected private: using bad_access_exception = bad_expected_access>; + static constexpr bool value_copy_constructible = std::is_copy_constructible_v; + static constexpr bool value_move_constructible = std::is_move_constructible_v; + static constexpr bool value_copy_assignable = std::is_copy_assignable_v; + static constexpr bool value_move_assignable = std::is_move_assignable_v; + + static constexpr bool value_nothrow_copy_constructible = std::is_nothrow_copy_constructible_v; + static constexpr bool value_nothrow_copy_assignable = std::is_nothrow_copy_assignable_v; static constexpr bool value_nothrow_move_constructible = std::is_nothrow_move_constructible_v; + static constexpr bool value_nothrow_move_assignable = std::is_nothrow_move_assignable_v; + + static constexpr bool error_copy_constructible = std::is_copy_constructible_v; + static constexpr bool error_copy_assignable = std::is_copy_assignable_v; + static constexpr bool error_move_constructible = std::is_move_constructible_v; + static constexpr bool error_move_assignable = std::is_move_assignable_v; + + static constexpr bool error_nothrow_copy_assignable = std::is_nothrow_copy_assignable_v; + static constexpr bool error_nothrow_copy_constructible = std::is_nothrow_copy_constructible_v; + + static constexpr bool error_nothrow_move_constructible = std::is_nothrow_move_constructible_v; + static constexpr bool error_nothrow_move_assignable = std::is_nothrow_move_assignable_v; + static constexpr bool value_trivially_copy_constructible = std::is_trivially_copy_constructible_v; static constexpr bool error_trivially_copy_constructible = std::is_trivially_copy_constructible_v; - static constexpr bool error_nothrow_move_constructible = std::is_nothrow_move_constructible_v; static constexpr bool both_are_nothrow_move_constructible = value_nothrow_move_constructible && error_nothrow_move_constructible; @@ -337,10 +390,6 @@ class [[nodiscard, clang::trivial_abi]] expected static constexpr bool both_are_trivially_move_constructible = std::is_trivially_move_constructible_v && std::is_trivially_move_constructible_v; - static constexpr bool value_copy_constructible = std::is_copy_constructible_v; - static constexpr bool value_move_constructible = std::is_move_constructible_v; - static constexpr bool error_copy_constructible = std::is_copy_constructible_v; - static constexpr bool error_move_constructible = std::is_move_constructible_v; union { T value_; @@ -500,43 +549,172 @@ class [[nodiscard, clang::trivial_abi]] expected } } +private: + template + static constexpr void + reinit_expected(ET * new_value, EU * old_value, A && arg) noexcept(std::is_nothrow_constructible_v) + { + if constexpr(std::is_nothrow_constructible_v) + { + std::destroy_at(old_value); + std::construct_at(new_value, std::forward(arg)); + } + else if constexpr(std::is_nothrow_move_constructible_v) + { + ET tmp(std::forward(arg)); + std::destroy_at(old_value); + std::construct_at(new_value, std::move(tmp)); + } + else + { + static_assert(std::is_nothrow_move_constructible_v); + detail::revert_if_except_t obj(std::move(*old_value), old_value); + std::construct_at(new_value, std::forward(arg)); + obj.release(); + } + } + + template + inline constexpr void + assign_value(other_value_type && v) noexcept(std::is_nothrow_constructible_v) + { + if(has_value_) + value_ = std::forward(v); + else + { + reinit_expected(std::addressof(value_), std::addressof(error_), std::forward(v)); + has_value_ = true; + } + } + + template + inline constexpr void + assign_unexpected(other_error_type && v) noexcept(std::is_nothrow_constructible_v) + { + if(has_value_) + { + reinit_expected(std::addressof(error_), std::addressof(value_), std::forward(v)); + has_value_ = false; + } + else + error_ = std::forward(v); + } + +public: + constexpr auto operator=(expected const &) -> expected & = delete; + + constexpr expected & operator=(expected const & rh) noexcept( + value_nothrow_copy_assignable and value_nothrow_copy_constructible and error_nothrow_copy_assignable + and error_nothrow_copy_constructible + ) + requires( + value_copy_assignable and value_copy_constructible and error_copy_assignable and error_copy_constructible + and (value_nothrow_move_constructible or error_nothrow_move_constructible) + ) + { + if(has_value_ and rh.has_value()) + value_ = rh.value_; + else if(has_value_) + assign_unexpected(rh.error_); + else if(rh.has_value()) + assign_value(rh.value_); + else + error_ = rh.error_; + return *this; + } + + constexpr expected & operator=(expected && rh) noexcept( + value_nothrow_move_assignable and value_nothrow_move_constructible and error_nothrow_move_assignable + and error_nothrow_move_constructible + ) + requires( + value_move_constructible and value_move_assignable and error_move_constructible and error_move_assignable + and (value_nothrow_move_constructible or error_nothrow_move_constructible) + ) + { + if(has_value_ && rh.has_value()) + value_ = std::move(rh.value_); + else if(has_value_) + assign_unexpected(std::move(rh.error_)); + else if(rh.has_value()) + assign_value(std::move(rh.value_)); + else + error_ = std::move(rh.error_); + return *this; + } + + template + constexpr auto operator=(Up && v) -> expected & + requires( + not std::is_same_v> && not concepts::is_unexpected> + and std::is_constructible_v && std::is_assignable_v + and (std::is_nothrow_constructible_v or value_nothrow_move_constructible or error_nothrow_move_constructible) + ) + { + if(has_value_) + value_ = std::forward(v); + else + assign_value(std::forward(v)); + return *this; + } + + template + requires std::is_constructible_v + and std::is_assignable_v + && (std::is_nothrow_constructible_v || value_nothrow_move_constructible || error_nothrow_move_constructible) + constexpr auto operator=(unexpected const & e) -> expected & + { + assign_unexpected(e.error()); + return *this; + } + + template + requires std::is_constructible_v && std::is_assignable_v + && (std::is_nothrow_constructible_v || value_nothrow_move_constructible || error_nothrow_move_constructible) + constexpr auto operator=(unexpected && e) -> expected & + { + assign_unexpected(std::move(e).error()); + return *this; + } + +public: [[nodiscard]] - constexpr value_type const * operator->() const noexcept + constexpr auto operator->() const noexcept -> value_type const * { assert(has_value_); return std::addressof(value_); } [[nodiscard]] - constexpr value_type * operator->() noexcept + constexpr auto operator->() noexcept -> value_type * { assert(has_value_); return std::addressof(value_); } [[nodiscard]] - constexpr value_type const & operator*() const & noexcept + constexpr auto operator*() const & noexcept -> value_type const & { assert(has_value_); return value_; } [[nodiscard]] - constexpr value_type & operator*() & noexcept + constexpr auto operator*() & noexcept -> value_type & { assert(has_value_); return value_; } [[nodiscard]] - constexpr value_type const && operator*() const && noexcept + constexpr auto operator*() const && noexcept -> value_type const && { assert(has_value_); return std::move(value_); } [[nodiscard]] - constexpr value_type && operator*() && noexcept + constexpr auto operator*() && noexcept -> value_type && { assert(has_value_); return std::move(value_); @@ -555,7 +733,7 @@ class [[nodiscard, clang::trivial_abi]] expected } [[nodiscard]] - constexpr value_type & value() & + constexpr auto value() & -> value_type & requires error_copy_constructible { if(has_value_) [[likely]] @@ -565,7 +743,7 @@ class [[nodiscard, clang::trivial_abi]] expected } [[nodiscard]] - constexpr value_type const & value() const & + constexpr auto value() const & -> value_type const & requires error_copy_constructible { if(has_value_) [[likely]] @@ -575,17 +753,14 @@ class [[nodiscard, clang::trivial_abi]] expected } [[nodiscard]] - constexpr value_type && value() && - requires(error_copy_constructible || error_move_constructible) - { + constexpr auto value() && -> value_type && requires(error_copy_constructible || error_move_constructible) { if(has_value_) [[likely]] return std::move(value_); else throw bad_access_exception{std::move(error_)}; - } + } - [[nodiscard]] - constexpr value_type const && value() const && + [[nodiscard]] constexpr auto value() const && -> value_type const && requires(error_copy_constructible || error_move_constructible) { if(has_value_) [[likely]] @@ -595,28 +770,28 @@ class [[nodiscard, clang::trivial_abi]] expected } [[nodiscard]] - constexpr error_type const & error() const & noexcept + constexpr auto error() const & noexcept -> error_type const & { assert(!has_value_); return error_; } [[nodiscard]] - constexpr error_type & error() & noexcept + constexpr auto error() & noexcept -> error_type & { assert(!has_value_); return error_; } [[nodiscard]] - constexpr error_type const && error() const && noexcept + constexpr auto error() const && noexcept -> error_type const && { assert(!has_value_); return std::move(error_); } [[nodiscard]] - constexpr error_type && error() && noexcept + constexpr auto error() && noexcept -> error_type && { assert(!has_value_); return std::move(error_); @@ -624,9 +799,9 @@ class [[nodiscard, clang::trivial_abi]] expected template [[nodiscard]] - constexpr value_type value_or( - U && default_value - ) const & noexcept(std::is_nothrow_copy_constructible_v && std::is_nothrow_convertible_v) + constexpr auto value_or(U && default_value) const & noexcept( + std::is_nothrow_copy_constructible_v && std::is_nothrow_convertible_v + ) -> value_type requires value_copy_constructible && std::is_convertible_v { return has_value_ ? value_ : static_cast(std::forward(default_value)); @@ -634,9 +809,9 @@ class [[nodiscard, clang::trivial_abi]] expected template [[nodiscard]] - constexpr value_type value_or( - U && default_value - ) && noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_convertible_v) + constexpr auto value_or(U && default_value) && noexcept( + std::is_nothrow_move_constructible_v && std::is_nothrow_convertible_v + ) -> value_type requires value_move_constructible && std::is_convertible_v { return has_value_ ? std::move(value_) : static_cast(std::forward(default_value)); @@ -835,7 +1010,7 @@ class [[nodiscard, clang::trivial_abi]] expected template requires std::same_as -class expected +class [[nodiscard, clang::trivial_abi]] expected { public: using value_type = void; @@ -847,12 +1022,18 @@ class expected private: using bad_access_exception = bad_expected_access>; + static constexpr bool error_nothrow_copy_constructible = std::is_nothrow_copy_constructible_v; static constexpr bool error_trivially_copy_constructible = std::is_trivially_copy_constructible_v; static constexpr bool error_nothrow_move_constructible = std::is_nothrow_move_constructible_v; + static constexpr bool error_nothrow_move_assignable = std::is_nothrow_move_assignable_v; static constexpr bool error_trivially_move_constructible = std::is_trivially_move_constructible_v; static constexpr bool error_copy_constructible = std::is_copy_constructible_v; static constexpr bool error_move_constructible = std::is_move_constructible_v; + static constexpr bool error_nothrow_copy_assignable = std::is_nothrow_copy_assignable_v; + static constexpr bool error_copy_assignable = std::is_copy_assignable_v; + static constexpr bool error_move_assignable = std::is_move_assignable_v; + union { E error_; }; @@ -938,7 +1119,8 @@ class expected template requires std::is_constructible_v - constexpr explicit(!std::is_convertible_v) expected(unexpected const & e) : has_value_{} + inline constexpr explicit(!std::is_convertible_v) expected(unexpected const & e) : + has_value_{} { std::construct_at(std::addressof(error_), e.error()); } @@ -950,6 +1132,60 @@ class expected std::construct_at(std::addressof(error_), std::forward(e.error())); } +private: + template + inline constexpr void assign_unexpected(other_error_type && v) + { + if(has_value_) + { + std::construct_at(std::addressof(error_), std::forward(v)); + has_value_ = false; + } + else + error_ = std::forward(v); + } + +public: + auto operator=(expected const &) -> expected & = delete; + + constexpr auto operator=(expected const & rh + ) noexcept(error_nothrow_copy_constructible and error_nothrow_copy_assignable) -> expected & + requires error_copy_constructible and error_copy_assignable + { + if(rh.has_value_) + emplace(); + else + assign_unexpected(rh.error_); + return *this; + } + + constexpr auto operator=(expected && rh) noexcept(error_nothrow_move_constructible and error_nothrow_move_assignable) + -> expected & + requires error_move_constructible && error_move_assignable + { + if(rh.has_value_) + emplace(); + else + assign_unexpected(std::move(rh.error_)); + return *this; + } + + template + requires std::is_constructible_v && std::is_assignable_v + constexpr auto operator=(unexpected const & ux) -> expected & + { + assign_unexpected(ux.error()); + return *this; + } + + template + requires std::is_constructible_v && std::is_assignable_v + constexpr auto operator=(unexpected && ux) -> expected & + { + assign_unexpected(std::move(ux).error()); + return *this; + } + constexpr ~expected() requires std::is_trivially_destructible_v = default; @@ -990,28 +1226,28 @@ class expected } [[nodiscard]] - constexpr error_type const & error() const & noexcept + constexpr auto error() const & noexcept -> error_type const & { assert(!has_value_); return error_; } [[nodiscard]] - constexpr error_type & error() & noexcept + constexpr auto error() & noexcept -> error_type & { assert(!has_value_); return error_; } [[nodiscard]] - constexpr error_type const && error() const && noexcept + constexpr auto error() const && noexcept -> error_type const && { assert(!has_value_); return std::move(error_); } [[nodiscard]] - constexpr error_type && error() && noexcept + constexpr auto error() && noexcept -> error_type && { assert(!has_value_); return std::move(error_); @@ -1259,47 +1495,10 @@ namespace detail } } - template - struct revert_if_except_t - { - struct empty_t - { - }; - - T value; - std::conditional_t release_address; - - constexpr explicit revert_if_except_t(T && v, T * release_addr) : value{std::move(v)} - { - if constexpr(!use_noexcept) - release_address = release_addr; - } - - constexpr ~revert_if_except_t() - requires use_noexcept - = default; - - constexpr T && release() noexcept - { - if constexpr(!use_noexcept) - release_address = nullptr; - return std::move(value); - } - - constexpr ~revert_if_except_t() - { - if constexpr(!use_noexcept) - if(release_address != nullptr) - std::construct_at(release_address, std::move(value)); - } - }; - struct swap_expected_t { template - static_call_operator constexpr void - operator()(expected & l, expected & r) static_call_operator_const noexcept(detail::swap_no_throw - ) + static constexpr void operator()(expected & l, expected & r) noexcept(detail::swap_no_throw) requires concepts::swap_constraints { if(l.has_value() && r.has_value()) @@ -1360,7 +1559,5 @@ namespace detail } // namespace detail } // namespace cxx23 - -#include #endif #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bf6dc45..a962698 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,29 +1,74 @@ +add_library(simple_enum::simple_enum ALIAS simple_enum) + +#---------------------------------------------------------------- +# boost-ext/ut +#---------------------------------------------------------------- +CPMAddPackage( + ut + GITHUB_REPOSITORY arturbac/ut-ext + GIT_TAG v2.0.1_7 +) +find_package(ut-ext REQUIRED) +if(SIMPLE_ENUM_ENABLE_TIME_TRACE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "AppleClang")) + # external dependencies only for comparison benchmarking + CPMAddPackage( + NAME magic_enum + GITHUB_REPOSITORY Neargye/magic_enum + GIT_TAG v0.9.5 + GIT_SHALLOW TRUE + ) + find_package(magic_enum REQUIRED) + + CPMAddPackage( + Name reflect + GITHUB_REPOSITORY boost-ext/reflect + GIT_TAG main + GIT_SHALLOW TRUE + ) + + add_library(reflect INTERFACE) + target_include_directories(reflect SYSTEM INTERFACE ${reflect_SOURCE_DIR}) + target_compile_definitions(reflect INTERFACE REFLECT_DISABLE_STATIC_ASSERT_TESTS) + add_library(reflect::reflect ALIAS reflect) + find_package(reflect REQUIRED) +endif() + +#---------------------------------------------------------------- +# glaze +#---------------------------------------------------------------- +CPMAddPackage( + glaze + GITHUB_REPOSITORY stephenberry/glaze + GIT_TAG ${GLAZE_GIT_TAG} + GIT_SHALLOW TRUE +) add_ut_test(test_simple_enum.cc) -target_compile_definitions( test_simple_enum PRIVATE BOOST_UT_DISABLE_MODULE) +target_compile_definitions(test_simple_enum PRIVATE BOOST_UT_DISABLE_MODULE) add_ut_test(ranges_views_ut.cc) -target_compile_definitions( ranges_views_ut PRIVATE BOOST_UT_DISABLE_MODULE) +target_compile_definitions(ranges_views_ut PRIVATE BOOST_UT_DISABLE_MODULE) add_ut_test(enum_cast_ut.cc) -target_compile_definitions( enum_cast_ut PRIVATE BOOST_UT_DISABLE_MODULE) +target_compile_definitions(enum_cast_ut PRIVATE BOOST_UT_DISABLE_MODULE) add_ut_test(enum_index_ut.cc) -target_compile_definitions( enum_index_ut PRIVATE BOOST_UT_DISABLE_MODULE) +target_compile_definitions(enum_index_ut PRIVATE BOOST_UT_DISABLE_MODULE) add_ut_test(generic_error_category_ut.cc) -target_compile_definitions( generic_error_category_ut PRIVATE BOOST_UT_DISABLE_MODULE) +target_compile_definitions(generic_error_category_ut PRIVATE BOOST_UT_DISABLE_MODULE) -if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "13") OR - (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "16") OR - (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")) +if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "13") + OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "16") + OR (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +) add_ut_test(std_format_ut.cc) endif() CPMAddPackage( NAME fmt GITHUB_REPOSITORY fmtlib/fmt - GIT_TAG 10.2.1 + GIT_TAG 11.1.2 ) # find_package(fmt REQUIRED) @@ -35,7 +80,7 @@ if(NOT MSVC) target_link_libraries(glaze_enum_name_ut PRIVATE glaze::glaze) # glaze causes objects to grow over 4GB on msvc # https://developercommunity.visualstudio.com/t/Memory-Explosion:-compiler-limit:-objec/10795558 - # target_compile_options(glaze_enum_name_ut PRIVATE + # target_compile_options(glaze_enum_name_ut PRIVATE # "/bigobj" # "/Zm500" # Increased memory allocation # "/Os" # Favor small code size @@ -44,6 +89,5 @@ if(NOT MSVC) endif() add_executable(diagnostics EXCLUDE_FROM_ALL) -target_sources( diagnostics PRIVATE diagnostics.cc) -target_compile_definitions( diagnostics PRIVATE BOOST_UT_DISABLE_MODULE) - +target_sources(diagnostics PRIVATE diagnostics.cc) +target_compile_definitions(diagnostics PRIVATE BOOST_UT_DISABLE_MODULE)