├── .clang-format ├── .clang-tidy ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── unittests_linux.yml │ ├── unittests_macos.yml │ └── unittests_windows.yml ├── .gitmodules ├── CMakeLists.txt ├── Doxyfile ├── Licence.txt ├── Readme.md ├── cmake └── ProjectConfig.cmake.in ├── doc └── custom_domain_worked_example.md ├── example ├── quick_status_code_from_enum.cpp ├── thrown_exception.cpp └── variant_return.cpp ├── include └── status-code │ ├── boost_error_code.hpp │ ├── com_code.hpp │ ├── config.hpp │ ├── detail │ ├── nt_code_to_generic_code.ipp │ ├── nt_code_to_win32_code.ipp │ └── win32_code_to_generic_code.ipp │ ├── error.hpp │ ├── errored_status_code.hpp │ ├── generic_code.hpp │ ├── getaddrinfo_code.hpp │ ├── http_status_code.hpp │ ├── iostream_support.hpp │ ├── nested_status_code.hpp │ ├── nt_code.hpp │ ├── posix_code.hpp │ ├── quick_status_code_from_enum.hpp │ ├── result.hpp │ ├── status_code.hpp │ ├── status_code_domain.hpp │ ├── status_code_gdb.py │ ├── status_error.hpp │ ├── std_error_code.hpp │ ├── system_code.hpp │ ├── system_code_from_exception.hpp │ ├── system_error2.hpp │ └── win32_code.hpp ├── index.html ├── make_docs.sh ├── single-header ├── Readme.md ├── system_error2-nowindows.hpp └── system_error2.hpp ├── test ├── issue0050.cpp ├── issue0056.cpp ├── main.cpp ├── p0709a.cpp └── result.cpp ├── utils └── generate-tables.cpp └── wg21 └── file_io_error.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -2 4 | ConstructorInitializerIndentWidth: 4 5 | AlignEscapedNewlinesLeft: false 6 | AlignTrailingComments: true 7 | AllowAllParametersOfDeclarationOnNextLine: true 8 | AllowShortBlocksOnASingleLine: false 9 | AllowShortIfStatementsOnASingleLine: false 10 | AllowShortLoopsOnASingleLine: false 11 | AllowShortFunctionsOnASingleLine: Inline 12 | AlwaysBreakTemplateDeclarations: false 13 | AlwaysBreakBeforeMultilineStrings: false 14 | BreakBeforeBinaryOperators: None 15 | BreakBeforeBraces: Allman 16 | BreakBeforeTernaryOperators: false 17 | BreakConstructorInitializersBeforeComma: true 18 | BinPackParameters: true 19 | ColumnLimit: 160 20 | CommentPragmas: '^!<' 21 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 22 | ContinuationIndentWidth: 0 23 | Cpp11BracedListStyle: true 24 | DerivePointerAlignment: false 25 | DisableFormat: false 26 | ExperimentalAutoDetectBinPacking: false 27 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 28 | IndentCaseLabels: false 29 | IndentFunctionDeclarationAfterType: false 30 | IndentWidth: 2 31 | IndentWrappedFunctionNames: false 32 | MaxEmptyLinesToKeep: 2 33 | KeepEmptyLinesAtTheStartOfBlocks: true 34 | NamespaceIndentation: All 35 | ObjCSpaceAfterProperty: false 36 | ObjCSpaceBeforeProtocolList: false 37 | PenaltyBreakBeforeFirstCallParameter: 19 38 | PenaltyBreakComment: 300 39 | PenaltyBreakString: 1000 40 | PenaltyBreakFirstLessLess: 120 41 | PenaltyExcessCharacter: 1000000 42 | PenaltyReturnTypeOnItsOwnLine: 60 43 | PointerAlignment: Right 44 | Standard: Cpp11 45 | SpaceAfterCStyleCast: true 46 | SpaceBeforeAssignmentOperators: true 47 | SpaceBeforeParens: Never 48 | SpaceInEmptyParentheses: false 49 | SpacesBeforeTrailingComments: 2 50 | SpacesInParentheses: false 51 | SpacesInAngles: false 52 | SpacesInCStyleCastParentheses: false 53 | SpacesInContainerLiterals: true 54 | TabWidth: 8 55 | UseTab: Never 56 | ... 57 | 58 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '*,-llvm-header-guard,-google-build-using-namespace,-google-runtime-int,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-modernize-unary-static-assert,-fuchsia-*,-llvm-namespace-comment' 3 | WarningsAsErrors: '' 4 | HeaderFilterRegex: '.*' 5 | AnalyzeTemporaryDtors: true 6 | CheckOptions: 7 | - key: cert-oop11-cpp.UseCERTSemantics 8 | value: '1' 9 | - key: google-readability-braces-around-statements.ShortStatementLines 10 | value: '1' 11 | - key: google-readability-function-size.StatementThreshold 12 | value: '800' 13 | - key: google-readability-namespace-comments.ShortNamespaceLines 14 | value: '10' 15 | - key: google-readability-namespace-comments.SpacesBeforeComments 16 | value: '2' 17 | - key: modernize-loop-convert.MaxCopySize 18 | value: '16' 19 | - key: modernize-loop-convert.MinConfidence 20 | value: reasonable 21 | - key: modernize-loop-convert.NamingStyle 22 | value: CamelCase 23 | - key: modernize-pass-by-value.IncludeStyle 24 | value: llvm 25 | - key: modernize-replace-auto-ptr.IncludeStyle 26 | value: llvm 27 | - key: modernize-use-nullptr.NullMacros 28 | value: 'NULL' 29 | ... 30 | 31 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto !eol svneol=native#text/plain 2 | *.gitattributes text svneol=native#text/plain 3 | 4 | # Scriptish formats 5 | *.bat text svneol=native#text/plain 6 | *.bsh text svneol=native#text/x-beanshell 7 | *.cgi text svneol=native#text/plain 8 | *.cmd text svneol=native#text/plain 9 | *.js text svneol=native#text/javascript 10 | *.php text svneol=native#text/x-php 11 | *.pl text svneol=native#text/x-perl 12 | *.pm text svneol=native#text/x-perl 13 | *.py text svneol=native#text/x-python 14 | *.sh eol=lf svneol=LF#text/x-sh 15 | configure eol=lf svneol=LF#text/x-sh 16 | 17 | # Image formats 18 | *.bmp binary svneol=unset#image/bmp 19 | *.gif binary svneol=unset#image/gif 20 | *.ico binary svneol=unset#image/ico 21 | *.jpeg binary svneol=unset#image/jpeg 22 | *.jpg binary svneol=unset#image/jpeg 23 | *.png binary svneol=unset#image/png 24 | *.tif binary svneol=unset#image/tiff 25 | *.tiff binary svneol=unset#image/tiff 26 | *.svg text svneol=native#image/svg%2Bxml 27 | 28 | # Data formats 29 | *.pdf binary svneol=unset#application/pdf 30 | *.avi binary svneol=unset#video/avi 31 | *.doc binary svneol=unset#application/msword 32 | *.dsp text svneol=crlf#text/plain 33 | *.dsw text svneol=crlf#text/plain 34 | *.eps binary svneol=unset#application/postscript 35 | *.json text svneol=native#application/json 36 | *.gz binary svneol=unset#application/gzip 37 | *.mov binary svneol=unset#video/quicktime 38 | *.mp3 binary svneol=unset#audio/mpeg 39 | *.ppt binary svneol=unset#application/vnd.ms-powerpoint 40 | *.ps binary svneol=unset#application/postscript 41 | *.psd binary svneol=unset#application/photoshop 42 | *.rdf binary svneol=unset#text/rdf 43 | *.rss text svneol=unset#text/xml 44 | *.rtf binary svneol=unset#text/rtf 45 | *.sln text svneol=native#text/plain 46 | *.swf binary svneol=unset#application/x-shockwave-flash 47 | *.tgz binary svneol=unset#application/gzip 48 | *.vcproj text svneol=native#text/xml 49 | *.vcxproj text svneol=native#text/xml 50 | *.vsprops text svneol=native#text/xml 51 | *.wav binary svneol=unset#audio/wav 52 | *.xls binary svneol=unset#application/vnd.ms-excel 53 | *.zip binary svneol=unset#application/zip 54 | 55 | # Text formats 56 | .htaccess text svneol=native#text/plain 57 | *.bbk text svneol=native#text/xml 58 | *.cmake text svneol=native#text/plain 59 | *.css text svneol=native#text/css 60 | *.dtd text svneol=native#text/xml 61 | *.htm text svneol=native#text/html 62 | *.html text svneol=native#text/html 63 | *.ini text svneol=native#text/plain 64 | *.log text svneol=native#text/plain 65 | *.mak text svneol=native#text/plain 66 | *.qbk text svneol=native#text/plain 67 | *.rst text svneol=native#text/plain 68 | *.sql text svneol=native#text/x-sql 69 | *.txt text svneol=native#text/plain 70 | *.xhtml text svneol=native#text/xhtml%2Bxml 71 | *.xml text svneol=native#text/xml 72 | *.xsd text svneol=native#text/xml 73 | *.xsl text svneol=native#text/xml 74 | *.xslt text svneol=native#text/xml 75 | *.xul text svneol=native#text/xul 76 | *.yml text svneol=native#text/plain 77 | boost-no-inspect text svneol=native#text/plain 78 | CHANGES text svneol=native#text/plain 79 | COPYING text svneol=native#text/plain 80 | INSTALL text svneol=native#text/plain 81 | Jamfile text svneol=native#text/plain 82 | Jamroot text svneol=native#text/plain 83 | Jamfile.v2 text svneol=native#text/plain 84 | Jamrules text svneol=native#text/plain 85 | Makefile* text svneol=native#text/plain 86 | README text svneol=native#text/plain 87 | TODO text svneol=native#text/plain 88 | 89 | # Code formats 90 | *.c text svneol=native#text/plain 91 | *.cpp text svneol=native#text/plain 92 | *.h text svneol=native#text/plain 93 | *.hpp text svneol=native#text/plain 94 | *.ipp text svneol=native#text/plain 95 | *.tpp text svneol=native#text/plain 96 | *.jam text svneol=native#text/plain 97 | *.java text svneol=native#text/plain 98 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ned14 2 | -------------------------------------------------------------------------------- /.github/workflows/unittests_linux.yml: -------------------------------------------------------------------------------- 1 | name: Unit tests Linux 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | - master 8 | pull_request: 9 | schedule: 10 | - cron: '0 0 1 * *' 11 | 12 | jobs: 13 | Linux: 14 | name: Ubuntu 22.04 15 | runs-on: ubuntu-22.04 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | compiler: [clang++, g++] 20 | standard: [11, 20] 21 | env: 22 | CXX: ${{ matrix.compiler }} 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: CMake build tests Linux 28 | shell: bash 29 | run: | 30 | mkdir build 31 | cd build 32 | cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_FLAGS="-fno-omit-frame-pointer -fsanitize=undefined" -DCMAKE_EXE_LINKER_FLAGS=-fsanitize=undefined -DCMAKE_CXX_STANDARD=${{ matrix.standard }} 33 | cmake --build . 34 | 35 | - name: CMake run tests Linux 36 | shell: bash 37 | run: | 38 | cd build 39 | bin/test-result 40 | bin/test-status-code 41 | bin/test-status-code-noexcept 42 | bin/test-status-code-not-posix 43 | bin/test-status-code-p0709a 44 | -------------------------------------------------------------------------------- /.github/workflows/unittests_macos.yml: -------------------------------------------------------------------------------- 1 | name: Unit tests Mac OS 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | - master 8 | pull_request: 9 | schedule: 10 | - cron: '0 0 1 * *' 11 | 12 | jobs: 13 | MacOS: 14 | name: Mac OS 15 | runs-on: macos-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | standard: [11, 20] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - name: CMake build tests Mac OS 25 | shell: bash 26 | run: | 27 | mkdir build 28 | cd build 29 | cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_FLAGS="-fno-omit-frame-pointer -fsanitize=undefined" -DCMAKE_EXE_LINKER_FLAGS=-fsanitize=undefined -DCMAKE_CXX_STANDARD=${{ matrix.standard }} 30 | cmake --build . 31 | 32 | - name: CMake run tests Mac OS 33 | shell: bash 34 | run: | 35 | cd build 36 | bin/test-result 37 | bin/test-status-code 38 | bin/test-status-code-noexcept 39 | bin/test-status-code-not-posix 40 | bin/test-status-code-p0709a 41 | -------------------------------------------------------------------------------- /.github/workflows/unittests_windows.yml: -------------------------------------------------------------------------------- 1 | name: Unit tests Windows 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | - master 8 | pull_request: 9 | schedule: 10 | - cron: '0 0 1 * *' 11 | 12 | jobs: 13 | WinVS2019: 14 | name: Windows VS2019 15 | runs-on: windows-2019 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: CMake build tests Windows 21 | shell: bash 22 | run: | 23 | mkdir build 24 | cd build 25 | cmake .. 26 | cmake --build . --config Release 27 | 28 | - name: CMake run tests Windows 29 | shell: bash 30 | run: | 31 | cd build 32 | bin/Release/test-result 33 | bin/Release/test-status-code 34 | bin/Release/test-status-code-noexcept 35 | bin/Release/test-status-code-not-posix 36 | bin/Release/test-status-code-p0709a 37 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "doc/html"] 2 | path = doc/html 3 | url = https://github.com/ned14/status-code.git 4 | branch = gh-pages 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10 FATAL_ERROR) 2 | 3 | project(status-code VERSION 1.0 LANGUAGES CXX) 4 | include(GNUInstallDirs) 5 | enable_testing() 6 | if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 7 | set(status-code_IS_DEPENDENCY OFF) 8 | else() 9 | set(status-code_IS_DEPENDENCY ON) 10 | endif() 11 | 12 | # On MSVC very annoyingly cmake puts /EHsc into the global flags which means you 13 | # get a warning when you try to disable exceptions. I hate to use this 14 | # globally imposed solution, but we are going to hack the global flags to use properties to 15 | # determine whether they are on or off 16 | # 17 | # Create custom properties called CXX_EXCEPTIONS and CXX_RTTI 18 | # These get placed at global, directory and target scopes 19 | foreach(scope GLOBAL DIRECTORY TARGET) 20 | define_property(${scope} PROPERTY "CXX_EXCEPTIONS" INHERITED 21 | BRIEF_DOCS "Enable C++ exceptions, defaults to ON at global scope" 22 | FULL_DOCS "Not choosing ON nor OFF with exact capitalisation will lead to misoperation!" 23 | ) 24 | define_property(${scope} PROPERTY "CXX_RTTI" INHERITED 25 | BRIEF_DOCS "Enable C++ runtime type information, defaults to ON at global scope" 26 | FULL_DOCS "Not choosing ON nor OFF with exact capitalisation will lead to misoperation!" 27 | ) 28 | endforeach() 29 | # Set the default for these properties at global scope. If they are not set per target or 30 | # whatever, the next highest scope will be looked up 31 | set_property(GLOBAL PROPERTY CXX_EXCEPTIONS ON) 32 | set_property(GLOBAL PROPERTY CXX_RTTI ON) 33 | if(MSVC) 34 | # Purge unconditional use of these flags and remove all the ignored 35 | # cruft which cmake adds for the LLVM-vs* toolset. 36 | set(purgelist 37 | "/EHsc" 38 | "/GR" 39 | "/Gm-" 40 | "-fms-extensions" 41 | "-fms-compatibility" 42 | #"-Wall" 43 | "-frtti" 44 | "-fexceptions" 45 | "-gline-tables-only" 46 | "-fno-inline" 47 | #"-O0" 48 | ) 49 | foreach(flag 50 | CMAKE_C_FLAGS CMAKE_CXX_FLAGS 51 | CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG 52 | CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE 53 | CMAKE_C_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_MINSIZEREL 54 | CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO 55 | ) 56 | foreach(item ${purgelist}) 57 | string(REPLACE "${item}" "" ${flag} "${${flag}}") 58 | endforeach() 59 | string(REPLACE "-O0" "/O0" ${flag} "${${flag}}") 60 | string(REPLACE "-O1" "/O1" ${flag} "${${flag}}") 61 | string(REPLACE "-O2" "/O2" ${flag} "${${flag}}") 62 | #message(STATUS "${flag} = ${${flag}}") 63 | endforeach() 64 | # Restore those same, but now selected by the properties 65 | add_compile_options( 66 | $<$,ON>:/EHsc> 67 | $<$,OFF>:/GR-> 68 | ) 69 | else() 70 | add_compile_options( 71 | $<$:$<$,ON>:-fexceptions>> 72 | $<$:$<$,ON>:-frtti>> 73 | $<$:$<$,OFF>:-fno-exceptions>> 74 | $<$:$<$,OFF>:-fno-rtti>> 75 | ) 76 | endif() 77 | 78 | add_library(status-code INTERFACE) 79 | target_compile_features(status-code INTERFACE cxx_std_11) 80 | target_include_directories(status-code INTERFACE 81 | "$" 82 | "$" 83 | ) 84 | foreach(source 85 | "include/status-code/detail/nt_code_to_generic_code.ipp" 86 | "include/status-code/detail/nt_code_to_win32_code.ipp" 87 | "include/status-code/detail/win32_code_to_generic_code.ipp" 88 | "include/status-code/boost_error_code.hpp" 89 | "include/status-code/com_code.hpp" 90 | "include/status-code/config.hpp" 91 | "include/status-code/error.hpp" 92 | "include/status-code/errored_status_code.hpp" 93 | "include/status-code/generic_code.hpp" 94 | "include/status-code/getaddrinfo_code.hpp" 95 | "include/status-code/http_status_code.hpp" 96 | "include/status-code/iostream_support.hpp" 97 | "include/status-code/nested_status_code.hpp" 98 | "include/status-code/nt_code.hpp" 99 | "include/status-code/posix_code.hpp" 100 | "include/status-code/quick_status_code_from_enum.hpp" 101 | "include/status-code/result.hpp" 102 | "include/status-code/status_code.hpp" 103 | "include/status-code/status_code_domain.hpp" 104 | "include/status-code/status_error.hpp" 105 | "include/status-code/std_error_code.hpp" 106 | "include/status-code/system_code.hpp" 107 | "include/status-code/system_code_from_exception.hpp" 108 | "include/status-code/system_error2.hpp" 109 | "include/status-code/win32_code.hpp" 110 | ) 111 | target_sources(status-code INTERFACE 112 | "$" 113 | ) 114 | get_filename_component(dir ${source} DIRECTORY) 115 | install(FILES "${source}" 116 | DESTINATION "${dir}" 117 | ) 118 | endforeach() 119 | 120 | install(TARGETS status-code 121 | EXPORT status-codeExports 122 | INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 123 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 124 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 125 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 126 | ) 127 | set_target_properties(status-code PROPERTIES EXPORT_NAME hl) 128 | 129 | configure_file( 130 | "${CMAKE_CURRENT_LIST_DIR}/cmake/ProjectConfig.cmake.in" 131 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 132 | @ONLY 133 | ) 134 | install(FILES 135 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 136 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 137 | ) 138 | install(EXPORT status-codeExports 139 | NAMESPACE status-code:: 140 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/status-code" 141 | ) 142 | 143 | if(NOT status-code_IS_DEPENDENCY AND (NOT DEFINED BUILD_TESTING OR BUILD_TESTING)) 144 | find_package(Python COMPONENTS Interpreter) 145 | # Make preprocessed edition of this library target 146 | if(NOT Python_Interpreter_FOUND) 147 | message(WARNING "NOT rebuilding preprocessed edition of library due to python not being installed") 148 | else() 149 | # See if the ply package is installed so pcpp can run 150 | execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "import pcpp" RESULT_VARIABLE python_has_pcpp) 151 | if(NOT python_has_pcpp EQUAL 0) 152 | message(WARNING "NOT rebuilding preprocessed edition of library due to installed python not having the pcpp package installed. " 153 | "Do '(sudo) pip install pcpp' to fix.") 154 | else() 155 | add_custom_target(status-code-pp 156 | pcpp -o "${CMAKE_CURRENT_SOURCE_DIR}/single-header/system_error2.hpp" 157 | "${CMAKE_CURRENT_SOURCE_DIR}/include/status-code/system_error2.hpp" 158 | "${CMAKE_CURRENT_SOURCE_DIR}/include/status-code/nested_status_code.hpp" 159 | --passthru-defines --passthru-unfound-includes --passthru-unknown-exprs 160 | --passthru-comments --line-directive --compress # --debug 161 | -U STANDARDESE_IS_IN_THE_HOUSE 162 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 163 | COMMENT "Preprocessing ${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME}.hpp into ${CMAKE_CURRENT_SOURCE_DIR}/single-header/${PROJECT_NAME}.hpp ..." 164 | ) 165 | add_custom_target(status-code-nowindows-pp 166 | pcpp -o "${CMAKE_CURRENT_SOURCE_DIR}/single-header/system_error2-nowindows.hpp" 167 | "${CMAKE_CURRENT_SOURCE_DIR}/include/status-code/system_error2.hpp" 168 | "${CMAKE_CURRENT_SOURCE_DIR}/include/status-code/nested_status_code.hpp" 169 | --passthru-defines --passthru-unfound-includes --passthru-unknown-exprs 170 | --passthru-comments --line-directive --compress # --debug 171 | -U STANDARDESE_IS_IN_THE_HOUSE -U _WIN32 172 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 173 | COMMENT "Preprocessing ${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME}.hpp into ${CMAKE_CURRENT_SOURCE_DIR}/single-header/${PROJECT_NAME}-nowindows.hpp ..." 174 | ) 175 | if(NOT CMAKE_VERSION VERSION_LESS 3.3) 176 | add_dependencies(status-code status-code-pp status-code-nowindows-pp) 177 | endif() 178 | endif() 179 | endif() 180 | 181 | if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "6.0") 182 | add_executable(test-result "test/result.cpp") 183 | target_compile_features(test-result PRIVATE cxx_std_17) 184 | target_link_libraries(test-result PRIVATE status-code) 185 | set_target_properties(test-result PROPERTIES 186 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 187 | ) 188 | add_test(NAME test-result COMMAND $) 189 | endif() 190 | 191 | # Forgot to git add this on the other computer 192 | # find_package(Boost COMPONENTS system) 193 | # if(Boost_FOUND) 194 | # add_executable(test-boost_error_code "test/boost_error_code.cpp") 195 | # target_link_libraries(test-boost_error_code PRIVATE status-code Boost::system) 196 | # set_target_properties(test-boost_error_code PROPERTIES 197 | # RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 198 | # ) 199 | # add_test(NAME test-boost_error_code COMMAND $) 200 | # endif() 201 | 202 | add_executable(test-issue0050 "test/issue0050.cpp") 203 | target_link_libraries(test-issue0050 PRIVATE status-code) 204 | set_target_properties(test-issue0050 PROPERTIES 205 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 206 | ) 207 | add_test(NAME test-issue0050 COMMAND $) 208 | 209 | add_executable(test-issue0056 "test/issue0056.cpp") 210 | target_compile_features(test-issue0056 PRIVATE cxx_std_17) 211 | target_link_libraries(test-issue0056 PRIVATE status-code) 212 | set_target_properties(test-issue0056 PROPERTIES 213 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 214 | ) 215 | add_test(NAME test-issue0056 COMMAND $) 216 | 217 | add_executable(test-status-code "test/main.cpp") 218 | target_link_libraries(test-status-code PRIVATE status-code) 219 | set_target_properties(test-status-code PROPERTIES 220 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 221 | ) 222 | add_test(NAME test-status-code COMMAND $) 223 | 224 | add_executable(test-status-code-noexcept "test/main.cpp") 225 | target_link_libraries(test-status-code-noexcept PRIVATE status-code) 226 | set_target_properties(test-status-code-noexcept PROPERTIES 227 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 228 | CXX_EXCEPTIONS Off 229 | CXX_RTTI Off 230 | ) 231 | add_test(NAME test-status-code-noexcept COMMAND $) 232 | 233 | add_executable(test-status-code-not-posix "test/main.cpp") 234 | target_compile_definitions(test-status-code-not-posix PRIVATE SYSTEM_ERROR2_NOT_POSIX=1 "SYSTEM_ERROR2_FATAL=::abort()") 235 | target_link_libraries(test-status-code-not-posix PRIVATE status-code) 236 | set_target_properties(test-status-code-not-posix PROPERTIES 237 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 238 | ) 239 | add_test(NAME test-status-code-not-posix COMMAND $) 240 | 241 | add_executable(test-status-code-p0709a "test/p0709a.cpp") 242 | target_link_libraries(test-status-code-p0709a PRIVATE status-code) 243 | set_target_properties(test-status-code-p0709a PROPERTIES 244 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 245 | ) 246 | add_test(NAME test-status-code-p0709a COMMAND $) 247 | 248 | if(WIN32) 249 | add_executable(generate-tables "utils/generate-tables.cpp") 250 | target_link_libraries(test-status-code PRIVATE status-code) 251 | set_target_properties(test-status-code PROPERTIES 252 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 253 | ) 254 | endif() 255 | 256 | # Compile examples 257 | add_executable(example-quick_status_code_from_enum "example/quick_status_code_from_enum.cpp") 258 | target_link_libraries(example-quick_status_code_from_enum PRIVATE status-code) 259 | set_target_properties(example-quick_status_code_from_enum PROPERTIES 260 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 261 | ) 262 | add_executable(example-thrown_exception "example/thrown_exception.cpp") 263 | target_link_libraries(example-thrown_exception PRIVATE status-code) 264 | set_target_properties(example-thrown_exception PROPERTIES 265 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 266 | ) 267 | target_compile_features(example-thrown_exception PRIVATE cxx_std_17) 268 | add_executable(example-file_io_error "wg21/file_io_error.cpp") 269 | target_link_libraries(example-file_io_error PRIVATE status-code) 270 | set_target_properties(example-file_io_error PROPERTIES 271 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 272 | ) 273 | 274 | endif() 275 | -------------------------------------------------------------------------------- /Licence.txt: -------------------------------------------------------------------------------- 1 | This software is licensed under your choice of the following two licences: 2 | 3 | --------------------------------- EITHER --------------------------------- 4 | 5 | Apache License 6 | 7 | Version 2.0, January 2004 8 | 9 | http://www.apache.org/licenses/ 10 | 11 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 12 | 13 | 1. Definitions. 14 | 15 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 16 | 17 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 18 | 19 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 20 | 21 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 22 | 23 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 24 | 25 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 26 | 27 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 28 | 29 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 30 | 31 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 32 | 33 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 34 | 35 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 36 | 37 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 38 | 39 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 40 | 41 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 42 | You must cause any modified files to carry prominent notices stating that You changed the files; and 43 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 44 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 45 | 46 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 47 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 48 | 49 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 50 | 51 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 52 | 53 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 54 | 55 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 56 | 57 | END OF TERMS AND CONDITIONS 58 | 59 | 60 | ----------------------------------- OR ----------------------------------- 61 | 62 | 63 | Boost Software License - Version 1.0 - August 17th, 2003 64 | 65 | Permission is hereby granted, free of charge, to any person or organization 66 | obtaining a copy of the software and accompanying documentation covered by 67 | this license (the "Software") to use, reproduce, display, distribute, 68 | execute, and transmit the Software, and to prepare derivative works of the 69 | Software, and to permit third-parties to whom the Software is furnished to 70 | do so, all subject to the following: 71 | 72 | The copyright notices in the Software and this entire statement, including 73 | the above license grant, this restriction and the following disclaimer, 74 | must be included in all copies of the Software, in whole or in part, and 75 | all derivative works of the Software, unless such copies or derivative 76 | works are solely in the form of machine-executable object code generated by 77 | a source language processor. 78 | 79 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 80 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 81 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 82 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 83 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 84 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 85 | DEALINGS IN THE SOFTWARE. 86 | -------------------------------------------------------------------------------- /cmake/ProjectConfig.cmake.in: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | set(${CMAKE_FIND_PACKAGE_NAME}_CONFIG ${CMAKE_CURRENT_LIST_FILE}) 3 | find_package_handle_standard_args(@PROJECT_NAME@ CONFIG_MODE) 4 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Exports.cmake") 5 | -------------------------------------------------------------------------------- /example/quick_status_code_from_enum.cpp: -------------------------------------------------------------------------------- 1 | /* Example use of status_code with outcome 2 | (C) 2020 Niall Douglas (5 commits) 3 | File Created: Sept 2020 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #include "status-code/system_error2.hpp" 26 | 27 | #include 28 | 29 | // This is some third party enumeration type in another namespace 30 | namespace another_namespace 31 | { 32 | // "Initialiser list" custom status code domain 33 | enum class AnotherCode : size_t 34 | { 35 | success1, 36 | goaway, 37 | success2, 38 | error2 39 | }; 40 | } // namespace another_namespace 41 | 42 | // To synthesise a custom status code domain for `AnotherCode`, inject the following 43 | // template specialisation: 44 | SYSTEM_ERROR2_NAMESPACE_BEGIN 45 | template <> struct quick_status_code_from_enum : quick_status_code_from_enum_defaults 46 | { 47 | // Text name of the enum 48 | static constexpr const auto domain_name = "Another Code"; 49 | 50 | // Unique UUID for the enum. PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h 51 | static constexpr const auto domain_uuid = "{be201f65-3962-dd0e-1266-a72e63776a42}"; 52 | 53 | // Map of each enum value to its text string, and list of semantically equivalent errc's 54 | static const std::initializer_list &value_mappings() 55 | { 56 | static const std::initializer_list v = { 57 | // Format is: { enum value, "string representation", { list of errc mappings ... } } 58 | {another_namespace::AnotherCode::success1, "Success 1", {errc::success}}, // 59 | {another_namespace::AnotherCode::goaway, "Go away", {errc::permission_denied}}, // 60 | {another_namespace::AnotherCode::success2, "Success 2", {errc::success}}, // 61 | {another_namespace::AnotherCode::error2, "Error 2", {errc::function_not_supported}}, // 62 | }; 63 | return v; 64 | } 65 | 66 | // Completely optional definition of mixin for the status code synthesised from `Enum`. 67 | // It can be omitted. 68 | template struct mixin : Base 69 | { 70 | using Base::Base; 71 | 72 | // A custom method on the synthesised status code 73 | constexpr int custom_method() const { return 42; } 74 | }; 75 | }; 76 | SYSTEM_ERROR2_NAMESPACE_END 77 | 78 | // If you wish easy manufacture of status codes from AnotherCode: 79 | namespace another_namespace 80 | { 81 | // ADL discovered, must be in same namespace as AnotherCode 82 | SYSTEM_ERROR2_CONSTEXPR14 inline SYSTEM_ERROR2_NAMESPACE::quick_status_code_from_enum_code status_code(AnotherCode c) { return SYSTEM_ERROR2_NAMESPACE::quick_status_code_from_enum_code(c); } 83 | } // namespace another_namespace 84 | 85 | 86 | int main(int argc, char *argv[]) 87 | { 88 | // Make a status code of the synthesised code domain for `AnotherCode`. Note the 89 | // unqualified lookup, ADL discovers the status_code() free function. 90 | SYSTEM_ERROR2_CONSTEXPR14 auto v = status_code(another_namespace::AnotherCode::error2); 91 | assert(v.value() == another_namespace::AnotherCode::error2); 92 | assert(v.custom_method() == 42); 93 | 94 | // If you don't need custom methods, just use system_code, all erased 95 | // status codes recognise quick_status_code_from_enum 96 | SYSTEM_ERROR2_NAMESPACE::system_code v2(another_namespace::AnotherCode::error2); 97 | assert(v2 == v); 98 | 99 | // If v.success() is true, this is a precondition failure which terminates 100 | // the program 101 | SYSTEM_ERROR2_NAMESPACE::error err = v; 102 | assert(v2 == err); 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /example/thrown_exception.cpp: -------------------------------------------------------------------------------- 1 | /* Example use of transporting a std::exception_ptr inside an error 2 | (C) 2018 Niall Douglas (5 commits) 3 | File Created: March 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #define _CRT_SECURE_NO_WARNINGS 26 | #include // for sprintf 27 | 28 | #include "status-code/system_error2.hpp" 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include // for std::move 35 | 36 | static constexpr size_t max_exception_ptrs = 16; 37 | 38 | 39 | using namespace SYSTEM_ERROR2_NAMESPACE; 40 | 41 | struct exception_ptr_storage_t 42 | { 43 | using index_type = unsigned int; 44 | 45 | mutable std::mutex lock; 46 | std::exception_ptr items[max_exception_ptrs]; 47 | index_type idx{0}; 48 | 49 | std::exception_ptr operator[](index_type i) const 50 | { 51 | std::lock_guard h(lock); 52 | return (idx - i < max_exception_ptrs) ? items[i % max_exception_ptrs] : std::exception_ptr(); 53 | } 54 | index_type add(std::exception_ptr p) 55 | { 56 | std::lock_guard h(lock); 57 | items[idx] = std::move(p); 58 | return idx++; 59 | } 60 | }; 61 | inline exception_ptr_storage_t exception_ptr_storage; 62 | 63 | // Alias our new status code to its domain 64 | class _thrown_exception_domain; 65 | using thrown_exception_code = status_code<_thrown_exception_domain>; 66 | 67 | class _thrown_exception_domain : public status_code_domain 68 | { 69 | // We permit status code to call our protected functions 70 | template friend class status_code; 71 | using _base = status_code_domain; 72 | 73 | public: 74 | // Our value type is the index into the exception_ptr storage 75 | using value_type = exception_ptr_storage_t::index_type; 76 | // std::exception::what() returns const char *, so the default string_ref is sufficient 77 | using _base::string_ref; 78 | 79 | // Always use https://www.random.org/cgi-bin/randbyte?nbytes=8&format=h to create a 80 | // unique 64 bit value for every unique domain you create! 81 | constexpr _thrown_exception_domain() noexcept 82 | : _base(0xb766b5e50597a655) 83 | { 84 | } 85 | 86 | // Default all the copy, move and destruct. This makes the type 100% constexpr in every way 87 | // which in turns allows the compiler to assume it will not be instantiated at runtime. 88 | _thrown_exception_domain(const _thrown_exception_domain &) = default; 89 | _thrown_exception_domain(_thrown_exception_domain &&) = default; 90 | _thrown_exception_domain &operator=(const _thrown_exception_domain &) = default; 91 | _thrown_exception_domain &operator=(_thrown_exception_domain &&) = default; 92 | ~_thrown_exception_domain() = default; 93 | 94 | // Fetch a constexpr instance of this domain 95 | static inline constexpr const _thrown_exception_domain &get(); 96 | 97 | // Return the name of this domain 98 | virtual _base::string_ref name() const noexcept override final { return _base::string_ref("thrown exception"); } 99 | 100 | // Return information about the value type of this domain 101 | virtual payload_info_t payload_info() const noexcept override 102 | { 103 | return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), 104 | (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; 105 | } 106 | 107 | protected: 108 | // This internal routine maps an exception ptr onto a generic_code 109 | // It is surely hideously slow, but that's all relative in the end 110 | static errc _to_generic_code(value_type c) noexcept 111 | { 112 | const std::exception_ptr &e = exception_ptr_storage[c]; 113 | try 114 | { 115 | if(!e) 116 | return errc::unknown; 117 | std::rethrow_exception(e); 118 | } 119 | catch(const std::invalid_argument & /*unused*/) 120 | { 121 | return errc::invalid_argument; 122 | } 123 | catch(const std::domain_error & /*unused*/) 124 | { 125 | return errc::argument_out_of_domain; 126 | } 127 | catch(const std::length_error & /*unused*/) 128 | { 129 | return errc::argument_list_too_long; 130 | } 131 | catch(const std::out_of_range & /*unused*/) 132 | { 133 | return errc::result_out_of_range; 134 | } 135 | catch(const std::logic_error & /*unused*/) /* base class for this group */ 136 | { 137 | return errc::invalid_argument; 138 | } 139 | catch(const std::system_error &e) /* also catches ios::failure */ 140 | { 141 | return static_cast(e.code().value()); 142 | } 143 | catch(const std::overflow_error & /*unused*/) 144 | { 145 | return errc::value_too_large; 146 | } 147 | catch(const std::range_error & /*unused*/) 148 | { 149 | return errc::result_out_of_range; 150 | } 151 | catch(const std::runtime_error & /*unused*/) /* base class for this group */ 152 | { 153 | return errc::resource_unavailable_try_again; 154 | } 155 | catch(const std::bad_alloc & /*unused*/) 156 | { 157 | return errc::not_enough_memory; 158 | } 159 | catch(...) 160 | { 161 | } 162 | return errc::unknown; 163 | } 164 | 165 | // Always true, as exception_ptr always represents failure 166 | virtual bool _do_failure(const status_code &code) const noexcept override final 167 | { 168 | assert(code.domain() == *this); 169 | return true; 170 | } 171 | // True if the exception ptr is equivalent to some other status code 172 | virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override final 173 | { 174 | assert(code1.domain() == *this); 175 | const auto &c1 = static_cast(code1); 176 | if(code2.domain() == *this) 177 | { 178 | const auto &c2 = static_cast(code2); 179 | // Always perform literal comparison when domains are equal. The fallback 180 | // semantic comparison of converting both to generic_code and comparing 181 | // will handle semantic comparison of the same domain. 182 | return c1.value() == c2.value(); 183 | } 184 | // If anything in your coding matches anything in errc, you should match it here 185 | if(code2.domain() == generic_code_domain) 186 | { 187 | const auto &c2 = static_cast(code2); 188 | if(c2.value() == _to_generic_code(c1.value())) 189 | { 190 | return true; 191 | } 192 | } 193 | return false; 194 | } 195 | // Called as a fallback if _equivalent() fails 196 | virtual generic_code _generic_code(const status_code &code) const noexcept override final 197 | { 198 | assert(code.domain() == *this); 199 | const auto &c1 = static_cast(code); 200 | return generic_code(_to_generic_code(c1.value())); 201 | } 202 | // Extract the what() from the exception 203 | virtual _base::string_ref _do_message(const status_code &code) const noexcept override final 204 | { 205 | assert(code.domain() == *this); 206 | const auto &c = static_cast(code); 207 | const std::exception_ptr &e = exception_ptr_storage[c.value()]; 208 | try 209 | { 210 | if(!e) 211 | return _base::string_ref("expired"); 212 | std::rethrow_exception(e); 213 | } 214 | catch(const std::exception &x) 215 | { 216 | /* MSVC throws a full copy here, because its rethrow_exception() 217 | makes copies instead of using the one stored in the array. So 218 | for it we actively must copy the message. */ 219 | #ifdef _WIN32 220 | auto *msg = x.what(); 221 | auto len = strlen(msg); 222 | auto *p = static_cast(malloc(len + 1)); 223 | if(p == nullptr) 224 | { 225 | return _base::string_ref("failed to allocate memory for what()"); 226 | } 227 | memcpy(p, msg, len + 1); 228 | return _base::atomic_refcounted_string_ref(p, len); 229 | #else 230 | return _base::string_ref(x.what()); 231 | #endif 232 | } 233 | catch(...) 234 | { 235 | return _base::string_ref("unknown thrown exception"); 236 | } 237 | } 238 | // Throw the code as a C++ exception 239 | virtual void _do_throw_exception(const status_code &code) const override final 240 | { 241 | assert(code.domain() == *this); 242 | const auto &c = static_cast(code); 243 | const std::exception_ptr &e = exception_ptr_storage[c.value()]; 244 | std::rethrow_exception(e); 245 | } 246 | }; 247 | 248 | //! A constexpr source variable for the throw exception code domain to return via get() 249 | constexpr inline _thrown_exception_domain thrown_exception_domain; 250 | inline constexpr const _thrown_exception_domain &_thrown_exception_domain::get() 251 | { 252 | return thrown_exception_domain; 253 | } 254 | 255 | // Helper to construct a thrown_exception_code from a std::exception_ptr 256 | inline thrown_exception_code make_status_code(std::exception_ptr ep) 257 | { 258 | return thrown_exception_code(in_place, exception_ptr_storage.add(std::move(ep))); 259 | } 260 | 261 | #ifndef DONT_DEFINE_MAIN 262 | int main() 263 | { 264 | thrown_exception_code tec(make_status_code(std::make_exception_ptr(std::bad_alloc()))); 265 | system_code sc(tec); 266 | printf("Thrown exception code has message %s\n", sc.message().c_str()); 267 | printf("Thrown exception code == errc::not_enough_memory = %d\n", sc == errc::not_enough_memory); 268 | return 0; 269 | } 270 | #endif 271 | -------------------------------------------------------------------------------- /example/variant_return.cpp: -------------------------------------------------------------------------------- 1 | /* Example use of status_code with outcome 2 | (C) 2020 Niall Douglas (5 commits) 3 | File Created: Sept 2020 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | /* There are many T or E variant return solutions available in the C++ 26 | ecosystem e.g. expected, LEAF, Outcome. These allow the return 27 | of a T, for success, or an E, for failure, from a function. This enables 28 | a reasonable simulacrum of any future deterministic exceptions which 29 | may get added to standard C++. 30 | 31 | This example uses Experimental.Outcome which bundles an internal 32 | copy of the system_error2 library. Outcome ships in a standalone 33 | edition, and in Boost since v1.70. Both work well with C++ exceptions 34 | globally disabled (though other bugs in Boost outside Outcome may 35 | frustrate you if you use the Boost edition). 36 | 37 | You can retrieve a single file integration of standalone Outcome 38 | bundled with this library from: 39 | 40 | https://raw.githubusercontent.com/ned14/outcome/develop/single-header/outcome-experimental.hpp 41 | 42 | You may also wish to study the Outcome tutorial at: 43 | 44 | https://ned14.github.io/outcome/tutorial/ 45 | */ 46 | #include "outcome-experimental.hpp" 47 | 48 | #include 49 | #include 50 | 51 | // Outcome's namespace permutes to avoid ABI collisions due to the unstable ABI. 52 | namespace outcome_e = OUTCOME_V2_NAMESPACE::experimental; 53 | 54 | /* Bind a local result type from Outcome.Experimental's status_result<>. 55 | status_result<> within Outcome is a specialised alias of outcome::basic_result<> 56 | for E types which are system_error2's system_code. As a general rule, you don't 57 | need to think much past just binding outcome_e::status_result into your local 58 | namespace as your local result type. 59 | */ 60 | template using result = outcome_e::status_result; 61 | 62 | result moveMountain(bool input) 63 | { 64 | if (input == true) 65 | { 66 | // Outcome's result will auto construct success from anything convertible to T (std::string) 67 | // If it's ambiguous which, use outcome_e::success(T) to disambiguate. 68 | return "worked"; 69 | } 70 | else 71 | { 72 | // Types whitelisted as convertible to E cause auto construction of failure. 73 | // If it's ambiguous which, use outcome_e::failure(E) to disambiguate. 74 | return outcome_e::errc::no_such_file_or_directory; 75 | }; 76 | } 77 | 78 | int main(int argc, char *argv[]) 79 | { 80 | if(auto r = moveMountain(argc > 1)) 81 | { 82 | std::cout << r.value() << std::endl;; 83 | } 84 | else 85 | { 86 | // We need to use .c_str() because Outcome's pregenerated "outcome-experimental.hpp" 87 | // does not #include iostream nor iosfwd. If you want iostream integration, 88 | // #include "iostream_support.hpp" in this library. 89 | std::cerr << "error happened " << r.error().message().c_str() << std::endl; 90 | } 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /include/status-code/boost_error_code.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 - 2022 Niall Douglas (5 commits) 3 | File Created: Mar 2022 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_BOOST_ERROR_CODE_HPP 26 | #define SYSTEM_ERROR2_BOOST_ERROR_CODE_HPP 27 | 28 | #ifndef SYSTEM_ERROR2_NOT_POSIX 29 | #include "posix_code.hpp" 30 | #endif 31 | 32 | #if defined(_WIN32) || defined(STANDARDESE_IS_IN_THE_HOUSE) 33 | #include "win32_code.hpp" 34 | #endif 35 | 36 | #include "std_error_code.hpp" 37 | 38 | #include 39 | #include 40 | 41 | #include 42 | 43 | SYSTEM_ERROR2_NAMESPACE_BEGIN 44 | 45 | class _boost_error_code_domain; 46 | //! A `status_code` representing exactly a `boost::system::error_code` 47 | using boost_error_code = status_code<_boost_error_code_domain>; 48 | 49 | namespace mixins 50 | { 51 | template struct mixin : public Base 52 | { 53 | using Base::Base; 54 | 55 | //! Implicit constructor from a `boost::system::error_code` 56 | inline mixin(boost::system::error_code ec); 57 | 58 | //! Returns the error code category 59 | inline const boost::system::error_category &category() const noexcept; 60 | }; 61 | } // namespace mixins 62 | 63 | 64 | /*! The implementation of the domain for `boost::system::error_code` error codes. 65 | */ 66 | class _boost_error_code_domain final : public status_code_domain 67 | { 68 | template friend class status_code; 69 | using _base = status_code_domain; 70 | using _error_code_type = boost::system::error_code; 71 | using _error_category_type = boost::system::error_category; 72 | 73 | std::string _name; 74 | 75 | static _base::string_ref _make_string_ref(_error_code_type c) noexcept 76 | { 77 | #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) 78 | try 79 | #endif 80 | { 81 | std::string msg = c.message(); 82 | auto *p = static_cast(malloc(msg.size() + 1)); // NOLINT 83 | if(p == nullptr) 84 | { 85 | return _base::string_ref("failed to allocate message"); 86 | } 87 | memcpy(p, msg.c_str(), msg.size() + 1); 88 | return _base::atomic_refcounted_string_ref(p, msg.size()); 89 | } 90 | #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) 91 | catch(...) 92 | { 93 | return _base::string_ref("failed to allocate message"); 94 | } 95 | #endif 96 | } 97 | 98 | public: 99 | //! The value type of the `boost::system::error_code` code, which stores the `int` from the `boost::system::error_code` 100 | using value_type = int; 101 | using _base::string_ref; 102 | 103 | //! Returns the error category singleton pointer this status code domain represents 104 | const _error_category_type &error_category() const noexcept 105 | { 106 | auto ptr = 0x0ea88ff382d94915 ^ this->id(); 107 | return *reinterpret_cast(ptr); 108 | } 109 | 110 | //! Default constructor 111 | explicit _boost_error_code_domain(const _error_category_type &category) noexcept 112 | : _base(0x0ea88ff382d94915 ^ reinterpret_cast<_base::unique_id_type>(&category)) 113 | , _name("boost_error_code_domain(") 114 | { 115 | _name.append(category.name()); 116 | _name.push_back(')'); 117 | } 118 | _boost_error_code_domain(const _boost_error_code_domain &) = default; 119 | _boost_error_code_domain(_boost_error_code_domain &&) = default; 120 | _boost_error_code_domain &operator=(const _boost_error_code_domain &) = default; 121 | _boost_error_code_domain &operator=(_boost_error_code_domain &&) = default; 122 | ~_boost_error_code_domain() = default; 123 | 124 | static inline const _boost_error_code_domain *get(_error_code_type ec); 125 | 126 | virtual string_ref name() const noexcept override { return string_ref(_name.c_str(), _name.size()); } // NOLINT 127 | 128 | virtual payload_info_t payload_info() const noexcept override 129 | { 130 | return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), 131 | (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; 132 | } 133 | 134 | protected: 135 | virtual bool _do_failure(const status_code &code) const noexcept override; 136 | virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override; 137 | virtual generic_code _generic_code(const status_code &code) const noexcept override; 138 | virtual string_ref _do_message(const status_code &code) const noexcept override; 139 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 140 | SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override; 141 | #endif 142 | }; 143 | 144 | namespace detail 145 | { 146 | extern inline _boost_error_code_domain *boost_error_code_domain_from_category(const boost::system::error_category &category) 147 | { 148 | static constexpr size_t max_items = 64; 149 | static struct storage_t 150 | { 151 | std::atomic _lock; 152 | union item_t 153 | { 154 | int _init; 155 | _boost_error_code_domain domain; 156 | constexpr item_t() 157 | : _init(0) 158 | { 159 | } 160 | ~item_t() {} 161 | } items[max_items]; 162 | size_t count{0}; 163 | 164 | void lock() 165 | { 166 | while(_lock.exchange(1, std::memory_order_acquire) != 0) 167 | ; 168 | } 169 | void unlock() { _lock.store(0, std::memory_order_release); } 170 | 171 | storage_t() {} 172 | ~storage_t() 173 | { 174 | lock(); 175 | for(size_t n = 0; n < count; n++) 176 | { 177 | items[n].domain.~_boost_error_code_domain(); 178 | } 179 | unlock(); 180 | } 181 | _boost_error_code_domain *add(const boost::system::error_category &category) 182 | { 183 | _boost_error_code_domain *ret = nullptr; 184 | lock(); 185 | for(size_t n = 0; n < count; n++) 186 | { 187 | if(items[n].domain.error_category() == category) 188 | { 189 | ret = &items[n].domain; 190 | break; 191 | } 192 | } 193 | if(ret == nullptr && count < max_items) 194 | { 195 | ret = new(SYSTEM_ERROR2_ADDRESS_OF(items[count++].domain)) _boost_error_code_domain(category); 196 | } 197 | unlock(); 198 | return ret; 199 | } 200 | } storage; 201 | return storage.add(category); 202 | } 203 | } // namespace detail 204 | 205 | namespace mixins 206 | { 207 | template 208 | inline mixin::mixin(boost::system::error_code ec) 209 | : Base(typename Base::_value_type_constructor{}, _boost_error_code_domain::get(ec), ec.value()) 210 | { 211 | } 212 | 213 | template inline const boost::system::error_category &mixin::category() const noexcept 214 | { 215 | const auto &domain = static_cast(this->domain()); 216 | return domain.error_category(); 217 | }; 218 | } // namespace mixins 219 | 220 | inline const _boost_error_code_domain *_boost_error_code_domain::get(boost::system::error_code ec) 221 | { 222 | auto *p = detail::boost_error_code_domain_from_category(ec.category()); 223 | assert(p != nullptr); 224 | if(p == nullptr) 225 | { 226 | abort(); 227 | } 228 | return p; 229 | } 230 | 231 | 232 | inline bool _boost_error_code_domain::_do_failure(const status_code &code) const noexcept 233 | { 234 | assert(code.domain() == *this); 235 | return static_cast(code).value() != 0; // NOLINT 236 | } 237 | 238 | inline bool _boost_error_code_domain::_do_equivalent(const status_code &code1, const status_code &code2) const noexcept 239 | { 240 | assert(code1.domain() == *this); 241 | const auto &c1 = static_cast(code1); // NOLINT 242 | const auto &cat1 = c1.category(); 243 | // Are we comparing to another wrapped error_code? 244 | if(code2.domain() == *this) 245 | { 246 | const auto &c2 = static_cast(code2); // NOLINT 247 | const auto &cat2 = c2.category(); 248 | // If the error code categories are identical, do literal comparison 249 | if(cat1 == cat2) 250 | { 251 | return c1.value() == c2.value(); 252 | } 253 | // Otherwise fall back onto the _generic_code comparison, which uses default_error_condition() 254 | return false; 255 | } 256 | // Am I an error code with generic category? 257 | if(cat1 == boost::system::generic_category()) 258 | { 259 | // Convert to generic code, and compare that 260 | generic_code _c1(static_cast(c1.value())); 261 | return _c1 == code2; 262 | } 263 | // Am I an error code with system category? 264 | if(cat1 == boost::system::system_category()) 265 | { 266 | // Convert to POSIX or Win32 code, and compare that 267 | #ifdef _WIN32 268 | win32_code _c1((win32::DWORD) c1.value()); 269 | return _c1 == code2; 270 | #elif !defined(SYSTEM_ERROR2_NOT_POSIX) 271 | posix_code _c1(c1.value()); 272 | return _c1 == code2; 273 | #endif 274 | } 275 | return false; 276 | } 277 | 278 | inline generic_code _boost_error_code_domain::_generic_code(const status_code &code) const noexcept 279 | { 280 | assert(code.domain() == *this); 281 | const auto &c = static_cast(code); // NOLINT 282 | // Ask my embedded error code for its mapping to boost::system::errc, which is a subset of our generic_code errc. 283 | boost::system::error_condition cond(c.category().default_error_condition(c.value())); 284 | if(cond.category() == boost::system::generic_category()) 285 | { 286 | return generic_code(static_cast(cond.value())); 287 | } 288 | #if !defined(SYSTEM_ERROR2_NOT_POSIX) && !defined(_WIN32) 289 | if(cond.category() == boost::system::system_category()) 290 | { 291 | return generic_code(static_cast(cond.value())); 292 | } 293 | #endif 294 | return errc::unknown; 295 | } 296 | 297 | inline _boost_error_code_domain::string_ref _boost_error_code_domain::_do_message(const status_code &code) const noexcept 298 | { 299 | assert(code.domain() == *this); 300 | const auto &c = static_cast(code); // NOLINT 301 | return _make_string_ref(_error_code_type(c.value(), c.category())); 302 | } 303 | 304 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 305 | SYSTEM_ERROR2_NORETURN inline void _boost_error_code_domain::_do_throw_exception(const status_code &code) const 306 | { 307 | assert(code.domain() == *this); 308 | const auto &c = static_cast(code); // NOLINT 309 | throw boost::system::system_error(boost::system::error_code(c.value(), c.category())); 310 | } 311 | #endif 312 | 313 | static_assert(sizeof(boost_error_code) <= sizeof(void *) * 2, "boost_error_code does not fit into a system_code!"); 314 | 315 | SYSTEM_ERROR2_NAMESPACE_END 316 | 317 | // Enable implicit construction of `boost_error_code` from `boost::system::error_code`. 318 | namespace boost 319 | { 320 | namespace system 321 | { 322 | inline SYSTEM_ERROR2_NAMESPACE::erased_status_code make_status_code(error_code c) noexcept 323 | { 324 | if(c.category() == detail::interop_category()) 325 | { 326 | // This is actually a wrap of std::error_code. If this fails to compile, your Boost is too old. 327 | return std::make_status_code(std::error_code(c)); 328 | } 329 | return SYSTEM_ERROR2_NAMESPACE::boost_error_code(c); 330 | } 331 | } // namespace system 332 | } // namespace boost 333 | 334 | #endif 335 | -------------------------------------------------------------------------------- /include/status-code/com_code.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 Niall Douglas (5 commits) 3 | File Created: Feb 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_COM_CODE_HPP 26 | #define SYSTEM_ERROR2_COM_CODE_HPP 27 | 28 | #if !defined(_WIN32) && !defined(STANDARDESE_IS_IN_THE_HOUSE) 29 | #error This file should only be included on Windows 30 | #endif 31 | 32 | #include "nt_code.hpp" 33 | #include "win32_code.hpp" 34 | 35 | #ifndef STANDARDESE_IS_IN_THE_HOUSE 36 | #include // needed by mingw for comdef.h to work 37 | 38 | #include 39 | #endif 40 | 41 | SYSTEM_ERROR2_NAMESPACE_BEGIN 42 | 43 | class _com_code_domain; 44 | /*! (Windows only) A COM error code. Note semantic equivalence testing is only implemented for `FACILITY_WIN32` 45 | and `FACILITY_NT_BIT`. As you can see at 46 | [https://blogs.msdn.microsoft.com/eldar/2007/04/03/a-lot-of-hresult-codes/](https://blogs.msdn.microsoft.com/eldar/2007/04/03/a-lot-of-hresult-codes/), there 47 | are an awful lot of COM error codes, and keeping mapping tables for all of them would be impractical (for the Win32 and NT facilities, we actually reuse the 48 | mapping tables in `win32_code` and `nt_code`). You can, of course, inherit your own COM code domain from this one and override the `_do_equivalent()` function 49 | to add semantic equivalence testing for whichever extra COM codes that your application specifically needs. 50 | */ 51 | using com_code = status_code<_com_code_domain>; 52 | //! (Windows only) A specialisation of `status_error` for the COM error code domain. 53 | using com_error = status_error<_com_code_domain>; 54 | 55 | /*! (Windows only) The implementation of the domain for COM error codes and/or `IErrorInfo`. 56 | */ 57 | class _com_code_domain : public status_code_domain 58 | { 59 | template friend class status_code; 60 | using _base = status_code_domain; 61 | 62 | //! Construct from a `HRESULT` error code 63 | #ifdef _COMDEF_NOT_WINAPI_FAMILY_DESKTOP_APP 64 | static _base::string_ref _make_string_ref(HRESULT c, wchar_t *perrinfo = nullptr) noexcept 65 | #else 66 | static _base::string_ref _make_string_ref(HRESULT c, IErrorInfo *perrinfo = nullptr) noexcept 67 | #endif 68 | { 69 | _com_error ce(c, perrinfo); 70 | #ifdef _UNICODE 71 | win32::DWORD wlen = (win32::DWORD) wcslen(ce.ErrorMessage()); 72 | size_t allocation = wlen + (wlen >> 1); 73 | win32::DWORD bytes; 74 | if(wlen == 0) 75 | { 76 | return _base::string_ref("failed to get message from system"); 77 | } 78 | for(;;) 79 | { 80 | auto *p = static_cast(malloc(allocation)); // NOLINT 81 | if(p == nullptr) 82 | { 83 | return _base::string_ref("failed to get message from system"); 84 | } 85 | bytes = win32::WideCharToMultiByte(65001 /*CP_UTF8*/, 0, ce.ErrorMessage(), (int) (wlen + 1), p, (int) allocation, nullptr, nullptr); 86 | if(bytes != 0) 87 | { 88 | char *end = strchr(p, 0); 89 | while(end[-1] == 10 || end[-1] == 13) 90 | { 91 | --end; 92 | } 93 | *end = 0; // NOLINT 94 | return _base::atomic_refcounted_string_ref(p, end - p); 95 | } 96 | free(p); // NOLINT 97 | if(win32::GetLastError() == 0x7a /*ERROR_INSUFFICIENT_BUFFER*/) 98 | { 99 | allocation += allocation >> 2; 100 | continue; 101 | } 102 | return _base::string_ref("failed to get message from system"); 103 | } 104 | #else 105 | auto wlen = static_cast(strlen(ce.ErrorMessage())); 106 | auto *p = static_cast(malloc(wlen + 1)); // NOLINT 107 | if(p == nullptr) 108 | { 109 | return _base::string_ref("failed to get message from system"); 110 | } 111 | memcpy(p, ce.ErrorMessage(), wlen + 1); 112 | char *end = strchr(p, 0); 113 | while(end[-1] == 10 || end[-1] == 13) 114 | { 115 | --end; 116 | } 117 | *end = 0; // NOLINT 118 | return _base::atomic_refcounted_string_ref(p, end - p); 119 | #endif 120 | } 121 | 122 | public: 123 | //! The value type of the COM code, which is a `HRESULT` 124 | using value_type = HRESULT; 125 | using _base::string_ref; 126 | 127 | public: 128 | //! Default constructor 129 | constexpr explicit _com_code_domain(typename _base::unique_id_type id = 0xdc8275428b4effac) noexcept 130 | : _base(id) 131 | { 132 | } 133 | _com_code_domain(const _com_code_domain &) = default; 134 | _com_code_domain(_com_code_domain &&) = default; 135 | _com_code_domain &operator=(const _com_code_domain &) = default; 136 | _com_code_domain &operator=(_com_code_domain &&) = default; 137 | ~_com_code_domain() = default; 138 | 139 | //! Constexpr singleton getter. Returns the constexpr com_code_domain variable. 140 | static inline constexpr const _com_code_domain &get(); 141 | 142 | virtual string_ref name() const noexcept override { return string_ref("COM domain"); } // NOLINT 143 | 144 | virtual payload_info_t payload_info() const noexcept override 145 | { 146 | return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), 147 | (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; 148 | } 149 | 150 | protected: 151 | virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT 152 | { 153 | assert(code.domain() == *this); 154 | return static_cast(code).value() < 0; // NOLINT 155 | } 156 | /*! Note semantic equivalence testing is only implemented for `FACILITY_WIN32` and `FACILITY_NT_BIT`. 157 | */ 158 | virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override // NOLINT 159 | { 160 | assert(code1.domain() == *this); 161 | const auto &c1 = static_cast(code1); // NOLINT 162 | if(code2.domain() == *this) 163 | { 164 | const auto &c2 = static_cast(code2); // NOLINT 165 | return c1.value() == c2.value(); 166 | } 167 | if((c1.value() & FACILITY_NT_BIT) != 0) 168 | { 169 | if(code2.domain() == nt_code_domain) 170 | { 171 | const auto &c2 = static_cast(code2); // NOLINT 172 | if(c2.value() == (c1.value() & ~FACILITY_NT_BIT)) 173 | { 174 | return true; 175 | } 176 | } 177 | else if(code2.domain() == generic_code_domain) 178 | { 179 | const auto &c2 = static_cast(code2); // NOLINT 180 | if(static_cast(c2.value()) == _nt_code_domain::_nt_code_to_errno(c1.value() & ~FACILITY_NT_BIT)) 181 | { 182 | return true; 183 | } 184 | } 185 | } 186 | else if(HRESULT_FACILITY(c1.value()) == FACILITY_WIN32) 187 | { 188 | if(code2.domain() == win32_code_domain) 189 | { 190 | const auto &c2 = static_cast(code2); // NOLINT 191 | if(c2.value() == HRESULT_CODE(c1.value())) 192 | { 193 | return true; 194 | } 195 | } 196 | else if(code2.domain() == generic_code_domain) 197 | { 198 | const auto &c2 = static_cast(code2); // NOLINT 199 | if(static_cast(c2.value()) == _win32_code_domain::_win32_code_to_errno(HRESULT_CODE(c1.value()))) 200 | { 201 | return true; 202 | } 203 | } 204 | } 205 | return false; 206 | } 207 | virtual generic_code _generic_code(const status_code &code) const noexcept override // NOLINT 208 | { 209 | assert(code.domain() == *this); 210 | const auto &c1 = static_cast(code); // NOLINT 211 | if(c1.value() == S_OK) 212 | { 213 | return generic_code(errc::success); 214 | } 215 | if((c1.value() & FACILITY_NT_BIT) != 0) 216 | { 217 | return generic_code(static_cast(_nt_code_domain::_nt_code_to_errno(c1.value() & ~FACILITY_NT_BIT))); 218 | } 219 | if(HRESULT_FACILITY(c1.value()) == FACILITY_WIN32) 220 | { 221 | return generic_code(static_cast(_win32_code_domain::_win32_code_to_errno(HRESULT_CODE(c1.value())))); 222 | } 223 | return generic_code(errc::unknown); 224 | } 225 | virtual string_ref _do_message(const status_code &code) const noexcept override // NOLINT 226 | { 227 | assert(code.domain() == *this); 228 | const auto &c = static_cast(code); // NOLINT 229 | return _make_string_ref(c.value()); 230 | } 231 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 232 | SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT 233 | { 234 | assert(code.domain() == *this); 235 | const auto &c = static_cast(code); // NOLINT 236 | throw status_error<_com_code_domain>(c); 237 | } 238 | #endif 239 | }; 240 | //! (Windows only) A constexpr source variable for the COM code domain. Returned by `_com_code_domain::get()`. 241 | constexpr _com_code_domain com_code_domain; 242 | inline constexpr const _com_code_domain &_com_code_domain::get() 243 | { 244 | return com_code_domain; 245 | } 246 | 247 | SYSTEM_ERROR2_NAMESPACE_END 248 | 249 | #endif 250 | -------------------------------------------------------------------------------- /include/status-code/detail/nt_code_to_generic_code.ipp: -------------------------------------------------------------------------------- 1 | case 0x80000002: return EACCES; 2 | case 0x8000000f: return EAGAIN; 3 | case 0x80000010: return EAGAIN; 4 | case 0x80000011: return EBUSY; 5 | case 0xc0000002: return ENOSYS; 6 | case 0xc0000005: return EACCES; 7 | case 0xc0000008: return EINVAL; 8 | case 0xc000000e: return ENOENT; 9 | case 0xc000000f: return ENOENT; 10 | case 0xc0000010: return ENOSYS; 11 | case 0xc0000013: return EAGAIN; 12 | case 0xc0000017: return ENOMEM; 13 | case 0xc000001c: return ENOSYS; 14 | case 0xc000001e: return EACCES; 15 | case 0xc000001f: return EACCES; 16 | case 0xc0000021: return EACCES; 17 | case 0xc0000022: return EACCES; 18 | case 0xc0000024: return EINVAL; 19 | case 0xc0000033: return EINVAL; 20 | case 0xc0000034: return ENOENT; 21 | case 0xc0000035: return EEXIST; 22 | case 0xc0000037: return EINVAL; 23 | case 0xc000003a: return ENOENT; 24 | case 0xc0000040: return ENOMEM; 25 | case 0xc0000041: return EACCES; 26 | case 0xc0000042: return EINVAL; 27 | case 0xc0000043: return EACCES; 28 | case 0xc000004b: return EACCES; 29 | case 0xc0000054: return ENOLCK; 30 | case 0xc0000055: return ENOLCK; 31 | case 0xc0000056: return EACCES; 32 | case 0xc000007f: return ENOSPC; 33 | case 0xc0000087: return ENOMEM; 34 | case 0xc0000097: return ENOMEM; 35 | case 0xc000009b: return ENOENT; 36 | case 0xc000009e: return EAGAIN; 37 | case 0xc00000a2: return EACCES; 38 | case 0xc00000a3: return EAGAIN; 39 | case 0xc00000af: return ENOSYS; 40 | case 0xc00000ba: return EACCES; 41 | case 0xc00000c0: return ENODEV; 42 | case 0xc00000d4: return EXDEV; 43 | case 0xc00000d5: return EACCES; 44 | case 0xc00000fb: return ENOENT; 45 | case 0xc0000101: return ENOTEMPTY; 46 | case 0xc0000103: return EINVAL; 47 | case 0xc0000107: return EBUSY; 48 | case 0xc0000108: return EBUSY; 49 | case 0xc000010a: return EACCES; 50 | case 0xc000011f: return EMFILE; 51 | case 0xc0000120: return ECANCELED; 52 | case 0xc0000121: return EACCES; 53 | case 0xc0000123: return EACCES; 54 | case 0xc0000128: return EINVAL; 55 | case 0xc0000189: return EACCES; 56 | case 0xc00001ad: return ENOMEM; 57 | case 0xc000022d: return EAGAIN; 58 | case 0xc0000235: return EINVAL; 59 | case 0xc000026e: return EAGAIN; 60 | case 0xc000028a: return EACCES; 61 | case 0xc000028b: return EACCES; 62 | case 0xc000028d: return EACCES; 63 | case 0xc000028e: return EACCES; 64 | case 0xc000028f: return EACCES; 65 | case 0xc0000290: return EACCES; 66 | case 0xc000029c: return ENOSYS; 67 | case 0xc00002c5: return EACCES; 68 | case 0xc00002d3: return EAGAIN; 69 | case 0xc00002ea: return EACCES; 70 | case 0xc00002f0: return ENOENT; 71 | case 0xc0000373: return ENOMEM; 72 | case 0xc0000416: return ENOMEM; 73 | case 0xc0000433: return EBUSY; 74 | case 0xc0000434: return EBUSY; 75 | case 0xc0000455: return EINVAL; 76 | case 0xc0000467: return EACCES; 77 | case 0xc0000491: return ENOENT; 78 | case 0xc0000495: return EAGAIN; 79 | case 0xc0000503: return EAGAIN; 80 | case 0xc0000507: return EBUSY; 81 | case 0xc0000512: return EACCES; 82 | case 0xc000070a: return EINVAL; 83 | case 0xc000070b: return EINVAL; 84 | case 0xc000070c: return EINVAL; 85 | case 0xc000070d: return EINVAL; 86 | case 0xc000070e: return EINVAL; 87 | case 0xc000070f: return EINVAL; 88 | case 0xc0000710: return ENOSYS; 89 | case 0xc0000711: return ENOSYS; 90 | case 0xc0000716: return EINVAL; 91 | case 0xc000071b: return ENOSYS; 92 | case 0xc000071d: return ENOSYS; 93 | case 0xc000071e: return ENOSYS; 94 | case 0xc000071f: return ENOSYS; 95 | case 0xc0000720: return ENOSYS; 96 | case 0xc0000721: return ENOSYS; 97 | case 0xc000080f: return EAGAIN; 98 | case 0xc000a203: return EACCES; 99 | -------------------------------------------------------------------------------- /include/status-code/detail/win32_code_to_generic_code.ipp: -------------------------------------------------------------------------------- 1 | case 0x1: return ENOSYS; 2 | case 0x2: return ENOENT; 3 | case 0x3: return ENOENT; 4 | case 0x4: return EMFILE; 5 | case 0x5: return EACCES; 6 | case 0x6: return EINVAL; 7 | case 0x8: return ENOMEM; 8 | case 0xc: return EACCES; 9 | case 0xe: return ENOMEM; 10 | case 0xf: return ENODEV; 11 | case 0x10: return EACCES; 12 | case 0x11: return EXDEV; 13 | case 0x13: return EACCES; 14 | case 0x14: return ENODEV; 15 | case 0x15: return EAGAIN; 16 | case 0x19: return EIO; 17 | case 0x1d: return EIO; 18 | case 0x1e: return EIO; 19 | case 0x20: return EACCES; 20 | case 0x21: return ENOLCK; 21 | case 0x27: return ENOSPC; 22 | case 0x37: return ENODEV; 23 | case 0x50: return EEXIST; 24 | case 0x52: return EACCES; 25 | case 0x57: return EINVAL; 26 | case 0x6e: return EIO; 27 | case 0x6f: return ENAMETOOLONG; 28 | case 0x70: return ENOSPC; 29 | case 0x7b: return EINVAL; 30 | case 0x83: return EINVAL; 31 | case 0x8e: return EBUSY; 32 | case 0x91: return ENOTEMPTY; 33 | case 0xaa: return EBUSY; 34 | case 0xb7: return EEXIST; 35 | case 0xd4: return ENOLCK; 36 | case 0x10b: return EINVAL; 37 | case 0x3e3: return ECANCELED; 38 | case 0x3e6: return EACCES; 39 | case 0x3f3: return EIO; 40 | case 0x3f4: return EIO; 41 | case 0x3f5: return EIO; 42 | case 0x4d5: return EAGAIN; 43 | case 0x961: return EBUSY; 44 | case 0x964: return EBUSY; 45 | case 0x2714: return EINTR; 46 | case 0x2719: return EBADF; 47 | case 0x271d: return EACCES; 48 | case 0x271e: return EFAULT; 49 | case 0x2726: return EINVAL; 50 | case 0x2728: return EMFILE; 51 | case 0x2733: return EWOULDBLOCK; 52 | case 0x2734: return EINPROGRESS; 53 | case 0x2735: return EALREADY; 54 | case 0x2736: return ENOTSOCK; 55 | case 0x2737: return EDESTADDRREQ; 56 | case 0x2738: return EMSGSIZE; 57 | case 0x2739: return EPROTOTYPE; 58 | case 0x273a: return ENOPROTOOPT; 59 | case 0x273b: return EPROTONOSUPPORT; 60 | case 0x273d: return EOPNOTSUPP; 61 | case 0x273f: return EAFNOSUPPORT; 62 | case 0x2740: return EADDRINUSE; 63 | case 0x2741: return EADDRNOTAVAIL; 64 | case 0x2742: return ENETDOWN; 65 | case 0x2743: return ENETUNREACH; 66 | case 0x2744: return ENETRESET; 67 | case 0x2745: return ECONNABORTED; 68 | case 0x2746: return ECONNRESET; 69 | case 0x2747: return ENOBUFS; 70 | case 0x2748: return EISCONN; 71 | case 0x2749: return ENOTCONN; 72 | case 0x274c: return ETIMEDOUT; 73 | case 0x274d: return ECONNREFUSED; 74 | case 0x274f: return ENAMETOOLONG; 75 | case 0x2751: return EHOSTUNREACH; 76 | -------------------------------------------------------------------------------- /include/status-code/error.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 Niall Douglas (5 commits) 3 | File Created: Feb 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_ERROR_HPP 26 | #define SYSTEM_ERROR2_ERROR_HPP 27 | 28 | #include "errored_status_code.hpp" 29 | #include "system_code.hpp" 30 | 31 | SYSTEM_ERROR2_NAMESPACE_BEGIN 32 | 33 | /*! An errored `system_code` which must be a failure upon copy or move or 34 | non-default construction. The closest equivalent to `std::error_code`, except 35 | it cannot be modified. 36 | 37 | This refines `system_code` into an `error` object meeting the requirements of 38 | [P0709 Zero-overhead deterministic exceptions](https://wg21.link/P0709). 39 | 40 | Differences from `system_code`: 41 | 42 | - Almost always a failure (this is checked at copy or move and non-default 43 | construction, and if not the case, the program is terminated as this is a logic 44 | error) 45 | - Is immutable. 46 | 47 | As with `system_code`, it remains guaranteed to be two CPU registers in size, 48 | and move bitcopying. 49 | */ 50 | using error = erased_errored_status_code; 51 | 52 | #ifndef NDEBUG 53 | static_assert(sizeof(error) == 2 * sizeof(void *), "error is not exactly two pointers in size!"); 54 | static_assert(traits::is_move_bitcopying::value, "error is not move bitcopying!"); 55 | #endif 56 | 57 | SYSTEM_ERROR2_NAMESPACE_END 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/status-code/getaddrinfo_code.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2020 Niall Douglas (5 commits) 3 | File Created: Jan 2020 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_GETADDRINFO_CODE_HPP 26 | #define SYSTEM_ERROR2_GETADDRINFO_CODE_HPP 27 | 28 | #include "quick_status_code_from_enum.hpp" 29 | 30 | #ifdef _WIN32 31 | #error Not available for Microsoft Windows 32 | #else 33 | #include 34 | #include 35 | #include 36 | #endif 37 | 38 | SYSTEM_ERROR2_NAMESPACE_BEGIN 39 | 40 | class _getaddrinfo_code_domain; 41 | //! A getaddrinfo error code, those returned by `getaddrinfo()`. 42 | using getaddrinfo_code = status_code<_getaddrinfo_code_domain>; 43 | //! A specialisation of `status_error` for the `getaddrinfo()` error code domain. 44 | using getaddrinfo_error = status_error<_getaddrinfo_code_domain>; 45 | 46 | /*! The implementation of the domain for `getaddrinfo()` error codes, those returned by `getaddrinfo()`. 47 | */ 48 | class _getaddrinfo_code_domain : public status_code_domain 49 | { 50 | template friend class status_code; 51 | using _base = status_code_domain; 52 | 53 | public: 54 | //! The value type of the `getaddrinfo()` code, which is an `int` 55 | using value_type = int; 56 | using _base::string_ref; 57 | 58 | //! Default constructor 59 | constexpr explicit _getaddrinfo_code_domain(typename _base::unique_id_type id = 0x5b24b2de470ff7b6) noexcept 60 | : _base(id) 61 | { 62 | } 63 | _getaddrinfo_code_domain(const _getaddrinfo_code_domain &) = default; 64 | _getaddrinfo_code_domain(_getaddrinfo_code_domain &&) = default; 65 | _getaddrinfo_code_domain &operator=(const _getaddrinfo_code_domain &) = default; 66 | _getaddrinfo_code_domain &operator=(_getaddrinfo_code_domain &&) = default; 67 | ~_getaddrinfo_code_domain() = default; 68 | 69 | //! Constexpr singleton getter. Returns constexpr getaddrinfo_code_domain variable. 70 | static inline constexpr const _getaddrinfo_code_domain &get(); 71 | 72 | virtual string_ref name() const noexcept override { return string_ref("getaddrinfo() domain"); } // NOLINT 73 | 74 | virtual payload_info_t payload_info() const noexcept override 75 | { 76 | return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), 77 | (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; 78 | } 79 | 80 | protected: 81 | virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT 82 | { 83 | assert(code.domain() == *this); // NOLINT 84 | return static_cast(code).value() != 0; // NOLINT 85 | } 86 | virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override // NOLINT 87 | { 88 | assert(code1.domain() == *this); // NOLINT 89 | const auto &c1 = static_cast(code1); // NOLINT 90 | if(code2.domain() == *this) 91 | { 92 | const auto &c2 = static_cast(code2); // NOLINT 93 | return c1.value() == c2.value(); 94 | } 95 | return false; 96 | } 97 | virtual generic_code _generic_code(const status_code &code) const noexcept override // NOLINT 98 | { 99 | assert(code.domain() == *this); // NOLINT 100 | const auto &c = static_cast(code); // NOLINT 101 | switch(c.value()) 102 | { 103 | #ifdef EAI_ADDRFAMILY 104 | case EAI_ADDRFAMILY: 105 | return errc::no_such_device_or_address; 106 | #endif 107 | case EAI_FAIL: 108 | return errc::io_error; 109 | case EAI_MEMORY: 110 | return errc::not_enough_memory; 111 | #ifdef EAI_NODATA 112 | case EAI_NODATA: 113 | return errc::no_such_device_or_address; 114 | #endif 115 | case EAI_NONAME: 116 | return errc::no_such_device_or_address; 117 | #ifdef EAI_OVERFLOW 118 | case EAI_OVERFLOW: 119 | return errc::argument_list_too_long; 120 | #endif 121 | case EAI_BADFLAGS: // fallthrough 122 | case EAI_SERVICE: 123 | return errc::invalid_argument; 124 | case EAI_FAMILY: // fallthrough 125 | case EAI_SOCKTYPE: 126 | return errc::operation_not_supported; 127 | case EAI_AGAIN: // fallthrough 128 | case EAI_SYSTEM: 129 | return errc::resource_unavailable_try_again; 130 | default: 131 | return errc::unknown; 132 | } 133 | } 134 | virtual string_ref _do_message(const status_code &code) const noexcept override // NOLINT 135 | { 136 | assert(code.domain() == *this); // NOLINT 137 | const auto &c = static_cast(code); // NOLINT 138 | return string_ref(gai_strerror(c.value())); 139 | } 140 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 141 | SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT 142 | { 143 | assert(code.domain() == *this); // NOLINT 144 | const auto &c = static_cast(code); // NOLINT 145 | throw status_error<_getaddrinfo_code_domain>(c); 146 | } 147 | #endif 148 | }; 149 | //! A constexpr source variable for the `getaddrinfo()` code domain, which is that of `getaddrinfo()`. Returned by `_getaddrinfo_code_domain::get()`. 150 | constexpr _getaddrinfo_code_domain getaddrinfo_code_domain; 151 | inline constexpr const _getaddrinfo_code_domain &_getaddrinfo_code_domain::get() 152 | { 153 | return getaddrinfo_code_domain; 154 | } 155 | 156 | SYSTEM_ERROR2_NAMESPACE_END 157 | 158 | #endif 159 | -------------------------------------------------------------------------------- /include/status-code/http_status_code.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2022 Niall Douglas (5 commits) 3 | File Created: Jun 2022 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_HTTP_STATUS_CODE_HPP 26 | #define SYSTEM_ERROR2_HTTP_STATUS_CODE_HPP 27 | 28 | #include "status_code.hpp" 29 | 30 | SYSTEM_ERROR2_NAMESPACE_BEGIN 31 | 32 | class _http_status_code_domain; 33 | //! A HTTP status code. 34 | using http_status_code = status_code<_http_status_code_domain>; 35 | 36 | namespace mixins 37 | { 38 | template struct mixin : public Base 39 | { 40 | using Base::Base; 41 | 42 | //! True if the HTTP status code is informational 43 | inline bool is_http_informational() const noexcept; 44 | //! True if the HTTP status code is successful 45 | inline bool is_http_success() const noexcept; 46 | //! True if the HTTP status code is redirection 47 | inline bool is_http_redirection() const noexcept; 48 | //! True if the HTTP status code is client error 49 | inline bool is_http_client_error() const noexcept; 50 | //! True if the HTTP status code is server error 51 | inline bool is_http_server_error() const noexcept; 52 | }; 53 | } // namespace mixins 54 | 55 | /*! The implementation of the domain for HTTP status codes. 56 | */ 57 | class _http_status_code_domain : public status_code_domain 58 | { 59 | template friend class status_code; 60 | using _base = status_code_domain; 61 | 62 | public: 63 | //! The value type of the HTTP code, which is an `int` 64 | using value_type = int; 65 | using _base::string_ref; 66 | 67 | //! Default constructor 68 | constexpr explicit _http_status_code_domain(typename _base::unique_id_type id = 0xbdb4cde88378a333ull) noexcept 69 | : _base(id) 70 | { 71 | } 72 | _http_status_code_domain(const _http_status_code_domain &) = default; 73 | _http_status_code_domain(_http_status_code_domain &&) = default; 74 | _http_status_code_domain &operator=(const _http_status_code_domain &) = default; 75 | _http_status_code_domain &operator=(_http_status_code_domain &&) = default; 76 | ~_http_status_code_domain() = default; 77 | 78 | //! Constexpr singleton getter. Returns constexpr http_status_code_domain variable. 79 | static inline constexpr const _http_status_code_domain &get(); 80 | 81 | virtual string_ref name() const noexcept override { return string_ref("HTTP status domain"); } // NOLINT 82 | 83 | virtual payload_info_t payload_info() const noexcept override 84 | { 85 | return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), 86 | (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; 87 | } 88 | 89 | protected: 90 | virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT 91 | { 92 | assert(code.domain() == *this); // NOLINT 93 | return static_cast(code).value() >= 400; // NOLINT 94 | } 95 | virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override // NOLINT 96 | { 97 | assert(code1.domain() == *this); // NOLINT 98 | const auto &c1 = static_cast(code1); // NOLINT 99 | if(code2.domain() == *this) 100 | { 101 | const auto &c2 = static_cast(code2); // NOLINT 102 | return c1.value() == c2.value(); 103 | } 104 | return false; 105 | } 106 | virtual generic_code _generic_code(const status_code &code) const noexcept override // NOLINT 107 | { 108 | assert(code.domain() == *this); // NOLINT 109 | const auto &c = static_cast(code); // NOLINT 110 | switch(c.value()) 111 | { 112 | case 102: 113 | case 202: 114 | return errc::operation_in_progress; 115 | case 400: 116 | return errc::invalid_argument; 117 | case 401: 118 | return errc::operation_not_permitted; 119 | case 403: 120 | return errc::permission_denied; 121 | case 404: 122 | case 410: 123 | return errc::no_such_file_or_directory; 124 | case 405: 125 | case 418: 126 | return errc::operation_not_supported; 127 | case 406: 128 | return errc::protocol_not_supported; 129 | case 408: 130 | return errc::timed_out; 131 | case 413: 132 | return errc::result_out_of_range; 133 | case 501: 134 | return errc::not_supported; 135 | case 503: 136 | return errc::resource_unavailable_try_again; 137 | case 504: 138 | return errc::timed_out; 139 | case 507: 140 | return errc::no_space_on_device; 141 | default: 142 | return errc::unknown; 143 | } 144 | } 145 | virtual string_ref _do_message(const status_code &code) const noexcept override // NOLINT 146 | { 147 | assert(code.domain() == *this); // NOLINT 148 | const auto &c = static_cast(code); // NOLINT 149 | return string_ref( 150 | [&]() -> const char * 151 | { 152 | switch(c.value()) 153 | { 154 | case 100: 155 | return "Continue"; 156 | case 101: 157 | return "Switching Protocols"; 158 | case 102: 159 | return "Processing"; 160 | case 103: 161 | return "Early Hints"; 162 | case 200: 163 | return "OK"; 164 | case 201: 165 | return "Created"; 166 | case 202: 167 | return "Accepted"; 168 | case 203: 169 | return "Non-Authoritative Information"; 170 | case 204: 171 | return "No Content"; 172 | case 205: 173 | return "Reset Content"; 174 | case 206: 175 | return "Partial Content"; 176 | case 207: 177 | return "Multi-Status"; 178 | case 208: 179 | return "Already Reported"; 180 | case 209: 181 | return "IM Used"; 182 | case 300: 183 | return "Multiple Choices"; 184 | case 301: 185 | return "Moved Permanently"; 186 | case 302: 187 | return "Found"; 188 | case 303: 189 | return "See Other"; 190 | case 304: 191 | return "Not Modified"; 192 | case 305: 193 | return "Use Proxy"; 194 | case 306: 195 | return "Switch Proxy"; 196 | case 307: 197 | return "Temporary Redirect"; 198 | case 308: 199 | return "Permanent Redirect"; 200 | case 400: 201 | return "Bad Request"; 202 | case 401: 203 | return "Unauthorized"; 204 | case 402: 205 | return "Payment Required"; 206 | case 403: 207 | return "Forbidden"; 208 | case 404: 209 | return "Not Found"; 210 | case 405: 211 | return "Method Not Allowed"; 212 | case 406: 213 | return "Not Acceptable"; 214 | case 407: 215 | return "Proxy Authentication Required"; 216 | case 408: 217 | return "Request Timeout"; 218 | case 409: 219 | return "Conflict"; 220 | case 410: 221 | return "Gone"; 222 | case 411: 223 | return "Length Required"; 224 | case 412: 225 | return "Precondition Failed"; 226 | case 413: 227 | return "Payload Too Large"; 228 | case 414: 229 | return "URI Too Long"; 230 | case 415: 231 | return "Unsupported Media Type"; 232 | case 416: 233 | return "Range Not Satisfiable"; 234 | case 417: 235 | return "Expectation Failed"; 236 | case 418: 237 | return "I'm a teapot"; 238 | case 421: 239 | return "Misdirected Request"; 240 | case 422: 241 | return "Unprocessable Entity"; 242 | case 423: 243 | return "Locked"; 244 | case 424: 245 | return "Failed Dependency"; 246 | case 425: 247 | return "Too Early"; 248 | case 426: 249 | return "Upgrade Required"; 250 | case 428: 251 | return "Precondition Required"; 252 | case 429: 253 | return "Too Many Requests"; 254 | case 431: 255 | return "Request Header Fields Too Large"; 256 | case 451: 257 | return "Unavailable For Legal Reasons"; 258 | case 500: 259 | return "Internal Server Error"; 260 | case 501: 261 | return "Not Implemented"; 262 | case 502: 263 | return "Bad Gateway"; 264 | case 503: 265 | return "Service Unavailable"; 266 | case 504: 267 | return "Gateway Timeout"; 268 | case 505: 269 | return "HTTP Version Not Supported"; 270 | case 506: 271 | return "Variant Also Negotiates"; 272 | case 507: 273 | return "Insufficient Storage"; 274 | case 508: 275 | return "Loop Detected"; 276 | case 510: 277 | return "Not Extended"; 278 | case 511: 279 | return "Network Authentication Required"; 280 | default: 281 | return "Unknown"; 282 | } 283 | }()); 284 | } 285 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 286 | SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT 287 | { 288 | assert(code.domain() == *this); // NOLINT 289 | const auto &c = static_cast(code); // NOLINT 290 | throw status_error<_http_status_code_domain>(c); 291 | } 292 | #endif 293 | }; 294 | //! A constexpr source variable for the `getaddrinfo()` code domain, which is that of `getaddrinfo()`. Returned by `_http_status_code_domain::get()`. 295 | constexpr _http_status_code_domain http_status_code_domain; 296 | inline constexpr const _http_status_code_domain &_http_status_code_domain::get() 297 | { 298 | return http_status_code_domain; 299 | } 300 | 301 | namespace mixins 302 | { 303 | template inline bool mixin::is_http_informational() const noexcept 304 | { 305 | const auto &c = static_cast(this)->value(); 306 | return c >= 100 && c < 200; 307 | } 308 | template inline bool mixin::is_http_success() const noexcept 309 | { 310 | const auto &c = static_cast(this)->value(); 311 | return c >= 200 && c < 300; 312 | } 313 | template inline bool mixin::is_http_redirection() const noexcept 314 | { 315 | const auto &c = static_cast(this)->value(); 316 | return c >= 300 && c < 400; 317 | } 318 | template inline bool mixin::is_http_client_error() const noexcept 319 | { 320 | const auto &c = static_cast(this)->value(); 321 | return c >= 400 && c < 500; 322 | } 323 | template inline bool mixin::is_http_server_error() const noexcept 324 | { 325 | const auto &c = static_cast(this)->value(); 326 | return c >= 500 && c < 600; 327 | } 328 | } // namespace mixins 329 | 330 | 331 | SYSTEM_ERROR2_NAMESPACE_END 332 | 333 | #endif 334 | -------------------------------------------------------------------------------- /include/status-code/iostream_support.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 Niall Douglas (5 commits) 3 | File Created: Feb 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_IOSTREAM_SUPPORT_HPP 26 | #define SYSTEM_ERROR2_IOSTREAM_SUPPORT_HPP 27 | 28 | #include "error.hpp" 29 | 30 | #include 31 | 32 | SYSTEM_ERROR2_NAMESPACE_BEGIN 33 | 34 | /*! Print the status code to a `std::ostream &`. 35 | Requires that `DomainType::value_type` implements an `operator<<` overload for `std::ostream`. 36 | */ 37 | SYSTEM_ERROR2_TEMPLATE(class DomainType) // 38 | SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED( 39 | std::is_same() << std::declval::value_type>())>::type>::value)) 41 | inline std::ostream &operator<<(std::ostream &s, const status_code &v) 42 | { 43 | if(v.empty()) 44 | { 45 | return s << "(empty)"; 46 | } 47 | return s << v.domain().name().c_str() << ": " << v.value(); 48 | } 49 | 50 | /*! Print a status code domain's `string_ref` to a `std::ostream &`. 51 | */ 52 | inline std::ostream &operator<<(std::ostream &s, const status_code_domain::string_ref &v) 53 | { 54 | return s << v.c_str(); 55 | } 56 | 57 | /*! Print the erased status code to a `std::ostream &`. 58 | */ 59 | template inline std::ostream &operator<<(std::ostream &s, const status_code> &v) 60 | { 61 | if(v.empty()) 62 | { 63 | return s << "(empty)"; 64 | } 65 | return s << v.domain().name() << ": " << v.message(); 66 | } 67 | 68 | /*! Print the generic code to a `std::ostream &`. 69 | */ 70 | inline std::ostream &operator<<(std::ostream &s, const generic_code &v) 71 | { 72 | if(v.empty()) 73 | { 74 | return s << "(empty)"; 75 | } 76 | return s << v.domain().name() << ": " << v.message(); 77 | } 78 | 79 | SYSTEM_ERROR2_NAMESPACE_END 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /include/status-code/nested_status_code.hpp: -------------------------------------------------------------------------------- 1 | /* Pointer to a SG14 status_code 2 | (C) 2018 - 2023 Niall Douglas (5 commits) 3 | File Created: Sep 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_NESTED_STATUS_CODE_HPP 26 | #define SYSTEM_ERROR2_NESTED_STATUS_CODE_HPP 27 | 28 | #include "quick_status_code_from_enum.hpp" 29 | 30 | #include // for allocator 31 | 32 | SYSTEM_ERROR2_NAMESPACE_BEGIN 33 | 34 | namespace detail 35 | { 36 | template class indirecting_domain : public status_code_domain 37 | { 38 | template friend class status_code; 39 | using _base = status_code_domain; 40 | 41 | public: 42 | struct payload_type 43 | { 44 | using allocator_traits = std::allocator_traits; 45 | union 46 | { 47 | char _uninit[sizeof(StatusCode)]; 48 | StatusCode sc; 49 | }; 50 | Allocator alloc; 51 | 52 | payload_type(StatusCode _sc, Allocator _alloc) 53 | : alloc(static_cast(_alloc)) 54 | { 55 | allocator_traits::construct(alloc, &sc, static_cast(_sc)); 56 | } 57 | payload_type(const payload_type &) = delete; 58 | payload_type(payload_type &&) = delete; 59 | ~payload_type() { allocator_traits::destroy(alloc, &sc); } 60 | }; 61 | using value_type = payload_type *; 62 | using payload_allocator_traits = typename payload_type::allocator_traits::template rebind_traits; 63 | using _base::string_ref; 64 | 65 | constexpr indirecting_domain() noexcept 66 | : _base(0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id() /* unique-ish based on domain's unique id */) 67 | { 68 | } 69 | indirecting_domain(const indirecting_domain &) = default; 70 | indirecting_domain(indirecting_domain &&) = default; // NOLINT 71 | indirecting_domain &operator=(const indirecting_domain &) = default; 72 | indirecting_domain &operator=(indirecting_domain &&) = default; // NOLINT 73 | ~indirecting_domain() = default; 74 | 75 | #if __cplusplus < 201402L && !defined(_MSC_VER) 76 | static inline const indirecting_domain &get() 77 | { 78 | static indirecting_domain v; 79 | return v; 80 | } 81 | #else 82 | static inline constexpr const indirecting_domain &get(); 83 | #endif 84 | 85 | virtual string_ref name() const noexcept override { return typename StatusCode::domain_type().name(); } // NOLINT 86 | 87 | virtual payload_info_t payload_info() const noexcept override 88 | { 89 | return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), 90 | (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; 91 | } 92 | 93 | protected: 94 | using _mycode = status_code; 95 | virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT 96 | { 97 | assert(code.domain() == *this); 98 | const auto &c = static_cast(code); // NOLINT 99 | return static_cast(typename StatusCode::domain_type())._do_failure(c.value()->sc); 100 | } 101 | virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override // NOLINT 102 | { 103 | assert(code1.domain() == *this); 104 | const auto &c1 = static_cast(code1); // NOLINT 105 | return static_cast(typename StatusCode::domain_type())._do_equivalent(c1.value()->sc, code2); 106 | } 107 | virtual generic_code _generic_code(const status_code &code) const noexcept override // NOLINT 108 | { 109 | assert(code.domain() == *this); 110 | const auto &c = static_cast(code); // NOLINT 111 | return static_cast(typename StatusCode::domain_type())._generic_code(c.value()->sc); 112 | } 113 | virtual string_ref _do_message(const status_code &code) const noexcept override // NOLINT 114 | { 115 | assert(code.domain() == *this); 116 | const auto &c = static_cast(code); // NOLINT 117 | return static_cast(typename StatusCode::domain_type())._do_message(c.value()->sc); 118 | } 119 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 120 | SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT 121 | { 122 | assert(code.domain() == *this); 123 | const auto &c = static_cast(code); // NOLINT 124 | static_cast(typename StatusCode::domain_type())._do_throw_exception(c.value()->sc); 125 | abort(); // suppress buggy GCC warning 126 | } 127 | #endif 128 | virtual bool _do_erased_copy(status_code &dst, const status_code &src, payload_info_t dstinfo) const override // NOLINT 129 | { 130 | // Note that dst may not have its domain set 131 | const auto srcinfo = payload_info(); 132 | assert(src.domain() == *this); 133 | if(dstinfo.total_size < srcinfo.total_size) 134 | { 135 | return false; 136 | } 137 | auto &d = static_cast<_mycode &>(dst); // NOLINT 138 | const auto &_s = static_cast(src); // NOLINT 139 | const payload_type &sp = *_s.value(); 140 | typename payload_allocator_traits::template rebind_alloc payload_alloc(sp.alloc); 141 | auto *dp = payload_allocator_traits::allocate(payload_alloc, 1); 142 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 143 | try 144 | #endif 145 | { 146 | payload_allocator_traits::construct(payload_alloc, dp, sp.sc, sp.alloc); 147 | new(SYSTEM_ERROR2_ADDRESS_OF(d)) _mycode(in_place, dp); 148 | } 149 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 150 | catch(...) 151 | { 152 | payload_allocator_traits::deallocate(payload_alloc, dp, 1); 153 | throw; 154 | } 155 | #endif 156 | return true; 157 | } 158 | virtual void _do_erased_destroy(status_code &code, payload_info_t /*unused*/) const noexcept override // NOLINT 159 | { 160 | assert(code.domain() == *this); 161 | auto &c = static_cast<_mycode &>(code); // NOLINT 162 | payload_type *p = c.value(); 163 | typename payload_allocator_traits::template rebind_alloc payload_alloc(p->alloc); 164 | payload_allocator_traits::destroy(payload_alloc, p); 165 | payload_allocator_traits::deallocate(payload_alloc, p, 1); 166 | } 167 | }; 168 | #if __cplusplus >= 201402L || defined(_MSC_VER) 169 | template constexpr indirecting_domain _indirecting_domain{}; 170 | template 171 | inline constexpr const indirecting_domain &indirecting_domain::get() 172 | { 173 | return _indirecting_domain; 174 | } 175 | #endif 176 | } // namespace detail 177 | 178 | /*! Make an erased status code which indirects to a dynamically allocated status code, 179 | using the allocator `alloc`. 180 | 181 | This is useful for shoehorning a rich status code with large value type into a small 182 | erased status code like `system_code`, with which the status code generated by this 183 | function is compatible. Note that this function can throw if the allocator throws. 184 | */ 185 | SYSTEM_ERROR2_TEMPLATE(class T, class Alloc = std::allocator::type>) 186 | SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) // 187 | inline status_code::type>::type>> make_nested_status_code(T &&v, Alloc alloc = {}) 188 | { 189 | using status_code_type = typename std::decay::type; 190 | using domain_type = detail::indirecting_domain::type>; 191 | using payload_allocator_traits = typename domain_type::payload_allocator_traits; 192 | typename payload_allocator_traits::template rebind_alloc payload_alloc(alloc); 193 | auto *p = payload_allocator_traits::allocate(payload_alloc, 1); 194 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 195 | try 196 | #endif 197 | { 198 | payload_allocator_traits::construct(payload_alloc, p, static_cast(v), static_cast(alloc)); 199 | return status_code(in_place, p); 200 | } 201 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 202 | catch(...) 203 | { 204 | payload_allocator_traits::deallocate(payload_alloc, p, 1); 205 | throw; 206 | } 207 | #endif 208 | } 209 | 210 | /*! If a status code refers to a `nested_status_code` which indirects to a status 211 | code of type `StatusCode`, return a pointer to that `StatusCode`. Otherwise return null. 212 | */ 213 | SYSTEM_ERROR2_TEMPLATE(class StatusCode, class U) 214 | SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) inline StatusCode *get_if(status_code> *v) noexcept 215 | { 216 | if((0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id()) != v->domain().id()) 217 | { 218 | return nullptr; 219 | } 220 | union 221 | { 222 | U value; 223 | StatusCode *ret; 224 | }; 225 | value = v->value(); 226 | return ret; 227 | } 228 | //! \overload Const overload 229 | SYSTEM_ERROR2_TEMPLATE(class StatusCode, class U) 230 | SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) 231 | inline const StatusCode *get_if(const status_code> *v) noexcept 232 | { 233 | if((0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id()) != v->domain().id()) 234 | { 235 | return nullptr; 236 | } 237 | union 238 | { 239 | U value; 240 | const StatusCode *ret; 241 | }; 242 | value = v->value(); 243 | return ret; 244 | } 245 | 246 | /*! If a status code refers to a `nested_status_code`, return the id of the erased 247 | status code's domain. Otherwise return a meaningless number. 248 | */ 249 | template inline typename status_code_domain::unique_id_type get_id(const status_code> &v) noexcept 250 | { 251 | return 0xc44f7bdeb2cc50e9 ^ v.domain().id(); 252 | } 253 | 254 | SYSTEM_ERROR2_NAMESPACE_END 255 | 256 | #endif 257 | -------------------------------------------------------------------------------- /include/status-code/nt_code.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 Niall Douglas (5 commits) 3 | File Created: Feb 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_NT_CODE_HPP 26 | #define SYSTEM_ERROR2_NT_CODE_HPP 27 | 28 | #if !defined(_WIN32) && !defined(STANDARDESE_IS_IN_THE_HOUSE) 29 | #error This file should only be included on Windows 30 | #endif 31 | 32 | #include "win32_code.hpp" 33 | 34 | #if defined(_MSC_VER) && !defined(__clang__) 35 | #pragma warning(push) 36 | #pragma warning(disable : 6326) // constant comparison 37 | #endif 38 | 39 | SYSTEM_ERROR2_NAMESPACE_BEGIN 40 | 41 | //! \exclude 42 | namespace win32 43 | { 44 | #ifdef __MINGW32__ 45 | extern "C" 46 | { 47 | #endif 48 | // A Win32 NTSTATUS 49 | using NTSTATUS = long; 50 | // A Win32 HMODULE 51 | using HMODULE = void *; 52 | // Used to retrieve where the NTDLL DLL is mapped into memory 53 | extern HMODULE __stdcall GetModuleHandleW(const wchar_t *lpModuleName); 54 | #ifdef __MINGW32__ 55 | } 56 | #else 57 | #pragma comment(lib, "kernel32.lib") 58 | #if(defined(__x86_64__) || defined(_M_X64)) || (defined(__aarch64__) || defined(_M_ARM64)) 59 | #pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YAPEAXPEB_W@Z=GetModuleHandleW") 60 | #elif defined(__x86__) || defined(_M_IX86) || defined(__i386__) 61 | #pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YGPAXPB_W@Z=_GetModuleHandleW@4") 62 | #elif defined(__arm__) || defined(_M_ARM) 63 | #pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YAPAXPB_W@Z=GetModuleHandleW") 64 | #else 65 | #error Unknown architecture 66 | #endif 67 | #endif 68 | } // namespace win32 69 | 70 | class _nt_code_domain; 71 | //! (Windows only) A NT error code, those returned by NT kernel functions. 72 | using nt_code = status_code<_nt_code_domain>; 73 | //! (Windows only) A specialisation of `status_error` for the NT error code domain. 74 | using nt_error = status_error<_nt_code_domain>; 75 | 76 | /*! (Windows only) The implementation of the domain for NT error codes, those returned by NT kernel functions. 77 | */ 78 | class _nt_code_domain : public status_code_domain 79 | { 80 | template friend class status_code; 81 | friend class _com_code_domain; 82 | using _base = status_code_domain; 83 | static int _nt_code_to_errno(win32::NTSTATUS c) 84 | { 85 | if(c >= 0) 86 | { 87 | return 0; // success 88 | } 89 | switch(static_cast(c)) 90 | { 91 | #include "detail/nt_code_to_generic_code.ipp" 92 | } 93 | return -1; 94 | } 95 | static win32::DWORD _nt_code_to_win32_code(win32::NTSTATUS c) // NOLINT 96 | { 97 | if(c >= 0) 98 | { 99 | return 0; // success 100 | } 101 | switch(static_cast(c)) 102 | { 103 | #include "detail/nt_code_to_win32_code.ipp" 104 | } 105 | return static_cast(-1); 106 | } 107 | //! Construct from a NT error code 108 | static _base::string_ref _make_string_ref(win32::NTSTATUS c) noexcept 109 | { 110 | wchar_t buffer[32768]; 111 | static win32::HMODULE ntdll = win32::GetModuleHandleW(L"NTDLL.DLL"); 112 | win32::DWORD wlen = 113 | win32::FormatMessageW(0x00000800 /*FORMAT_MESSAGE_FROM_HMODULE*/ | 0x00001000 /*FORMAT_MESSAGE_FROM_SYSTEM*/ | 0x00000200 /*FORMAT_MESSAGE_IGNORE_INSERTS*/, 114 | ntdll, c, (1 << 10) /*MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)*/, buffer, 32768, nullptr); 115 | size_t allocation = wlen + (wlen >> 1); 116 | win32::DWORD bytes; 117 | if(wlen == 0) 118 | { 119 | return _base::string_ref("failed to get message from system"); 120 | } 121 | for(;;) 122 | { 123 | auto *p = static_cast(malloc(allocation)); // NOLINT 124 | if(p == nullptr) 125 | { 126 | return _base::string_ref("failed to get message from system"); 127 | } 128 | bytes = win32::WideCharToMultiByte(65001 /*CP_UTF8*/, 0, buffer, (int) (wlen + 1), p, (int) allocation, nullptr, nullptr); 129 | if(bytes != 0) 130 | { 131 | char *end = strchr(p, 0); 132 | while(end[-1] == 10 || end[-1] == 13) 133 | { 134 | --end; 135 | } 136 | *end = 0; // NOLINT 137 | return _base::atomic_refcounted_string_ref(p, end - p); 138 | } 139 | free(p); // NOLINT 140 | if(win32::GetLastError() == 0x7a /*ERROR_INSUFFICIENT_BUFFER*/) 141 | { 142 | allocation += allocation >> 2; 143 | continue; 144 | } 145 | return _base::string_ref("failed to get message from system"); 146 | } 147 | } 148 | 149 | public: 150 | //! The value type of the NT code, which is a `win32::NTSTATUS` 151 | using value_type = win32::NTSTATUS; 152 | using _base::string_ref; 153 | 154 | public: 155 | //! Default constructor 156 | constexpr explicit _nt_code_domain(typename _base::unique_id_type id = 0x93f3b4487e4af25b) noexcept 157 | : _base(id) 158 | { 159 | } 160 | _nt_code_domain(const _nt_code_domain &) = default; 161 | _nt_code_domain(_nt_code_domain &&) = default; 162 | _nt_code_domain &operator=(const _nt_code_domain &) = default; 163 | _nt_code_domain &operator=(_nt_code_domain &&) = default; 164 | ~_nt_code_domain() = default; 165 | 166 | //! Constexpr singleton getter. Returns the constexpr nt_code_domain variable. 167 | static inline constexpr const _nt_code_domain &get(); 168 | 169 | virtual string_ref name() const noexcept override { return string_ref("NT domain"); } // NOLINT 170 | 171 | virtual payload_info_t payload_info() const noexcept override 172 | { 173 | return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), 174 | (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; 175 | } 176 | 177 | protected: 178 | virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT 179 | { 180 | assert(code.domain() == *this); 181 | return static_cast(code).value() < 0; // NOLINT 182 | } 183 | virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override // NOLINT 184 | { 185 | assert(code1.domain() == *this); 186 | const auto &c1 = static_cast(code1); // NOLINT 187 | if(code2.domain() == *this) 188 | { 189 | const auto &c2 = static_cast(code2); // NOLINT 190 | return c1.value() == c2.value(); 191 | } 192 | if(code2.domain() == generic_code_domain) 193 | { 194 | const auto &c2 = static_cast(code2); // NOLINT 195 | if(static_cast(c2.value()) == _nt_code_to_errno(c1.value())) 196 | { 197 | return true; 198 | } 199 | } 200 | if(code2.domain() == win32_code_domain) 201 | { 202 | const auto &c2 = static_cast(code2); // NOLINT 203 | if(c2.value() == _nt_code_to_win32_code(c1.value())) 204 | { 205 | return true; 206 | } 207 | } 208 | return false; 209 | } 210 | virtual generic_code _generic_code(const status_code &code) const noexcept override // NOLINT 211 | { 212 | assert(code.domain() == *this); 213 | const auto &c = static_cast(code); // NOLINT 214 | return generic_code(static_cast(_nt_code_to_errno(c.value()))); 215 | } 216 | virtual string_ref _do_message(const status_code &code) const noexcept override // NOLINT 217 | { 218 | assert(code.domain() == *this); 219 | const auto &c = static_cast(code); // NOLINT 220 | return _make_string_ref(c.value()); 221 | } 222 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 223 | SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT 224 | { 225 | assert(code.domain() == *this); 226 | const auto &c = static_cast(code); // NOLINT 227 | throw status_error<_nt_code_domain>(c); 228 | } 229 | #endif 230 | }; 231 | //! (Windows only) A constexpr source variable for the NT code domain, which is that of NT kernel functions. Returned by `_nt_code_domain::get()`. 232 | constexpr _nt_code_domain nt_code_domain; 233 | inline constexpr const _nt_code_domain &_nt_code_domain::get() 234 | { 235 | return nt_code_domain; 236 | } 237 | 238 | SYSTEM_ERROR2_NAMESPACE_END 239 | 240 | #if defined(_MSC_VER) && !defined(__clang__) 241 | #pragma warning(pop) 242 | #endif 243 | 244 | #endif 245 | -------------------------------------------------------------------------------- /include/status-code/posix_code.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018-2020 Niall Douglas (5 commits) 3 | File Created: Feb 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_POSIX_CODE_HPP 26 | #define SYSTEM_ERROR2_POSIX_CODE_HPP 27 | 28 | #ifdef SYSTEM_ERROR2_NOT_POSIX 29 | #error is not includable when SYSTEM_ERROR2_NOT_POSIX is defined! 30 | #endif 31 | 32 | #include "quick_status_code_from_enum.hpp" 33 | 34 | #include // for strchr and strerror_r 35 | 36 | #if defined(_MSC_VER) && !defined(__clang__) 37 | #pragma warning(push) 38 | #pragma warning(disable : 6326) // constant comparison 39 | #endif 40 | 41 | SYSTEM_ERROR2_NAMESPACE_BEGIN 42 | 43 | // Fix for issue #48 Issue compiling on arm-none-eabi (newlib) with GNU extensions off 44 | #if !defined(_MSC_VER) && !defined(__APPLE__) 45 | namespace detail 46 | { 47 | namespace avoid_string_include 48 | { 49 | #if defined(__ANDROID__) 50 | using ::strerror_r; 51 | #elif defined(__GLIBC__) && !defined(__UCLIBC__) 52 | // This returns int for non-glibc strerror_r, but glibc's is particularly weird so we retain it 53 | extern "C" char *strerror_r(int errnum, char *buf, size_t buflen); 54 | #else 55 | extern "C" int strerror_r(int errnum, char *buf, size_t buflen); 56 | #endif 57 | } // namespace avoid_string_include 58 | } // namespace detail 59 | #endif 60 | 61 | class _posix_code_domain; 62 | //! A POSIX error code, those returned by `errno`. 63 | using posix_code = status_code<_posix_code_domain>; 64 | //! A specialisation of `status_error` for the POSIX error code domain. 65 | using posix_error = status_error<_posix_code_domain>; 66 | 67 | namespace mixins 68 | { 69 | template struct mixin : public Base 70 | { 71 | using Base::Base; 72 | 73 | //! Returns a `posix_code` for the current value of `errno`. 74 | static posix_code current() noexcept; 75 | }; 76 | } // namespace mixins 77 | 78 | /*! The implementation of the domain for POSIX error codes, those returned by `errno`. 79 | */ 80 | class _posix_code_domain : public status_code_domain 81 | { 82 | template friend class status_code; 83 | using _base = status_code_domain; 84 | 85 | static _base::string_ref _make_string_ref(int c) noexcept 86 | { 87 | char buffer[1024] = ""; 88 | #ifdef _WIN32 89 | strerror_s(buffer, sizeof(buffer), c); 90 | #elif defined(__GLIBC__) && !defined(__UCLIBC__) // handle glibc's weird strerror_r() 91 | char *s = detail::avoid_string_include::strerror_r(c, buffer, sizeof(buffer)); // NOLINT 92 | if(s != nullptr) 93 | { 94 | strncpy(buffer, s, sizeof(buffer) - 1); // NOLINT 95 | buffer[1023] = 0; 96 | } 97 | #elif !defined(__APPLE__) 98 | detail::avoid_string_include::strerror_r(c, buffer, sizeof(buffer)); 99 | #else 100 | strerror_r(c, buffer, sizeof(buffer)); 101 | #endif 102 | size_t length = strlen(buffer); // NOLINT 103 | auto *p = static_cast(malloc(length + 1)); // NOLINT 104 | if(p == nullptr) 105 | { 106 | return _base::string_ref("failed to get message from system"); 107 | } 108 | memcpy(p, buffer, length + 1); // NOLINT 109 | return _base::atomic_refcounted_string_ref(p, length); 110 | } 111 | 112 | public: 113 | //! The value type of the POSIX code, which is an `int` 114 | using value_type = int; 115 | using _base::string_ref; 116 | 117 | //! Default constructor 118 | constexpr explicit _posix_code_domain(typename _base::unique_id_type id = 0xa59a56fe5f310933) noexcept 119 | : _base(id) 120 | { 121 | } 122 | _posix_code_domain(const _posix_code_domain &) = default; 123 | _posix_code_domain(_posix_code_domain &&) = default; 124 | _posix_code_domain &operator=(const _posix_code_domain &) = default; 125 | _posix_code_domain &operator=(_posix_code_domain &&) = default; 126 | ~_posix_code_domain() = default; 127 | 128 | //! Constexpr singleton getter. Returns constexpr posix_code_domain variable. 129 | static inline constexpr const _posix_code_domain &get(); 130 | 131 | virtual string_ref name() const noexcept override { return string_ref("posix domain"); } // NOLINT 132 | 133 | virtual payload_info_t payload_info() const noexcept override 134 | { 135 | return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), 136 | (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; 137 | } 138 | 139 | protected: 140 | virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT 141 | { 142 | assert(code.domain() == *this); // NOLINT 143 | return static_cast(code).value() != 0; // NOLINT 144 | } 145 | virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override // NOLINT 146 | { 147 | assert(code1.domain() == *this); // NOLINT 148 | const auto &c1 = static_cast(code1); // NOLINT 149 | if(code2.domain() == *this) 150 | { 151 | const auto &c2 = static_cast(code2); // NOLINT 152 | return c1.value() == c2.value(); 153 | } 154 | if(code2.domain() == generic_code_domain) 155 | { 156 | const auto &c2 = static_cast(code2); // NOLINT 157 | if(static_cast(c2.value()) == c1.value()) 158 | { 159 | return true; 160 | } 161 | } 162 | return false; 163 | } 164 | virtual generic_code _generic_code(const status_code &code) const noexcept override // NOLINT 165 | { 166 | assert(code.domain() == *this); // NOLINT 167 | const auto &c = static_cast(code); // NOLINT 168 | return generic_code(static_cast(c.value())); 169 | } 170 | virtual string_ref _do_message(const status_code &code) const noexcept override // NOLINT 171 | { 172 | assert(code.domain() == *this); // NOLINT 173 | const auto &c = static_cast(code); // NOLINT 174 | return _make_string_ref(c.value()); 175 | } 176 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 177 | SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT 178 | { 179 | assert(code.domain() == *this); // NOLINT 180 | const auto &c = static_cast(code); // NOLINT 181 | throw status_error<_posix_code_domain>(c); 182 | } 183 | #endif 184 | }; 185 | //! A constexpr source variable for the POSIX code domain, which is that of `errno`. Returned by `_posix_code_domain::get()`. 186 | constexpr _posix_code_domain posix_code_domain; 187 | inline constexpr const _posix_code_domain &_posix_code_domain::get() 188 | { 189 | return posix_code_domain; 190 | } 191 | 192 | namespace mixins 193 | { 194 | template inline posix_code mixin::current() noexcept 195 | { 196 | return posix_code(errno); 197 | } 198 | } // namespace mixins 199 | 200 | SYSTEM_ERROR2_NAMESPACE_END 201 | 202 | #if defined(_MSC_VER) && !defined(__clang__) 203 | #pragma warning(pop) 204 | #endif 205 | 206 | #endif 207 | -------------------------------------------------------------------------------- /include/status-code/quick_status_code_from_enum.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 - 2020 Niall Douglas (5 commits) 3 | File Created: May 2020 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_HPP 26 | #define SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_HPP 27 | 28 | #ifndef SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES 29 | #define SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES 1 30 | #endif 31 | 32 | #include "generic_code.hpp" 33 | 34 | SYSTEM_ERROR2_NAMESPACE_BEGIN 35 | 36 | template class _quick_status_code_from_enum_domain; 37 | //! A status code wrapping `Enum` generated from `quick_status_code_from_enum`. 38 | template using quick_status_code_from_enum_code = status_code<_quick_status_code_from_enum_domain>; 39 | 40 | //! Defaults for an implementation of `quick_status_code_from_enum` 41 | template struct quick_status_code_from_enum_defaults 42 | { 43 | //! The type of the resulting code 44 | using code_type = quick_status_code_from_enum_code; 45 | //! Used within `quick_status_code_from_enum` to define a mapping of enumeration value with its status code 46 | struct mapping 47 | { 48 | //! The enumeration type 49 | using enumeration_type = Enum; 50 | 51 | //! The value being mapped 52 | const Enum value; 53 | //! A string representation for this enumeration value 54 | const char *message; 55 | //! A list of `errc` equivalents for this enumeration value 56 | const std::initializer_list code_mappings; 57 | }; 58 | //! Used within `quick_status_code_from_enum` to define mixins for the status code wrapping `Enum` 59 | template struct mixin : Base 60 | { 61 | using Base::Base; 62 | }; 63 | }; 64 | 65 | /*! The implementation of the domain for status codes wrapping `Enum` generated from `quick_status_code_from_enum`. 66 | */ 67 | template class _quick_status_code_from_enum_domain : public status_code_domain 68 | { 69 | template friend class status_code; 70 | using _base = status_code_domain; 71 | using _src = quick_status_code_from_enum; 72 | 73 | public: 74 | //! The value type of the quick status code from enum 75 | using value_type = Enum; 76 | using _base::string_ref; 77 | 78 | constexpr _quick_status_code_from_enum_domain() 79 | : status_code_domain(_src::domain_uuid, _uuid_size()) 80 | { 81 | } 82 | _quick_status_code_from_enum_domain(const _quick_status_code_from_enum_domain &) = default; 83 | _quick_status_code_from_enum_domain(_quick_status_code_from_enum_domain &&) = default; 84 | _quick_status_code_from_enum_domain &operator=(const _quick_status_code_from_enum_domain &) = default; 85 | _quick_status_code_from_enum_domain &operator=(_quick_status_code_from_enum_domain &&) = default; 86 | ~_quick_status_code_from_enum_domain() = default; 87 | 88 | #if __cplusplus < 201402L && !defined(_MSC_VER) 89 | static inline const _quick_status_code_from_enum_domain &get() 90 | { 91 | static _quick_status_code_from_enum_domain v; 92 | return v; 93 | } 94 | #else 95 | static inline constexpr const _quick_status_code_from_enum_domain &get(); 96 | #endif 97 | 98 | virtual string_ref name() const noexcept override { return string_ref(_src::domain_name); } 99 | 100 | virtual payload_info_t payload_info() const noexcept override 101 | { 102 | return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), 103 | (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; 104 | } 105 | 106 | protected: 107 | // Not sure if a hash table is worth it here, most enumerations won't be long enough to be worth it 108 | // Also, until C++ 20's consteval, the hash table would get emitted into the binary, bloating it 109 | static SYSTEM_ERROR2_CONSTEXPR14 const typename _src::mapping *_find_mapping(value_type v) noexcept 110 | { 111 | for(const auto &i : _src::value_mappings()) 112 | { 113 | if(i.value == v) 114 | { 115 | return &i; 116 | } 117 | } 118 | return nullptr; 119 | } 120 | 121 | virtual bool _do_failure(const status_code &code) const noexcept override 122 | { 123 | assert(code.domain() == *this); // NOLINT 124 | // If `errc::success` is in the generic code mapping, it is not a failure 125 | const auto *mapping = _find_mapping(static_cast &>(code).value()); 126 | #if SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES 127 | assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table 128 | #endif 129 | if(mapping != nullptr) 130 | { 131 | for(errc ec : mapping->code_mappings) 132 | { 133 | if(ec == errc::success) 134 | { 135 | return false; 136 | } 137 | } 138 | } 139 | return true; 140 | } 141 | virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override 142 | { 143 | assert(code1.domain() == *this); // NOLINT 144 | const auto &c1 = static_cast &>(code1); // NOLINT 145 | if(code2.domain() == *this) 146 | { 147 | const auto &c2 = static_cast &>(code2); // NOLINT 148 | return c1.value() == c2.value(); 149 | } 150 | if(code2.domain() == generic_code_domain) 151 | { 152 | const auto &c2 = static_cast(code2); // NOLINT 153 | const auto *mapping = _find_mapping(c1.value()); 154 | #if SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES 155 | assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table 156 | #endif 157 | if(mapping != nullptr) 158 | { 159 | for(errc ec : mapping->code_mappings) 160 | { 161 | if(ec == c2.value()) 162 | { 163 | return true; 164 | } 165 | } 166 | } 167 | } 168 | return false; 169 | } 170 | virtual generic_code _generic_code(const status_code &code) const noexcept override 171 | { 172 | assert(code.domain() == *this); // NOLINT 173 | const auto *mapping = _find_mapping(static_cast &>(code).value()); 174 | #if SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES 175 | assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table 176 | #endif 177 | if(mapping != nullptr) 178 | { 179 | if(mapping->code_mappings.size() > 0) 180 | { 181 | return *mapping->code_mappings.begin(); 182 | } 183 | } 184 | return errc::unknown; 185 | } 186 | virtual string_ref _do_message(const status_code &code) const noexcept override 187 | { 188 | assert(code.domain() == *this); // NOLINT 189 | const auto *mapping = _find_mapping(static_cast &>(code).value()); 190 | #if SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES 191 | assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table 192 | #endif 193 | if(mapping != nullptr) 194 | { 195 | return string_ref(mapping->message); 196 | } 197 | return string_ref("unknown"); 198 | } 199 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 200 | SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override 201 | { 202 | assert(code.domain() == *this); // NOLINT 203 | const auto &c = static_cast &>(code); // NOLINT 204 | throw status_error<_quick_status_code_from_enum_domain>(c); 205 | } 206 | #endif 207 | }; 208 | 209 | #if __cplusplus >= 201402L || defined(_MSC_VER) 210 | template constexpr _quick_status_code_from_enum_domain quick_status_code_from_enum_domain = {}; 211 | template inline constexpr const _quick_status_code_from_enum_domain &_quick_status_code_from_enum_domain::get() 212 | { 213 | return quick_status_code_from_enum_domain; 214 | } 215 | #endif 216 | 217 | namespace mixins 218 | { 219 | template 220 | struct mixin> : public quick_status_code_from_enum::template mixin 221 | { 222 | using quick_status_code_from_enum::template mixin::mixin; 223 | }; 224 | } // namespace mixins 225 | 226 | SYSTEM_ERROR2_NAMESPACE_END 227 | 228 | #endif 229 | -------------------------------------------------------------------------------- /include/status-code/result.hpp: -------------------------------------------------------------------------------- 1 | /* A partial result based on std::variant and proposed std::error 2 | (C) 2020 Niall Douglas (11 commits) 3 | File Created: Jan 2020 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License in the accompanying file 8 | Licence.txt or at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | 19 | Distributed under the Boost Software License, Version 1.0. 20 | (See accompanying file Licence.txt or copy at 21 | http://www.boost.org/LICENSE_1_0.txt) 22 | */ 23 | 24 | #ifndef SYSTEM_ERROR2_RESULT_HPP 25 | #define SYSTEM_ERROR2_RESULT_HPP 26 | 27 | #include "error.hpp" 28 | 29 | #if __cplusplus >= 201703L || _HAS_CXX17 30 | #if __has_include() 31 | 32 | #include 33 | #include 34 | 35 | SYSTEM_ERROR2_NAMESPACE_BEGIN 36 | 37 | template inline constexpr std::in_place_type_t in_place_type{}; 38 | 39 | template class result; 40 | 41 | //! \brief A trait for detecting result types 42 | template struct is_result : public std::false_type 43 | { 44 | }; 45 | template struct is_result> : public std::true_type 46 | { 47 | }; 48 | 49 | /*! \brief Exception type representing the failure to retrieve an error. 50 | */ 51 | class bad_result_access : public std::exception 52 | { 53 | public: 54 | bad_result_access() = default; 55 | //! Return an explanatory string 56 | virtual const char *what() const noexcept override { return "bad result access"; } // NOLINT 57 | }; 58 | 59 | namespace detail 60 | { 61 | struct void_ 62 | { 63 | }; 64 | template using devoid = std::conditional_t, void_, T>; 65 | } // namespace detail 66 | 67 | /*! \class result 68 | \brief A imperfect `result` type with its error type hardcoded to `error`, only available on C++ 17 or later. 69 | 70 | Note that the proper `result` type does not have the possibility of 71 | valueless by exception state. This implementation is therefore imperfect. 72 | */ 73 | template class result : protected std::variant> 74 | { 75 | using _base = std::variant>; 76 | static_assert(!std::is_reference_v, "Type cannot be a reference"); 77 | static_assert(!std::is_array_v, "Type cannot be an array"); 78 | static_assert(!std::is_same_v, "Type cannot be a std::error"); 79 | // not success nor failure types 80 | 81 | struct _implicit_converting_constructor_tag 82 | { 83 | }; 84 | struct _explicit_converting_constructor_tag 85 | { 86 | }; 87 | struct _implicit_constructor_tag 88 | { 89 | }; 90 | struct _implicit_in_place_value_constructor_tag 91 | { 92 | }; 93 | struct _implicit_in_place_error_constructor_tag 94 | { 95 | }; 96 | 97 | public: 98 | //! The value type 99 | using value_type = T; 100 | //! The error type 101 | using error_type = SYSTEM_ERROR2_NAMESPACE::error; 102 | //! The value type, if it is available, else a usefully named unusable internal type 103 | using value_type_if_enabled = detail::devoid; 104 | //! Used to rebind result types 105 | template using rebind = result; 106 | 107 | protected: 108 | constexpr void _check() const 109 | { 110 | if(_base::index() == 0) 111 | { 112 | std::get_if<0>(this)->throw_exception(); 113 | } 114 | } 115 | constexpr 116 | #ifdef _MSC_VER 117 | __declspec(noreturn) 118 | #elif defined(__GNUC__) || defined(__clang__) 119 | __attribute__((noreturn)) 120 | #endif 121 | void _ub() 122 | { 123 | assert(false); // NOLINT 124 | #if defined(__GNUC__) || defined(__clang__) 125 | __builtin_unreachable(); 126 | #elif defined(_MSC_VER) 127 | __assume(0); 128 | #endif 129 | } 130 | 131 | public: 132 | constexpr _base &_internal() noexcept { return *this; } 133 | constexpr const _base &_internal() const noexcept { return *this; } 134 | 135 | //! Default constructor is disabled 136 | result() = delete; 137 | //! Copy constructor 138 | result(const result &) = delete; 139 | //! Move constructor 140 | result(result &&) = default; 141 | //! Copy assignment 142 | result &operator=(const result &) = delete; 143 | //! Move assignment 144 | result &operator=(result &&) = default; 145 | //! Destructor 146 | ~result() = default; 147 | 148 | //! Implicit result converting move constructor 149 | SYSTEM_ERROR2_TEMPLATE(class U) 150 | SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_convertible_v)) constexpr result(result &&o, _implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v) 151 | : _base(std::move(o)) 152 | { 153 | } 154 | //! Implicit result converting copy constructor 155 | SYSTEM_ERROR2_TEMPLATE(class U) 156 | SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_convertible_v)) constexpr result(const result &o, _implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v) 157 | : _base(o) 158 | { 159 | } 160 | //! Explicit result converting move constructor 161 | SYSTEM_ERROR2_TEMPLATE(class U) 162 | SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_constructible_v)) constexpr explicit result(result &&o, _explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v) 163 | : _base(std::move(o)) 164 | { 165 | } 166 | //! Explicit result converting copy constructor 167 | SYSTEM_ERROR2_TEMPLATE(class U) 168 | SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_constructible_v)) constexpr explicit result(const result &o, _explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v) 169 | : _base(o) 170 | { 171 | } 172 | 173 | //! Anything which `std::variant` will construct from, we shall implicitly construct from 174 | using _base::_base; 175 | 176 | //! Special case `in_place_type_t` 177 | constexpr explicit result(std::in_place_type_t /*unused*/) noexcept 178 | : _base(in_place_type) 179 | { 180 | } 181 | 182 | //! Implicit in-place converting error constructor 183 | SYSTEM_ERROR2_TEMPLATE(class Arg1, class Arg2, class... Args, long = 5) // 184 | SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(!(std::is_constructible_v && std::is_constructible_v) // 185 | &&std::is_constructible_v)) 186 | constexpr result(Arg1 &&arg1, Arg2 &&arg2, Args &&...args) noexcept(std::is_nothrow_constructible_v) 187 | : _base(std::in_place_index<0>, std::forward(arg1), std::forward(arg2), std::forward(args)...) 188 | { 189 | } 190 | 191 | //! Implicit in-place converting value constructor 192 | SYSTEM_ERROR2_TEMPLATE(class Arg1, class Arg2, class... Args, int = 5) // 193 | SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(!(std::is_constructible_v && std::is_constructible_v) // 194 | &&std::is_constructible_v)) 195 | constexpr result(Arg1 &&arg1, Arg2 &&arg2, Args &&...args) noexcept(std::is_nothrow_constructible_v) 196 | : _base(std::in_place_index<1>, std::forward(arg1), std::forward(arg2), std::forward(args)...) 197 | { 198 | } 199 | 200 | //! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`. 201 | SYSTEM_ERROR2_TEMPLATE(class U, class... Args, // 202 | class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result::type) // Safe ADL lookup of make_status_code(), returns void if not found 203 | SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(!std::is_same::type, result>::value // not copy/move of self 204 | && !std::is_same::type, value_type>::value // not copy/move of value type 205 | && is_status_code::value // ADL makes a status code 206 | && std::is_constructible::value)) // ADLed status code is compatible 207 | constexpr result(U &&v, Args &&...args) noexcept(noexcept(make_status_code(std::declval(), std::declval()...))) // NOLINT 208 | : _base(std::in_place_index<0>, make_status_code(static_cast(v), static_cast(args)...)) 209 | { 210 | } 211 | 212 | //! Swap with another result 213 | constexpr void swap(result &o) noexcept(std::is_nothrow_swappable_v<_base>) { _base::swap(o); } 214 | 215 | //! Clone the result 216 | constexpr result clone() const { return has_value() ? result(value()) : result(error().clone()); } 217 | 218 | //! True if result has a value 219 | constexpr bool has_value() const noexcept { return _base::index() == 1; } 220 | //! True if result has a value 221 | explicit operator bool() const noexcept { return has_value(); } 222 | //! True if result has an error 223 | constexpr bool has_error() const noexcept { return _base::index() == 0; } 224 | 225 | //! Accesses the value if one exists, else calls `.error().throw_exception()`. 226 | constexpr value_type_if_enabled &value() & 227 | { 228 | _check(); 229 | return std::get<1>(*this); 230 | } 231 | //! Accesses the value if one exists, else calls `.error().throw_exception()`. 232 | constexpr const value_type_if_enabled &value() const & 233 | { 234 | _check(); 235 | return std::get<1>(*this); 236 | } 237 | //! Accesses the value if one exists, else calls `.error().throw_exception()`. 238 | constexpr value_type_if_enabled &&value() && 239 | { 240 | _check(); 241 | return std::get<1>(std::move(*this)); 242 | } 243 | //! Accesses the value if one exists, else calls `.error().throw_exception()`. 244 | constexpr const value_type_if_enabled &&value() const && 245 | { 246 | _check(); 247 | return std::get<1>(std::move(*this)); 248 | } 249 | 250 | //! Accesses the error if one exists, else throws `bad_result_access`. 251 | constexpr error_type &error() & 252 | { 253 | if(!has_error()) 254 | { 255 | #ifdef __cpp_exceptions 256 | throw bad_result_access(); 257 | #else 258 | abort(); 259 | #endif 260 | } 261 | return *std::get_if<0>(this); 262 | } 263 | //! Accesses the error if one exists, else throws `bad_result_access`. 264 | constexpr const error_type &error() const & 265 | { 266 | if(!has_error()) 267 | { 268 | #ifdef __cpp_exceptions 269 | throw bad_result_access(); 270 | #else 271 | abort(); 272 | #endif 273 | } 274 | return *std::get_if<0>(this); 275 | } 276 | //! Accesses the error if one exists, else throws `bad_result_access`. 277 | constexpr error_type &&error() && 278 | { 279 | if(!has_error()) 280 | { 281 | #ifdef __cpp_exceptions 282 | throw bad_result_access(); 283 | #else 284 | abort(); 285 | #endif 286 | } 287 | return std::move(*std::get_if<0>(this)); 288 | } 289 | //! Accesses the error if one exists, else throws `bad_result_access`. 290 | constexpr const error_type &&error() const && 291 | { 292 | if(!has_error()) 293 | { 294 | #ifdef __cpp_exceptions 295 | throw bad_result_access(); 296 | #else 297 | abort(); 298 | #endif 299 | } 300 | return std::move(*std::get_if<0>(this)); 301 | } 302 | 303 | //! Accesses the value, being UB if none exists 304 | constexpr value_type_if_enabled &assume_value() &noexcept 305 | { 306 | if(!has_value()) 307 | { 308 | _ub(); 309 | } 310 | return *std::get_if<1>(this); 311 | } 312 | //! Accesses the error, being UB if none exists 313 | constexpr const value_type_if_enabled &assume_value() const &noexcept 314 | { 315 | if(!has_value()) 316 | { 317 | _ub(); 318 | } 319 | return *std::get_if<1>(this); 320 | } 321 | //! Accesses the error, being UB if none exists 322 | constexpr value_type_if_enabled &&assume_value() &&noexcept 323 | { 324 | if(!has_value()) 325 | { 326 | _ub(); 327 | } 328 | return std::move(*std::get_if<1>(this)); 329 | } 330 | //! Accesses the error, being UB if none exists 331 | constexpr const value_type_if_enabled &&assume_value() const &&noexcept 332 | { 333 | if(!has_value()) 334 | { 335 | _ub(); 336 | } 337 | return std::move(*std::get_if<1>(this)); 338 | } 339 | 340 | //! Accesses the error, being UB if none exists 341 | constexpr error_type &assume_error() &noexcept 342 | { 343 | if(!has_error()) 344 | { 345 | _ub(); 346 | } 347 | return *std::get_if<0>(this); 348 | } 349 | //! Accesses the error, being UB if none exists 350 | constexpr const error_type &assume_error() const &noexcept 351 | { 352 | if(!has_error()) 353 | { 354 | _ub(); 355 | } 356 | return *std::get_if<0>(this); 357 | } 358 | //! Accesses the error, being UB if none exists 359 | constexpr error_type &&assume_error() &&noexcept 360 | { 361 | if(!has_error()) 362 | { 363 | _ub(); 364 | } 365 | return std::move(*std::get_if<0>(this)); 366 | } 367 | //! Accesses the error, being UB if none exists 368 | constexpr const error_type &&assume_error() const &&noexcept 369 | { 370 | if(!has_error()) 371 | { 372 | _ub(); 373 | } 374 | return std::move(*std::get_if<0>(this)); 375 | } 376 | }; 377 | 378 | //! True if the two results compare equal. 379 | template () == std::declval())> constexpr inline bool operator==(const result &a, const result &b) noexcept 380 | { 381 | const auto &x = a._internal(); 382 | return x == b; 383 | } 384 | //! True if the two results compare unequal. 385 | template () != std::declval())> constexpr inline bool operator!=(const result &a, const result &b) noexcept 386 | { 387 | const auto &x = a._internal(); 388 | return x != b; 389 | } 390 | 391 | SYSTEM_ERROR2_NAMESPACE_END 392 | 393 | #endif 394 | #endif 395 | #endif 396 | -------------------------------------------------------------------------------- /include/status-code/status_code_gdb.py: -------------------------------------------------------------------------------- 1 | import gdb.printing 2 | import gdb 3 | import os 4 | 5 | def synthesise_gdb_value_from_string(s): 6 | '''For when you want to return a synthetic string from children()''' 7 | return gdb.Value(s + '\0').cast(gdb.lookup_type('char').pointer()) 8 | 9 | class StatusCodePrinter(object): 10 | '''Print a system_error2::status_code''' 11 | 12 | def __init__(self, val): 13 | self.val = val 14 | 15 | def children(self): 16 | s = str(self.val['_domain']) 17 | if 'posix_code_domain' in s or 'generic_code_domain' in s: 18 | yield ('msg', synthesise_gdb_value_from_string(str(self.val['_value']) + ' (' + os.strerror(int(self.val['_value'])) + ')')) 19 | yield ('domain', self.val['_domain']) 20 | yield ('value', self.val['_value']) 21 | 22 | def display_hint(self): 23 | return None 24 | 25 | def to_string(self): 26 | s = str(self.val['_domain']) 27 | if 'posix_code_domain' in s or 'generic_code_domain' in s: 28 | return str(self.val['_value']) + ' (' + os.strerror(int(self.val['_value'])) + ')' 29 | else: 30 | return self.val['_value'] 31 | 32 | def build_pretty_printer(): 33 | pp = gdb.printing.RegexpCollectionPrettyPrinter('system_error2') 34 | pp.add_printer('system_error2::status_code', '^(boost::)?system_error2::status_code<.*>$', StatusCodePrinter) 35 | return pp 36 | 37 | def register_printers(obj = None): 38 | gdb.printing.register_pretty_printer(obj, build_pretty_printer(), replace = True) 39 | 40 | register_printers(gdb.current_objfile()) 41 | -------------------------------------------------------------------------------- /include/status-code/status_error.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 - 2022 Niall Douglas (5 commits) 3 | File Created: Feb 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_STATUS_ERROR_HPP 26 | #define SYSTEM_ERROR2_STATUS_ERROR_HPP 27 | 28 | #include "status_code.hpp" 29 | 30 | #include // for std::exception 31 | 32 | SYSTEM_ERROR2_NAMESPACE_BEGIN 33 | 34 | /*! Exception type representing a thrown status_code 35 | */ 36 | template class status_error; 37 | 38 | /*! The erased type edition of status_error. 39 | */ 40 | template <> class status_error : public std::exception 41 | { 42 | protected: 43 | //! Constructs an instance. Not publicly available. 44 | status_error() = default; 45 | //! Copy constructor. Not publicly available 46 | status_error(const status_error &) = default; 47 | //! Move constructor. Not publicly available 48 | status_error(status_error &&) = default; 49 | //! Copy assignment. Not publicly available 50 | status_error &operator=(const status_error &) = default; 51 | //! Move assignment. Not publicly available 52 | status_error &operator=(status_error &&) = default; 53 | //! Destructor. Not publicly available. 54 | ~status_error() override = default; 55 | 56 | virtual const status_code &_do_code() const noexcept = 0; 57 | 58 | public: 59 | //! The type of the status domain 60 | using domain_type = void; 61 | //! The type of the status code 62 | using status_code_type = status_code; 63 | 64 | public: 65 | //! The erased status code which generated this exception instance. 66 | const status_code &code() const noexcept { return _do_code(); } 67 | }; 68 | 69 | /*! Exception type representing a thrown status_code 70 | */ 71 | template class status_error : public status_error 72 | { 73 | status_code _code; 74 | typename DomainType::string_ref _msgref; 75 | 76 | virtual const status_code &_do_code() const noexcept override final { return _code; } 77 | 78 | public: 79 | //! The type of the status domain 80 | using domain_type = DomainType; 81 | //! The type of the status code 82 | using status_code_type = status_code; 83 | 84 | //! Constructs an instance 85 | explicit status_error(status_code code) 86 | : _code(static_cast &&>(code)) 87 | , _msgref(_code.message()) 88 | { 89 | } 90 | 91 | //! Return an explanatory string 92 | virtual const char *what() const noexcept override { return _msgref.c_str(); } // NOLINT 93 | 94 | //! Returns a reference to the code 95 | const status_code_type &code() const & { return _code; } 96 | //! Returns a reference to the code 97 | status_code_type &code() & { return _code; } 98 | //! Returns a reference to the code 99 | const status_code_type &&code() const && { return _code; } 100 | //! Returns a reference to the code 101 | status_code_type &&code() && { return _code; } 102 | }; 103 | 104 | /*! Exception type representing a thrown erased status_code 105 | */ 106 | template class status_error> : public status_error 107 | { 108 | status_code> _code; 109 | typename status_code_domain::string_ref _msgref; 110 | 111 | virtual const status_code> &_do_code() const noexcept override final { return _code; } 112 | 113 | public: 114 | //! The type of the status domain 115 | using domain_type = void; 116 | //! The type of the status code 117 | using status_code_type = status_code>; 118 | 119 | //! Constructs an instance 120 | explicit status_error(status_code> code) 121 | : _code(static_cast> &&>(code)) 122 | , _msgref(_code.message()) 123 | { 124 | } 125 | 126 | //! Return an explanatory string 127 | virtual const char *what() const noexcept override { return _msgref.c_str(); } // NOLINT 128 | 129 | //! Returns a reference to the code 130 | const status_code_type &code() const & { return _code; } 131 | //! Returns a reference to the code 132 | status_code_type &code() & { return _code; } 133 | //! Returns a reference to the code 134 | const status_code_type &&code() const && { return _code; } 135 | //! Returns a reference to the code 136 | status_code_type &&code() && { return _code; } 137 | }; 138 | 139 | SYSTEM_ERROR2_NAMESPACE_END 140 | 141 | #endif 142 | -------------------------------------------------------------------------------- /include/status-code/std_error_code.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 - 2021 Niall Douglas (5 commits) 3 | File Created: Aug 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_STD_ERROR_CODE_HPP 26 | #define SYSTEM_ERROR2_STD_ERROR_CODE_HPP 27 | 28 | #ifndef SYSTEM_ERROR2_NOT_POSIX 29 | #include "posix_code.hpp" 30 | #endif 31 | 32 | #if defined(_WIN32) || defined(STANDARDESE_IS_IN_THE_HOUSE) 33 | #include "win32_code.hpp" 34 | #endif 35 | 36 | #include 37 | 38 | SYSTEM_ERROR2_NAMESPACE_BEGIN 39 | 40 | class _std_error_code_domain; 41 | //! A `status_code` representing exactly a `std::error_code` 42 | using std_error_code = status_code<_std_error_code_domain>; 43 | 44 | namespace mixins 45 | { 46 | template struct mixin : public Base 47 | { 48 | using Base::Base; 49 | 50 | //! Implicit constructor from a `std::error_code` 51 | inline mixin(std::error_code ec); 52 | 53 | //! Returns the error code category 54 | inline const std::error_category &category() const noexcept; 55 | }; 56 | } // namespace mixins 57 | 58 | 59 | /*! The implementation of the domain for `std::error_code` error codes. 60 | */ 61 | class _std_error_code_domain final : public status_code_domain 62 | { 63 | template friend class status_code; 64 | using _base = status_code_domain; 65 | using _error_code_type = std::error_code; 66 | using _error_category_type = std::error_category; 67 | 68 | std::string _name; 69 | 70 | static _base::string_ref _make_string_ref(_error_code_type c) noexcept 71 | { 72 | #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) 73 | try 74 | #endif 75 | { 76 | std::string msg = c.message(); 77 | auto *p = static_cast(malloc(msg.size() + 1)); // NOLINT 78 | if(p == nullptr) 79 | { 80 | return _base::string_ref("failed to allocate message"); 81 | } 82 | memcpy(p, msg.c_str(), msg.size() + 1); 83 | return _base::atomic_refcounted_string_ref(p, msg.size()); 84 | } 85 | #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) 86 | catch(...) 87 | { 88 | return _base::string_ref("failed to allocate message"); 89 | } 90 | #endif 91 | } 92 | 93 | public: 94 | //! The value type of the `std::error_code` code, which stores the `int` from the `std::error_code` 95 | using value_type = int; 96 | using _base::string_ref; 97 | 98 | //! Returns the error category singleton pointer this status code domain represents 99 | const _error_category_type &error_category() const noexcept 100 | { 101 | auto ptr = 0x223a160d20de97b4 ^ this->id(); 102 | return *reinterpret_cast(ptr); 103 | } 104 | 105 | //! Default constructor 106 | explicit _std_error_code_domain(const _error_category_type &category) noexcept 107 | : _base(0x223a160d20de97b4 ^ reinterpret_cast<_base::unique_id_type>(&category)) 108 | , _name("std_error_code_domain(") 109 | { 110 | _name.append(category.name()); 111 | _name.push_back(')'); 112 | } 113 | _std_error_code_domain(const _std_error_code_domain &) = default; 114 | _std_error_code_domain(_std_error_code_domain &&) = default; 115 | _std_error_code_domain &operator=(const _std_error_code_domain &) = default; 116 | _std_error_code_domain &operator=(_std_error_code_domain &&) = default; 117 | ~_std_error_code_domain() = default; 118 | 119 | static inline const _std_error_code_domain *get(_error_code_type ec); 120 | 121 | virtual string_ref name() const noexcept override { return string_ref(_name.c_str(), _name.size()); } // NOLINT 122 | 123 | virtual payload_info_t payload_info() const noexcept override 124 | { 125 | return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), 126 | (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; 127 | } 128 | 129 | protected: 130 | virtual bool _do_failure(const status_code &code) const noexcept override; 131 | virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override; 132 | virtual generic_code _generic_code(const status_code &code) const noexcept override; 133 | virtual string_ref _do_message(const status_code &code) const noexcept override; 134 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 135 | SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override; 136 | #endif 137 | }; 138 | 139 | namespace detail 140 | { 141 | extern inline _std_error_code_domain *std_error_code_domain_from_category(const std::error_category &category) 142 | { 143 | static constexpr size_t max_items = 64; 144 | static struct storage_t 145 | { 146 | std::atomic _lock; 147 | union item_t 148 | { 149 | int _init; 150 | _std_error_code_domain domain; 151 | constexpr item_t() 152 | : _init(0) 153 | { 154 | } 155 | ~item_t() {} 156 | } items[max_items]; 157 | size_t count{0}; 158 | 159 | void lock() 160 | { 161 | while(_lock.exchange(1, std::memory_order_acquire) != 0) 162 | ; 163 | } 164 | void unlock() { _lock.store(0, std::memory_order_release); } 165 | 166 | storage_t() {} 167 | ~storage_t() 168 | { 169 | lock(); 170 | for(size_t n = 0; n < count; n++) 171 | { 172 | items[n].domain.~_std_error_code_domain(); 173 | } 174 | unlock(); 175 | } 176 | _std_error_code_domain *add(const std::error_category &category) 177 | { 178 | _std_error_code_domain *ret = nullptr; 179 | lock(); 180 | for(size_t n = 0; n < count; n++) 181 | { 182 | if(items[n].domain.error_category() == category) 183 | { 184 | ret = &items[n].domain; 185 | break; 186 | } 187 | } 188 | if(ret == nullptr && count < max_items) 189 | { 190 | ret = new(std::addressof(items[count++].domain)) _std_error_code_domain(category); 191 | } 192 | unlock(); 193 | return ret; 194 | } 195 | } storage; 196 | return storage.add(category); 197 | } 198 | } // namespace detail 199 | 200 | namespace mixins 201 | { 202 | template 203 | inline mixin::mixin(std::error_code ec) 204 | : Base(typename Base::_value_type_constructor{}, _std_error_code_domain::get(ec), ec.value()) 205 | { 206 | } 207 | 208 | template inline const std::error_category &mixin::category() const noexcept 209 | { 210 | const auto &domain = static_cast(this->domain()); 211 | return domain.error_category(); 212 | }; 213 | } // namespace mixins 214 | 215 | inline const _std_error_code_domain *_std_error_code_domain::get(std::error_code ec) 216 | { 217 | auto *p = detail::std_error_code_domain_from_category(ec.category()); 218 | assert(p != nullptr); 219 | if(p == nullptr) 220 | { 221 | abort(); 222 | } 223 | return p; 224 | } 225 | 226 | 227 | inline bool _std_error_code_domain::_do_failure(const status_code &code) const noexcept 228 | { 229 | assert(code.domain() == *this); 230 | return static_cast(code).value() != 0; // NOLINT 231 | } 232 | 233 | inline bool _std_error_code_domain::_do_equivalent(const status_code &code1, const status_code &code2) const noexcept 234 | { 235 | assert(code1.domain() == *this); 236 | const auto &c1 = static_cast(code1); // NOLINT 237 | const auto &cat1 = c1.category(); 238 | // Are we comparing to another wrapped error_code? 239 | if(code2.domain() == *this) 240 | { 241 | const auto &c2 = static_cast(code2); // NOLINT 242 | const auto &cat2 = c2.category(); 243 | // If the error code categories are identical, do literal comparison 244 | if(cat1 == cat2) 245 | { 246 | return c1.value() == c2.value(); 247 | } 248 | // Otherwise fall back onto the _generic_code comparison, which uses default_error_condition() 249 | return false; 250 | } 251 | // Am I an error code with generic category? 252 | if(cat1 == std::generic_category()) 253 | { 254 | // Convert to generic code, and compare that 255 | generic_code _c1(static_cast(c1.value())); 256 | return _c1 == code2; 257 | } 258 | // Am I an error code with system category? 259 | if(cat1 == std::system_category()) 260 | { 261 | // Convert to POSIX or Win32 code, and compare that 262 | #ifdef _WIN32 263 | win32_code _c1((win32::DWORD) c1.value()); 264 | return _c1 == code2; 265 | #elif !defined(SYSTEM_ERROR2_NOT_POSIX) 266 | posix_code _c1(c1.value()); 267 | return _c1 == code2; 268 | #endif 269 | } 270 | return false; 271 | } 272 | 273 | inline generic_code _std_error_code_domain::_generic_code(const status_code &code) const noexcept 274 | { 275 | assert(code.domain() == *this); 276 | const auto &c = static_cast(code); // NOLINT 277 | // Ask my embedded error code for its mapping to std::errc, which is a subset of our generic_code errc. 278 | std::error_condition cond(c.category().default_error_condition(c.value())); 279 | if(cond.category() == std::generic_category()) 280 | { 281 | return generic_code(static_cast(cond.value())); 282 | } 283 | #if !defined(SYSTEM_ERROR2_NOT_POSIX) && !defined(_WIN32) 284 | if(cond.category() == std::system_category()) 285 | { 286 | return generic_code(static_cast(cond.value())); 287 | } 288 | #endif 289 | return errc::unknown; 290 | } 291 | 292 | inline _std_error_code_domain::string_ref _std_error_code_domain::_do_message(const status_code &code) const noexcept 293 | { 294 | assert(code.domain() == *this); 295 | const auto &c = static_cast(code); // NOLINT 296 | return _make_string_ref(_error_code_type(c.value(), c.category())); 297 | } 298 | 299 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 300 | SYSTEM_ERROR2_NORETURN inline void _std_error_code_domain::_do_throw_exception(const status_code &code) const 301 | { 302 | assert(code.domain() == *this); 303 | const auto &c = static_cast(code); // NOLINT 304 | throw std::system_error(std::error_code(c.value(), c.category())); 305 | } 306 | #endif 307 | 308 | static_assert(sizeof(std_error_code) <= sizeof(void *) * 2, "std_error_code does not fit into a system_code!"); 309 | 310 | SYSTEM_ERROR2_NAMESPACE_END 311 | 312 | // Enable implicit construction of `std_error_code` from `std::error_code`. 313 | namespace std 314 | { 315 | inline SYSTEM_ERROR2_NAMESPACE::std_error_code make_status_code(error_code c) noexcept 316 | { 317 | return SYSTEM_ERROR2_NAMESPACE::std_error_code(c); 318 | } 319 | } // namespace std 320 | 321 | #endif 322 | -------------------------------------------------------------------------------- /include/status-code/system_code.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 Niall Douglas (5 commits) 3 | File Created: Feb 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_SYSTEM_CODE_HPP 26 | #define SYSTEM_ERROR2_SYSTEM_CODE_HPP 27 | 28 | #ifndef SYSTEM_ERROR2_NOT_POSIX 29 | #include "posix_code.hpp" 30 | #else 31 | #include "quick_status_code_from_enum.hpp" 32 | #endif 33 | 34 | #if defined(_WIN32) || defined(STANDARDESE_IS_IN_THE_HOUSE) 35 | #include "nt_code.hpp" 36 | #include "win32_code.hpp" 37 | // NOT "com_code.hpp" 38 | #endif 39 | 40 | SYSTEM_ERROR2_NAMESPACE_BEGIN 41 | /*! An erased-mutable status code suitably large for all the system codes 42 | which can be returned on this system. 43 | 44 | For Windows, these might be: 45 | 46 | - `com_code` (`HRESULT`) [you need to include "com_code.hpp" explicitly for this] 47 | - `nt_code` (`LONG`) 48 | - `win32_code` (`DWORD`) 49 | 50 | For POSIX, `posix_code` is possible. 51 | 52 | You are guaranteed that `system_code` can be transported by the compiler 53 | in exactly two CPU registers. 54 | */ 55 | using system_code = erased_status_code; 56 | 57 | #ifndef NDEBUG 58 | static_assert(sizeof(system_code) == 2 * sizeof(void *), "system_code is not exactly two pointers in size!"); 59 | static_assert(traits::is_move_bitcopying::value, "system_code is not move bitcopying!"); 60 | #endif 61 | 62 | SYSTEM_ERROR2_NAMESPACE_END 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /include/status-code/system_code_from_exception.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 Niall Douglas (5 commits) 3 | File Created: June 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_SYSTEM_CODE_FROM_EXCEPTION_HPP 26 | #define SYSTEM_ERROR2_SYSTEM_CODE_FROM_EXCEPTION_HPP 27 | 28 | #include "system_code.hpp" 29 | 30 | #include "status_error.hpp" 31 | 32 | #include // for exception_ptr 33 | #include // for the exception types 34 | #include // for std::system_error 35 | 36 | SYSTEM_ERROR2_NAMESPACE_BEGIN 37 | 38 | /*! A utility function which returns the closest matching system_code to a supplied 39 | exception ptr. 40 | */ 41 | inline system_code system_code_from_exception(std::exception_ptr &&ep = std::current_exception(), 42 | system_code not_matched = generic_code(errc::resource_unavailable_try_again)) noexcept 43 | { 44 | if(!ep) 45 | { 46 | return generic_code(errc::success); 47 | } 48 | try 49 | { 50 | try 51 | { 52 | std::rethrow_exception(ep); 53 | } 54 | catch(const status_error &e) 55 | { 56 | try 57 | { 58 | system_code erased(in_place, e.code()); 59 | if(!erased.empty()) 60 | { 61 | return erased; 62 | } 63 | } 64 | catch(...) 65 | { 66 | // Source status code's do_erased_copy() routine refused to copy the original 67 | // Process instead as if the source were not a status_error 68 | } 69 | throw; 70 | } 71 | catch(...) 72 | { 73 | throw; 74 | } 75 | } 76 | catch(const std::invalid_argument & /*unused*/) 77 | { 78 | ep = std::exception_ptr(); 79 | return generic_code(errc::invalid_argument); 80 | } 81 | catch(const std::domain_error & /*unused*/) 82 | { 83 | ep = std::exception_ptr(); 84 | return generic_code(errc::argument_out_of_domain); 85 | } 86 | catch(const std::length_error & /*unused*/) 87 | { 88 | ep = std::exception_ptr(); 89 | return generic_code(errc::argument_list_too_long); 90 | } 91 | catch(const std::out_of_range & /*unused*/) 92 | { 93 | ep = std::exception_ptr(); 94 | return generic_code(errc::result_out_of_range); 95 | } 96 | catch(const std::logic_error & /*unused*/) /* base class for this group */ 97 | { 98 | ep = std::exception_ptr(); 99 | return generic_code(errc::invalid_argument); 100 | } 101 | catch(const std::system_error &e) /* also catches ios::failure */ 102 | { 103 | ep = std::exception_ptr(); 104 | if(e.code().category() == std::generic_category()) 105 | { 106 | return generic_code(static_cast(static_cast(e.code().value()))); 107 | } 108 | if(e.code().category() == std::system_category()) 109 | { 110 | #ifdef _WIN32 111 | return win32_code(e.code().value()); 112 | #else 113 | #ifndef SYSTEM_ERROR2_NOT_POSIX 114 | return posix_code(e.code().value()); 115 | #else 116 | return generic_code(static_cast(e.code().value())); 117 | #endif 118 | #endif 119 | } 120 | // Don't know this error code category, can't wrap it into std_error_code 121 | // as its payload won't fit into system_code, so fall through. 122 | } 123 | catch(const std::overflow_error & /*unused*/) 124 | { 125 | ep = std::exception_ptr(); 126 | return generic_code(errc::value_too_large); 127 | } 128 | catch(const std::range_error & /*unused*/) 129 | { 130 | ep = std::exception_ptr(); 131 | return generic_code(errc::result_out_of_range); 132 | } 133 | catch(const std::runtime_error & /*unused*/) /* base class for this group */ 134 | { 135 | ep = std::exception_ptr(); 136 | return generic_code(errc::resource_unavailable_try_again); 137 | } 138 | catch(const std::bad_alloc & /*unused*/) 139 | { 140 | ep = std::exception_ptr(); 141 | return generic_code(errc::not_enough_memory); 142 | } 143 | catch(...) 144 | { 145 | } 146 | return not_matched; 147 | } 148 | 149 | SYSTEM_ERROR2_NAMESPACE_END 150 | 151 | #endif 152 | -------------------------------------------------------------------------------- /include/status-code/system_error2.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 Niall Douglas (5 commits) 3 | File Created: Feb 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_HPP 26 | #define SYSTEM_ERROR2_HPP 27 | 28 | #include "error.hpp" 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/status-code/win32_code.hpp: -------------------------------------------------------------------------------- 1 | /* Proposed SG14 status_code 2 | (C) 2018 Niall Douglas (5 commits) 3 | File Created: Feb 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifndef SYSTEM_ERROR2_WIN32_CODE_HPP 26 | #define SYSTEM_ERROR2_WIN32_CODE_HPP 27 | 28 | #if !defined(_WIN32) && !defined(STANDARDESE_IS_IN_THE_HOUSE) 29 | #error This file should only be included on Windows 30 | #endif 31 | 32 | #include "quick_status_code_from_enum.hpp" 33 | 34 | #if defined(_MSC_VER) && !defined(__clang__) 35 | #pragma warning(push) 36 | #pragma warning(disable : 6326) // constant comparison 37 | #endif 38 | 39 | SYSTEM_ERROR2_NAMESPACE_BEGIN 40 | 41 | //! \exclude 42 | namespace win32 43 | { 44 | #ifdef __MINGW32__ 45 | extern "C" 46 | { 47 | #endif 48 | // A Win32 DWORD 49 | using DWORD = unsigned long; 50 | // Used to retrieve the current Win32 error code 51 | extern DWORD __stdcall GetLastError(); 52 | // Used to retrieve a locale-specific message string for some error code 53 | extern DWORD __stdcall FormatMessageW(DWORD dwFlags, const void *lpSource, DWORD dwMessageId, DWORD dwLanguageId, wchar_t *lpBuffer, DWORD nSize, 54 | void /*va_list*/ *Arguments); 55 | // Converts UTF-16 message string to UTF-8 56 | extern int __stdcall WideCharToMultiByte(unsigned int CodePage, DWORD dwFlags, const wchar_t *lpWideCharStr, int cchWideChar, char *lpMultiByteStr, 57 | int cbMultiByte, const char *lpDefaultChar, int *lpUsedDefaultChar); 58 | #ifdef __MINGW32__ 59 | } 60 | #else 61 | #pragma comment(lib, "kernel32.lib") 62 | #if(defined(__x86_64__) || defined(_M_X64)) || (defined(__aarch64__) || defined(_M_ARM64)) 63 | #pragma comment(linker, "/alternatename:?GetLastError@win32@system_error2@@YAKXZ=GetLastError") 64 | #pragma comment(linker, "/alternatename:?FormatMessageW@win32@system_error2@@YAKKPEBXKKPEA_WKPEAX@Z=FormatMessageW") 65 | #pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@system_error2@@YAHIKPEB_WHPEADHPEBDPEAH@Z=WideCharToMultiByte") 66 | #elif defined(__x86__) || defined(_M_IX86) || defined(__i386__) 67 | #pragma comment(linker, "/alternatename:?GetLastError@win32@system_error2@@YGKXZ=_GetLastError@0") 68 | #pragma comment(linker, "/alternatename:?FormatMessageW@win32@system_error2@@YGKKPBXKKPA_WKPAX@Z=_FormatMessageW@28") 69 | #pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@system_error2@@YGHIKPB_WHPADHPBDPAH@Z=_WideCharToMultiByte@32") 70 | #elif defined(__arm__) || defined(_M_ARM) 71 | #pragma comment(linker, "/alternatename:?GetLastError@win32@system_error2@@YAKXZ=GetLastError") 72 | #pragma comment(linker, "/alternatename:?FormatMessageW@win32@system_error2@@YAKKPBXKKPA_WKPAX@Z=FormatMessageW") 73 | #pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@system_error2@@YAHIKPB_WHPADHPBDPAH@Z=WideCharToMultiByte") 74 | #else 75 | #error Unknown architecture 76 | #endif 77 | #endif 78 | } // namespace win32 79 | 80 | class _win32_code_domain; 81 | class _com_code_domain; 82 | //! (Windows only) A Win32 error code, those returned by `GetLastError()`. 83 | using win32_code = status_code<_win32_code_domain>; 84 | //! (Windows only) A specialisation of `status_error` for the Win32 error code domain. 85 | using win32_error = status_error<_win32_code_domain>; 86 | 87 | namespace mixins 88 | { 89 | template struct mixin : public Base 90 | { 91 | using Base::Base; 92 | 93 | //! Returns a `win32_code` for the current value of `GetLastError()`. 94 | static inline win32_code current() noexcept; 95 | }; 96 | } // namespace mixins 97 | 98 | /*! (Windows only) The implementation of the domain for Win32 error codes, those returned by `GetLastError()`. 99 | */ 100 | class _win32_code_domain : public status_code_domain 101 | { 102 | template friend class status_code; 103 | friend class _com_code_domain; 104 | using _base = status_code_domain; 105 | static int _win32_code_to_errno(win32::DWORD c) 106 | { 107 | switch(c) 108 | { 109 | case 0: 110 | return 0; 111 | #include "detail/win32_code_to_generic_code.ipp" 112 | } 113 | return -1; 114 | } 115 | //! Construct from a Win32 error code 116 | static _base::string_ref _make_string_ref(win32::DWORD c) noexcept 117 | { 118 | wchar_t buffer[32768]; 119 | win32::DWORD wlen = 120 | win32::FormatMessageW(0x00001000 /*FORMAT_MESSAGE_FROM_SYSTEM*/ | 0x00000200 /*FORMAT_MESSAGE_IGNORE_INSERTS*/, nullptr, c, 0, buffer, 32768, nullptr); 121 | size_t allocation = wlen + (wlen >> 1); 122 | win32::DWORD bytes; 123 | if(wlen == 0) 124 | { 125 | return _base::string_ref("failed to get message from system"); 126 | } 127 | for(;;) 128 | { 129 | auto *p = static_cast(malloc(allocation)); // NOLINT 130 | if(p == nullptr) 131 | { 132 | return _base::string_ref("failed to get message from system"); 133 | } 134 | bytes = win32::WideCharToMultiByte(65001 /*CP_UTF8*/, 0, buffer, (int) (wlen + 1), p, (int) allocation, nullptr, nullptr); 135 | if(bytes != 0) 136 | { 137 | char *end = strchr(p, 0); 138 | while(end[-1] == 10 || end[-1] == 13) 139 | { 140 | --end; 141 | } 142 | *end = 0; // NOLINT 143 | return _base::atomic_refcounted_string_ref(p, end - p); 144 | } 145 | free(p); // NOLINT 146 | if(win32::GetLastError() == 0x7a /*ERROR_INSUFFICIENT_BUFFER*/) 147 | { 148 | allocation += allocation >> 2; 149 | continue; 150 | } 151 | return _base::string_ref("failed to get message from system"); 152 | } 153 | } 154 | 155 | public: 156 | //! The value type of the win32 code, which is a `win32::DWORD` 157 | using value_type = win32::DWORD; 158 | using _base::string_ref; 159 | 160 | public: 161 | //! Default constructor 162 | constexpr explicit _win32_code_domain(typename _base::unique_id_type id = 0x8cd18ee72d680f1b) noexcept 163 | : _base(id) 164 | { 165 | } 166 | _win32_code_domain(const _win32_code_domain &) = default; 167 | _win32_code_domain(_win32_code_domain &&) = default; 168 | _win32_code_domain &operator=(const _win32_code_domain &) = default; 169 | _win32_code_domain &operator=(_win32_code_domain &&) = default; 170 | ~_win32_code_domain() = default; 171 | 172 | //! Constexpr singleton getter. Returns the constexpr win32_code_domain variable. 173 | static inline constexpr const _win32_code_domain &get(); 174 | 175 | virtual string_ref name() const noexcept override { return string_ref("win32 domain"); } // NOLINT 176 | 177 | virtual payload_info_t payload_info() const noexcept override 178 | { 179 | return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), 180 | (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; 181 | } 182 | 183 | protected: 184 | virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT 185 | { 186 | assert(code.domain() == *this); 187 | return static_cast(code).value() != 0; // NOLINT 188 | } 189 | virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override // NOLINT 190 | { 191 | assert(code1.domain() == *this); 192 | const auto &c1 = static_cast(code1); // NOLINT 193 | if(code2.domain() == *this) 194 | { 195 | const auto &c2 = static_cast(code2); // NOLINT 196 | return c1.value() == c2.value(); 197 | } 198 | if(code2.domain() == generic_code_domain) 199 | { 200 | const auto &c2 = static_cast(code2); // NOLINT 201 | if(static_cast(c2.value()) == _win32_code_to_errno(c1.value())) 202 | { 203 | return true; 204 | } 205 | } 206 | return false; 207 | } 208 | virtual generic_code _generic_code(const status_code &code) const noexcept override // NOLINT 209 | { 210 | assert(code.domain() == *this); 211 | const auto &c = static_cast(code); // NOLINT 212 | return generic_code(static_cast(_win32_code_to_errno(c.value()))); 213 | } 214 | virtual string_ref _do_message(const status_code &code) const noexcept override // NOLINT 215 | { 216 | assert(code.domain() == *this); 217 | const auto &c = static_cast(code); // NOLINT 218 | return _make_string_ref(c.value()); 219 | } 220 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) 221 | SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT 222 | { 223 | assert(code.domain() == *this); 224 | const auto &c = static_cast(code); // NOLINT 225 | throw status_error<_win32_code_domain>(c); 226 | } 227 | #endif 228 | }; 229 | //! (Windows only) A constexpr source variable for the win32 code domain, which is that of `GetLastError()` (Windows). Returned by 230 | //! `_win32_code_domain::get()`. 231 | constexpr _win32_code_domain win32_code_domain; 232 | inline constexpr const _win32_code_domain &_win32_code_domain::get() 233 | { 234 | return win32_code_domain; 235 | } 236 | 237 | namespace mixins 238 | { 239 | template inline win32_code mixin::current() noexcept 240 | { 241 | return win32_code(win32::GetLastError()); 242 | } 243 | } // namespace mixins 244 | 245 | SYSTEM_ERROR2_NAMESPACE_END 246 | 247 | #if defined(_MSC_VER) && !defined(__clang__) 248 | #pragma warning(pop) 249 | #endif 250 | 251 | #endif 252 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Please go to doc/html/index.html 4 | 5 | -------------------------------------------------------------------------------- /make_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf doc/html/* 3 | cd doc/html 4 | ../../../standardese --output.format=commonmark_html --input.blacklist_namespace=detail -DSTANDARDESE_IS_IN_THE_HOUSE=1 -D__cplusplus=202000L ../../include/*.hpp 5 | cp ../../Readme.md index.md 6 | cat standardese_entities.md >> index.md 7 | rm standardese_*.md 8 | cat ../custom_domain_worked_example.md >> index.md 9 | for f in *.md 10 | do 11 | echo "Rendering $f to html ..." 12 | echo ' 13 | 14 | 15 | 16 | ' > ${f%.md}.html 17 | echo ${f%.md} >> ${f%.md}.html 18 | echo ' 19 | 20 | 21 | ' >> ${f%.md}.html 22 | python -m markdown $f -x markdown.extensions.fenced_code >> ${f%.md}.html 23 | echo ' 24 | ' >> ${f%.md}.html 25 | sed -i -e 's/standardese_entities\.md/index.md/g' ${f%.md}.html 26 | sed -i -e 's/\.md/.html/g' ${f%.md}.html 27 | done 28 | rm *.md 29 | cd ../.. 30 | -------------------------------------------------------------------------------- /single-header/Readme.md: -------------------------------------------------------------------------------- 1 | Herein lie the following single header editions of https://wg21.link/P1028 `status_code`: 2 | 3 |
4 |
system_error2.hpp
5 |
An inclusion of the following headers: 6 | 7 |
    8 |
  • config.hpp 9 |
  • error.hpp 10 |
  • errored_status_code.hpp 11 |
  • generic_code.hpp 12 |
  • nested_status_code.hpp 13 |
  • posix_code.hpp 14 |
  • status_code.hpp 15 |
  • status_code_domain.hpp 16 |
  • status_error.hpp 17 |
  • system_code.hpp 18 |
19 |
    20 |
  • nt_code.hpp (used on Windows only, completely omitted in system_error2-nowindows.hpp) 21 |
  • win32_code.hpp (used on Windows only, completely omitted in system_error2-nowindows.hpp) 22 |
23 | 24 | The following headers are specifically NOT included: 25 | 26 |
    27 |
  • com_code.hpp (drags in COM) 28 |
  • iostream_support.hpp (drags in <iostream>) 29 |
  • std_error_code.hpp (drags in <system_error> and its many, many dependencies) 30 |
  • system_code_from_exception.hpp (drags in <system_error> and its many, many dependencies) 31 |
32 | 33 | This bisection is to give an absolute minimum compile time 34 | impact edition of this library. See https://github.com/ned14/stl-header-heft. 35 | The bisection above causes the inclusion of the following system headers: 36 | 37 |
    38 |
  • <atomic> 39 |
  • <cassert> 40 |
  • <cerrno> 41 |
  • <cstddef> 42 |
  • <cstdlib> 43 |
  • <cstring> 44 |
  • <exception> 45 |
  • <initializer_list> 46 |
  • <new> 47 |
  • <type_traits> 48 |
  • <unistd.h> (Mac OS only) 49 |
  • <utility> (C++ 17 or later only) 50 |
51 | 52 |
53 |
54 | -------------------------------------------------------------------------------- /test/issue0050.cpp: -------------------------------------------------------------------------------- 1 | /* Regression testing 2 | (C) 2022 Niall Douglas (5 commits) 3 | File Created: Oct 2022 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #include "status-code/iostream_support.hpp" 26 | #include "status-code/nested_status_code.hpp" 27 | #include "status-code/system_error2.hpp" 28 | 29 | #include 30 | #include 31 | 32 | #define CHECK(expr) \ 33 | if(!(expr)) \ 34 | { \ 35 | fprintf(stderr, #expr " failed at line %d\n", __LINE__); \ 36 | retcode = 1; \ 37 | } 38 | 39 | int main() 40 | { 41 | using namespace SYSTEM_ERROR2_NAMESPACE; 42 | int retcode = 0; 43 | #ifdef _MSC_VER 44 | _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 45 | #endif 46 | 47 | auto do_evil = [](posix_code c) -> system_code 48 | { 49 | auto p = make_nested_status_code(c); 50 | system_code c1 = std::move(p); 51 | return p; // moved from, so empty 52 | }; 53 | auto sc = do_evil(posix_code(int(std::errc::bad_file_descriptor))); 54 | auto msg = sc.message(); 55 | std::cout << msg << std::endl; 56 | CHECK(strstr(msg.c_str(), "(empty)") != nullptr); 57 | return retcode; 58 | } -------------------------------------------------------------------------------- /test/issue0056.cpp: -------------------------------------------------------------------------------- 1 | /* Regression testing 2 | (C) 2023 Niall Douglas (5 commits) 3 | File Created: Oct 2022 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #define DONT_DEFINE_MAIN 1 26 | #include "../example/thrown_exception.cpp" 27 | 28 | #define CHECK(expr) \ 29 | if(!(expr)) \ 30 | { \ 31 | fprintf(stderr, #expr " failed at line %d\n", __LINE__); \ 32 | retcode = 1; \ 33 | } 34 | 35 | void throw_something() 36 | { 37 | throw std::logic_error("oops"); 38 | } 39 | 40 | SYSTEM_ERROR2_NAMESPACE::system_code produce_error() 41 | { 42 | using namespace SYSTEM_ERROR2_NAMESPACE; 43 | try 44 | { 45 | throw_something(); 46 | } 47 | catch(std::exception &e) 48 | { 49 | printf("Exception has message %s\n", e.what()); 50 | auto eptr = std::current_exception(); 51 | thrown_exception_code tec = make_status_code(eptr); 52 | return tec; 53 | } 54 | return errc::success; 55 | } 56 | 57 | int main() 58 | { 59 | int retcode = 0; 60 | #ifdef _MSC_VER 61 | _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 62 | #endif 63 | 64 | using namespace SYSTEM_ERROR2_NAMESPACE; 65 | auto sc = produce_error(); 66 | auto msg = sc.message(); 67 | CHECK(strcmp(msg.c_str(), "oops") == 0); 68 | printf("Thrown exception code has message %s\n", sc.message().c_str()); 69 | return retcode; 70 | } 71 | -------------------------------------------------------------------------------- /test/p0709a.cpp: -------------------------------------------------------------------------------- 1 | #include "status-code/system_error2.hpp" 2 | 3 | #include // for INT_MAX 4 | 5 | // status_code> 6 | using error = SYSTEM_ERROR2_NAMESPACE::error; 7 | 8 | enum class arithmetic_errc 9 | { 10 | success, 11 | divide_by_zero, 12 | integer_divide_overflows, 13 | not_integer_division 14 | }; 15 | 16 | class _arithmetic_errc_domain; 17 | using arithmetic_errc_error = SYSTEM_ERROR2_NAMESPACE::status_code<_arithmetic_errc_domain>; 18 | 19 | class _arithmetic_errc_domain : public SYSTEM_ERROR2_NAMESPACE::status_code_domain 20 | { 21 | using _base = SYSTEM_ERROR2_NAMESPACE::status_code_domain; 22 | 23 | public: 24 | using value_type = arithmetic_errc; 25 | 26 | constexpr explicit _arithmetic_errc_domain(typename _base::unique_id_type id = 0x290f170194f0c6c7) noexcept 27 | : _base(id) 28 | { 29 | } 30 | static inline constexpr const _arithmetic_errc_domain &get(); 31 | 32 | virtual _base::string_ref name() const noexcept override final // NOLINT 33 | { 34 | static string_ref v("arithmetic error domain"); 35 | return v; // NOLINT 36 | } 37 | virtual payload_info_t payload_info() const noexcept override final { return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)}; } 38 | 39 | protected: 40 | virtual bool _do_failure(const SYSTEM_ERROR2_NAMESPACE::status_code &code) const noexcept override final 41 | { 42 | assert(code.domain() == *this); 43 | const auto &c1 = static_cast(code); // NOLINT 44 | return c1.value() != arithmetic_errc::success; 45 | } 46 | virtual bool _do_equivalent(const SYSTEM_ERROR2_NAMESPACE::status_code &code1, const SYSTEM_ERROR2_NAMESPACE::status_code &code2) const noexcept override final { return false; } 47 | virtual SYSTEM_ERROR2_NAMESPACE::generic_code _generic_code(const SYSTEM_ERROR2_NAMESPACE::status_code &code) const noexcept override final { return {}; } 48 | virtual _base::string_ref _do_message(const SYSTEM_ERROR2_NAMESPACE::status_code &code) const noexcept override final // NOLINT 49 | { 50 | assert(code.domain() == *this); 51 | const auto &c1 = static_cast(code); // NOLINT 52 | switch(c1.value()) 53 | { 54 | case arithmetic_errc::success: 55 | return _base::string_ref("success"); 56 | case arithmetic_errc::divide_by_zero: 57 | return _base::string_ref("divide by zero"); 58 | case arithmetic_errc::integer_divide_overflows: 59 | return _base::string_ref("integer divide overflows"); 60 | case arithmetic_errc::not_integer_division: 61 | return _base::string_ref("not integer division"); 62 | } 63 | return _base::string_ref("unknown"); 64 | } 65 | SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const SYSTEM_ERROR2_NAMESPACE::status_code &code) const override final { abort(); } 66 | }; 67 | 68 | constexpr _arithmetic_errc_domain arithmetic_errc_domain; 69 | inline constexpr const _arithmetic_errc_domain &_arithmetic_errc_domain::get() 70 | { 71 | return arithmetic_errc_domain; 72 | } 73 | 74 | // Tell status code about the available implicit conversion 75 | inline arithmetic_errc_error make_status_code(arithmetic_errc e) 76 | { 77 | return arithmetic_errc_error(SYSTEM_ERROR2_NAMESPACE::in_place, e); 78 | } 79 | 80 | 81 | error safe_divide(int i, int j) 82 | { 83 | if(j == 0) 84 | return arithmetic_errc::divide_by_zero; 85 | if(i == INT_MIN && j == -1) 86 | return arithmetic_errc::integer_divide_overflows; 87 | if(i % j != 0) 88 | return arithmetic_errc::not_integer_division; 89 | return arithmetic_errc::success; 90 | } 91 | 92 | int caller2(int i, int j) 93 | { 94 | auto e = safe_divide(i, j); 95 | if(e == arithmetic_errc::divide_by_zero) 96 | return 0; 97 | if(e == arithmetic_errc::not_integer_division) 98 | return i / j; // ignore 99 | if(e == arithmetic_errc::integer_divide_overflows) 100 | return INT_MIN; 101 | return 0; 102 | } 103 | 104 | int main() 105 | { 106 | return caller2(5, 6); 107 | } -------------------------------------------------------------------------------- /utils/generate-tables.cpp: -------------------------------------------------------------------------------- 1 | /* Generate mapping tables of NT kernel and Win32 error codes 2 | (C) 2017 Niall Douglas (5 commits) 3 | File Created: July 2017 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #ifdef _WIN32 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | /* NTSTATUS (LONG) bit format: 33 | // 27 16 15 0 34 | // +---+-+-+-----------------------+-------------------------------+ 35 | // |Sev|C|R| Facility | Code | 36 | // +---+-+-+-----------------------+-------------------------------+ 37 | // 38 | // where 39 | // 40 | // Sev - is the severity code (bits 31, 30) 41 | // 42 | // 00 - Success 43 | // 01 - Informational 44 | // 10 - Warning 45 | // 11 - Error 46 | // 47 | // C - is the Customer code flag (bit 29) 48 | // 49 | // R - is a reserved bit (bit 28) 50 | // 51 | // Facility - is the facility code (bits 27-16) 52 | // 53 | // Code - is the facility's status code (bits 15-0) 54 | 55 | So anything with bit 31 set is an error, they will be prefixed with 0x8 or 0xC. 56 | 57 | Highest system facility warning appears to be 0x80000030L 58 | Highest system facility error appears to be 0xC000A2A4L 59 | */ 60 | 61 | // clang-format off 62 | static constexpr std::pair posixmap[] = { 63 | { 1,"EPERM" }, 64 | { 2,"ENOENT" }, 65 | { 3,"ESRCH" }, 66 | { 4,"EINTR" }, 67 | { 5,"EIO" }, 68 | { 6,"ENXIO" }, 69 | { 7,"E2BIG" }, 70 | { 8,"ENOEXEC" }, 71 | { 9,"EBADF" }, 72 | { 10,"ECHILD" }, 73 | { 11,"EAGAIN" }, 74 | { 12,"ENOMEM" }, 75 | { 13,"EACCES" }, 76 | { 14,"EFAULT" }, 77 | { 16,"EBUSY" }, 78 | { 17,"EEXIST" }, 79 | { 18,"EXDEV" }, 80 | { 19,"ENODEV" }, 81 | { 20,"ENOTDIR" }, 82 | { 21,"EISDIR" }, 83 | { 22,"EINVAL" }, 84 | { 23,"ENFILE" }, 85 | { 24,"EMFILE" }, 86 | { 25,"ENOTTY" }, 87 | { 27,"EFBIG" }, 88 | { 28,"ENOSPC" }, 89 | { 29,"ESPIPE" }, 90 | { 30,"EROFS" }, 91 | { 31,"EMLINK" }, 92 | { 32,"EPIPE" }, 93 | { 33,"EDOM" }, 94 | { 34,"ERANGE" }, 95 | { 36,"EDEADLK" }, 96 | { 38,"ENAMETOOLONG" }, 97 | { 39,"ENOLCK" }, 98 | { 40,"ENOSYS" }, 99 | { 41,"ENOTEMPTY" }, 100 | { 42,"EILSEQ" }, 101 | { 80,"STRUNCATE" }, 102 | { 100,"EADDRINUSE" }, 103 | { 101,"EADDRNOTAVAIL" }, 104 | { 102,"EAFNOSUPPORT" }, 105 | { 103,"EALREADY" }, 106 | { 104,"EBADMSG" }, 107 | { 105,"ECANCELED" }, 108 | { 106,"ECONNABORTED" }, 109 | { 107,"ECONNREFUSED" }, 110 | { 108,"ECONNRESET" }, 111 | { 109,"EDESTADDRREQ" }, 112 | { 110,"EHOSTUNREACH" }, 113 | { 111,"EIDRM" }, 114 | { 112,"EINPROGRESS" }, 115 | { 113,"EISCONN" }, 116 | { 114,"ELOOP" }, 117 | { 115,"EMSGSIZE" }, 118 | { 116,"ENETDOWN" }, 119 | { 117,"ENETRESET" }, 120 | { 118,"ENETUNREACH" }, 121 | { 119,"ENOBUFS" }, 122 | { 120,"ENODATA" }, 123 | { 121,"ENOLINK" }, 124 | { 122,"ENOMSG" }, 125 | { 123,"ENOPROTOOPT" }, 126 | { 124,"ENOSR" }, 127 | { 125,"ENOSTR" }, 128 | { 126,"ENOTCONN" }, 129 | { 127,"ENOTRECOVERABLE" }, 130 | { 128,"ENOTSOCK" }, 131 | { 129,"ENOTSUP" }, 132 | { 130,"EOPNOTSUPP" }, 133 | { 131,"EOTHER" }, 134 | { 132,"EOVERFLOW" }, 135 | { 133,"EOWNERDEAD" }, 136 | { 134,"EPROTO" }, 137 | { 135,"EPROTONOSUPPORT" }, 138 | { 136,"EPROTOTYPE" }, 139 | { 137,"ETIME" }, 140 | { 138,"ETIMEDOUT" }, 141 | { 139,"ETXTBSY" }, 142 | { 140,"EWOULDBLOCK" }, 143 | }; 144 | // clang-format on 145 | 146 | static constexpr std::pair inputs[] = { 147 | // 148 | //{0x00000000L, 0x0000ffffL}, // 149 | //{0x40000000L, 0x4000ffffL}, // 150 | {0x80000001L, 0x8000ffffL}, // 151 | {0x80100001L, 0x801fffffL}, // 152 | {0xC0000001L, 0xC000ffffL} // 153 | }; 154 | 155 | typedef NTSTATUS(NTAPI *RtlUnicodeToUTF8N_t)(_Out_ PCHAR UTF8StringDestination, _In_ ULONG UTF8StringMaxByteCount, _Out_ PULONG UTF8StringActualByteCount, _In_ PCWCH UnicodeStringSource, _In_ ULONG UnicodeStringByteCount); 156 | static RtlUnicodeToUTF8N_t RtlUnicodeToUTF8N; 157 | 158 | static inline DWORD win32_error_from_nt_status(NTSTATUS ntstatus) 159 | { 160 | DWORD br; 161 | OVERLAPPED o; 162 | 163 | SetLastError(0); 164 | o.Internal = ntstatus; 165 | o.InternalHigh = 0; 166 | o.Offset = 0; 167 | o.OffsetHigh = 0; 168 | o.hEvent = 0; 169 | GetOverlappedResult(NULL, &o, &br, FALSE); 170 | return GetLastError(); 171 | } 172 | 173 | int main() 174 | { 175 | std::ofstream nt_win32("../include/detail/nt_code_to_win32_code.ipp"); 176 | std::ofstream nt_generic("../include/detail/nt_code_to_generic_code.ipp"); 177 | for(auto &input : inputs) 178 | { 179 | for(NTSTATUS code = input.first; code < input.second; code++) 180 | { 181 | DWORD win32code = win32_error_from_nt_status(code); 182 | if(win32code == code) 183 | win32code = 0; 184 | if(win32code != 0 && win32code != 0x13d) 185 | { 186 | std::error_code ec(win32code, std::system_category()); 187 | std::error_condition ecnd = ec.default_error_condition(); 188 | const char *errc = "0"; 189 | if(ecnd.category() == std::generic_category()) 190 | { 191 | for(auto &p : posixmap) 192 | { 193 | if(p.first == ecnd.value()) 194 | errc = p.second; 195 | } 196 | } 197 | if(win32code < 0xffff) 198 | nt_win32 << "case 0x" << std::hex << (unsigned) code << ": return 0x" << win32code << ";\n"; 199 | if(errc[0] != '0') 200 | nt_generic << "case 0x" << std::hex << (unsigned) code << ": return " << errc << ";\n"; 201 | } 202 | } 203 | } 204 | 205 | std::ofstream win32_generic("../include/detail/win32_code_to_generic_code.ipp"); 206 | for(DWORD win32code = 0; win32code <= 0xffff; win32code++) 207 | { 208 | std::error_code ec(win32code, std::system_category()); 209 | std::error_condition ecnd = ec.default_error_condition(); 210 | const char *errc = "0"; 211 | if(ecnd.category() == std::generic_category()) 212 | { 213 | for(auto &p : posixmap) 214 | { 215 | if(p.first == ecnd.value()) 216 | errc = p.second; 217 | } 218 | } 219 | if(errc[0] != '0') 220 | win32_generic << "case 0x" << std::hex << win32code << ": return " << errc << ";\n"; 221 | /* Omissions from the C++ 11 STL mapping */ 222 | else if(win32code == 0x57 /*ERROR_INVALID_PARAMETER*/) 223 | win32_generic << "case 0x" << std::hex << win32code << ": return EINVAL;\n"; 224 | } 225 | } 226 | 227 | #else 228 | int main() 229 | { 230 | std::cerr << "This program can only work on Windows" << std::endl; 231 | return 1; 232 | } 233 | #endif -------------------------------------------------------------------------------- /wg21/file_io_error.cpp: -------------------------------------------------------------------------------- 1 | /* Example use of std::error implicit conversion 2 | (C) 2018 Niall Douglas (5 commits) 3 | File Created: Sept 2018 4 | 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License in the accompanying file 9 | Licence.txt or at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | 20 | Distributed under the Boost Software License, Version 1.0. 21 | (See accompanying file Licence.txt or copy at 22 | http://www.boost.org/LICENSE_1_0.txt) 23 | */ 24 | 25 | #define _CRT_SECURE_NO_WARNINGS 26 | #include // for sprintf 27 | #include // for std::move 28 | 29 | #include "status-code/nested_status_code.hpp" 30 | #include "status-code/system_error2.hpp" 31 | 32 | 33 | using namespace SYSTEM_ERROR2_NAMESPACE; 34 | 35 | /* This code is to prove that proposed P1028 `std::error` need not be more 36 | than two CPU registers in size, yet it can still transport arbitrary 37 | payload. 38 | 39 | We firstly will define a custom code domain for a `file_io_error` code whose 40 | value type carries __FILE__ and __LINE__ (i.e. `source_location`) in 41 | addition to the cause of the error. This is what one would expect end 42 | users to do in order to define a specialised failure type local to 43 | a specific piece of code, or library. In our case, `file_io_error` 44 | will be lightweight and deterministic by being trivially copyable. 45 | 46 | We secondly will tell the type system that an implicit conversion 47 | between `file_io_error` and `error` exists via `make_nested_status_code()`. 48 | This bundles the `file_io_error` instance into dynamically allocated 49 | memory, and returns an `error` instance referring to that. 50 | 51 | Thus if under P0709 a function with `throws(file_io_error)` is called 52 | by a function with merely `throws(std::error)`, a `std::error` can be 53 | **implicitly** constructed by the compiler from the `file_io_error`. I 54 | prove this in code below (see end). 55 | */ 56 | 57 | 58 | /********************** Example boilerplate begins ***********************/ 59 | 60 | // To define a `file_io_error` which participates in the P1028 world 61 | // of `std::error`, we must first declare, then define, a custom code 62 | // domain which extends `posix_code` (the std error coding for POSIX 63 | // failures). The following is fairly standard boilerplate for defining 64 | // a custom code domain. It is analogous to defining a custom `std::error_category`. 65 | 66 | class _file_io_error_domain; 67 | // We define `file_io_error` to be the status code whose domain is `_file_io_error_domain`. 68 | using file_io_error = status_code<_file_io_error_domain>; 69 | 70 | // Now we define `_file_io_error_domain`. 71 | class _file_io_error_domain : public posix_code::domain_type 72 | { 73 | using _base = typename posix_code::domain_type; 74 | 75 | public: 76 | // This is the value type for `file_io_error`. We add line number and source file path. 77 | struct value_type 78 | { 79 | typename posix_code::value_type errcode; // from POSIX, as we inherit from _posix_code_domain 80 | 81 | // Our additional payload 82 | int lineno; // from __LINE__ 83 | const char *file; // from __FILE__ 84 | // Could also place a backtrace of void *[16] here ... 85 | }; 86 | 87 | // unique id must be from a hard random number source 88 | constexpr explicit _file_io_error_domain(typename _base::unique_id_type id = 0x230f170194fcc6c7) noexcept 89 | : _base(id) 90 | { 91 | } 92 | static inline constexpr const _file_io_error_domain &get(); 93 | 94 | // Return the name of our custom code domain 95 | virtual _base::string_ref name() const noexcept override final // NOLINT 96 | { 97 | static string_ref v("file i/o error domain"); 98 | return v; // NOLINT 99 | } 100 | 101 | // Return a string describing a specific code. We will return the 102 | // string returned by our POSIX code base domain, with the source 103 | // file and line number appended 104 | virtual _base::string_ref _do_message(const status_code &code) const noexcept override final // NOLINT 105 | { 106 | assert(code.domain() == *this); 107 | // Fetch message from base (POSIX) 108 | auto msg = _base::_do_message(code); 109 | const auto &c1 = static_cast(code); // NOLINT 110 | const auto &v = c1.value(); 111 | // Append my source file and line number 112 | if(v.file == nullptr) 113 | { 114 | return msg; 115 | } 116 | size_t length = strlen(v.file) + 16 + msg.size(); 117 | auto *p = static_cast(malloc(length)); // NOLINT 118 | if(p == nullptr) 119 | { 120 | return _base::string_ref("failed to get message from system"); 121 | } 122 | sprintf(p, "%s (%s:%d)", msg.data(), v.file, v.lineno); 123 | // Return as atomically reference counted string 124 | return _base::atomic_refcounted_string_ref(p, length); 125 | } 126 | }; 127 | 128 | // 100% constexpr instantiation 129 | constexpr _file_io_error_domain file_io_error_domain; 130 | inline constexpr const _file_io_error_domain &_file_io_error_domain::get() 131 | { 132 | return file_io_error_domain; 133 | } 134 | 135 | 136 | // Now tell `error` how it can implicitly construct from `file_io_error`. 137 | // This is done by us defining a free function called `make_status_code()` 138 | // which is discovered using ADL. `error` is an alias to the refinement 139 | // `status_code>` which is a status code whose value type 140 | // has been erased into an `intptr_t`. `status_code>` 141 | // (i.e. `error`) are move relocating (P1029) i.e. they are move-only 142 | // types whose move operation is defined to leave the source in the same 143 | // representation as a default constructed instance, and for whose 144 | // non-trivial destructor when called upon a default constructed instance 145 | // is guaranteed to do nothing. 146 | inline system_code make_status_code(file_io_error v) 147 | { 148 | // `make_nested_status_code()` dynamically allocates memory to store an 149 | // instance of `file_io_error`, then returns a status code whose domain 150 | // specifies that its value type is a pointer to `file_io_error`. The 151 | // domain is a templated instance which indirects all observers of the 152 | // status code to the pointed-to status code. 153 | // 154 | // Note that the status code returned's value type is a pointer, which 155 | // by definition fits into `intptr_t` and is trivially copyable. 156 | // Therefore `system_code` (which is also a type alias to 157 | // `status_code>`) is happy to implicitly construct 158 | // from the status code returned by `make_nested_status_code()`. 159 | return make_nested_status_code(std::move(v)); 160 | } 161 | 162 | 163 | /********************** Proof it works begins ***********************/ 164 | 165 | /* As under P1095 deterministic exception throws are identical to returning 166 | error types from functions, that's what we model here by functions which only 167 | return failure. The proof being demonstrated is that rich local failure 168 | types (i.e. `file_io_error`) will auto-decay into `error` implicitly. 169 | */ 170 | 171 | file_io_error open_file(const char * /* unused */) // models throws(file_io_error) 172 | { 173 | // Fail with permission denied 174 | return file_io_error({EPERM, __LINE__, __FILE__}); 175 | } 176 | 177 | error open_resource() // models throws(std::error) 178 | { 179 | for(;;) 180 | { 181 | /* In P1095 would actually be: 182 | 183 | try { open_file("some file"); } 184 | catch(file_io_error e) { if(e != errc::resource_unavailable_try_again) throw; ... } 185 | */ 186 | file_io_error e = open_file("some file"); 187 | if(e != errc::resource_unavailable_try_again) 188 | { 189 | // NOTE this implicitly converts from `file_io_error` to `error` via the 190 | // `make_status_code()` free function customisation point defined above. 191 | return e; 192 | } 193 | } 194 | // success continues here ... 195 | } 196 | 197 | int main(void) 198 | { 199 | error e = open_resource(); 200 | // A quick demonstration that the indirection works as indicated 201 | printf("Returned error has a code domain of '%s', a message of '%s'\n", e.domain().name().c_str(), e.message().c_str()); 202 | printf("\nAnd semantically comparing it to 'errc::operation_not_permitted' = %d\n", e == errc::operation_not_permitted); 203 | } --------------------------------------------------------------------------------