diff --git a/.github/actions/boost_clone/action.yml b/.github/actions/boost_clone/action.yml new file mode 100644 index 000000000..ecae5c7ee --- /dev/null +++ b/.github/actions/boost_clone/action.yml @@ -0,0 +1,121 @@ +name: 'Boost Clone' +description: 'This workflow clones the boost source directory, attempting to get it from the cache first' +inputs: + boost_dir: + description: 'The boost directory. The default value assumes boost is in-source.' + required: false + default: 'boost' + branch: + description: 'Branch of the super-project' + required: false + default: 'master' + patches: + description: 'Libraries used to patch the boost installation' + required: true + default: '' + modules: + description: 'The boost submodules we need to clone' + required: false + default: '' + +runs: + using: "composite" + steps: + - name: Environment + id: ctx + shell: bash + run: | + boost_hash=$(git ls-remote https://github.com/boostorg/boost.git ${{ inputs.branch }} | awk '{ print $1 }') + echo "boost_hash=$boost_hash" >> $GITHUB_OUTPUT + + # Create cache hash + cache_hash=${{ runner.os }}-boost-$boost_hash + # Add modules names to hash + modules=${{ inputs.modules }} + for module in ${modules//,/ } + do + module_filename=${module##*/} + cache_hash=$cache_hash-$module_filename + done + # Add patch names and hashes to hash + patches=${{ inputs.patches }} + for patch in ${patches//,/ } + do + patch_hash=$(git ls-remote $patch ${{ inputs.branch }} | awk '{ print $1 }') + cache_hash=$cache_hash-$patch-$patch_hash + done + echo "cache_hash=$cache_hash" >> $GITHUB_OUTPUT + + # attempt to get boost from the cache before cloning it + - name: boost cache + id: cache-boost + uses: actions/cache@v3 + with: + path: boost + key: ${{ steps.ctx.outputs.cache_hash }} + + # clone (if boost not found in cache) + - name: boost clone + if: steps.cache-boost.outputs.cache-hit != 'true' + shell: bash + run: | + git clone https://github.com/boostorg/boost.git -b ${{ inputs.branch }} ${{ inputs.boost_dir }} + + # apply patches (if boost not found in cache) + - name: boost patches + if: steps.cache-boost.outputs.cache-hit != 'true' && inputs.patches != '' + shell: bash + working-directory: ${{ inputs.boost_dir }}/libs + run: | + # Apply boost patches ${{ inputs.patches }} + patches=${{ inputs.patches }} + for patch in ${patches//,/ } + do + git clone $patch -b ${{ inputs.branch }} + done + + # Init all submodules (if boost not found in cache + no specific modules specified) + - name: boost init submodules + if: (steps.cache-boost.outputs.cache-hit != 'true' && inputs.modules == '') + working-directory: ${{ inputs.boost_dir }} + shell: bash + run: | + # Init all boost submodules + git submodule update --init --recursive + + # Init specified submodules (if boost not found in cache + modules specified) + - name: boost patches + if: (steps.cache-boost.outputs.cache-hit != 'true' && inputs.modules != '') + working-directory: ${{ inputs.boost_dir }} + shell: bash + run: | + # Init required boost submodules + echo "Look for python" + if command -v python &> /dev/null; then + python_executable="python" + elif command -v python3 &> /dev/null; then + python_executable="python3" + elif command -v python2 &> /dev/null; then + python_executable="python2" + else + echo "Please install Python!" >&2 + false + fi + echo "python_executable=$python_executable" + + echo "Init boostdep" + git submodule update -q --init tools/boostdep + + echo "Run boostdep for required modules: ${{ inputs.modules }}" + modules=${{ inputs.modules }} + for module in ${modules//,/ } + do + echo "Init submodule $module" + git submodule update -q --init libs/$module || true + done + for module in ${modules//,/ } + do + echo "Run boostdep for required module $module" + $python_executable tools/boostdep/depinst/depinst.py --include benchmark --include example --include examples --include tools --include source $module + done + diff --git a/.github/actions/cmake_run/action.yml b/.github/actions/cmake_run/action.yml new file mode 100644 index 000000000..a559746fd --- /dev/null +++ b/.github/actions/cmake_run/action.yml @@ -0,0 +1,131 @@ +name: 'Install dependencies' +description: 'This workflow installs dependencies from multiple package managers' +inputs: + cmake_exec: + description: 'The cmake executable' + required: false + default: 'cmake' + cc: + description: 'Path to C compiler.' + required: false + default: '' + cxx: + description: 'Path to C++ compiler.' + required: false + default: '' + cxxstd: + description: 'List of standards with which cmake will build and test the program.' + required: false + default: '' + source_dir: + description: 'Path to the source directory.' + required: false + default: '' + toolchain: + description: 'Path to toolchain.' + required: false + default: '' + build-type: + description: 'Build type.' + required: false + default: 'Release' + build-target: + description: 'Targets to build instead of the default target' + required: false + default: '' + install-prefix: + description: 'Path where the library should be installed.' + required: false + default: '.local/usr' + run-tests: + description: 'Whether we should run tests.' + required: false + default: 'true' + extra-args: + description: 'Extra arguments to cmake configure command.' + required: false + default: '' + +runs: + using: "composite" + steps: + - name: Get CPU cores + uses: SimenB/github-actions-cpu-cores@v1 + id: cpu-cores + + - name: Setup msvc dev-cmd + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + + - name: CMake Build C++${{ inputs.cxxstd }} + shell: bash + working-directory: ${{ inputs.source_dir }} + run: | + set -xe + + # cmake args + cc=${{ matrix.cc }} + if [ "$cc" != "" ]; then + if command -v $cc &> /dev/null; then + cc="$(which $cc)" + elif command -v /usr/bin/$cc &> /dev/null; then + cc="/usr/bin/$cc" + fi + cmake_cc_path_args="-D CMAKE_C_COMPILER=$cc" + else + cmake_cc_path_args= + fi + + cxx=${{ matrix.cxx }} + if [ "$cxx" != "" ]; then + if command -v $cxx &> /dev/null; then + cxx="$(which $cxx)" + elif command -v /usr/bin/$cxx &> /dev/null; then + cxx="/usr/bin/$cxx" + fi + cmake_cxx_path_args="-D CMAKE_CXX_COMPILER=$cxx" + else + cmake_cxx_path_args= + fi + + cxxstds=${{ inputs.cxxstd }} + if [ "$cxxstds" == "" ]; then + cxxstds=defaultcxx + fi + + cmake_toolchain=${{ inputs.toolchain }} + if [ "$cmake_toolchain" != "" ]; then + cmake_toolchain_arg="-D CMAKE_TOOLCHAIN_FILE=$cmake_toolchain" + else + cmake_toolchain_arg= + fi + + run_tests=${{ inputs.run-tests }} + if [ "$run_tests" == "true" ]; then + cmake_enable_test_args="-D BUILD_TESTING=ON" + fi + + build_target=${{ inputs.build-target }} + if [ "$build_target" != "true" ]; then + target_args="--target $build_target" + fi + + # iterate stds + for cxxstd in ${cxxstds//,/ } + do + echo "==================================> CMAKE: C++$cxxstd" + if [ "$cxxstd" == "defaultcxx" ]; then + cmake_cxxstd_arg="" + build_dir="build" + else + cmake_cxxstd_arg="-D CMAKE_CXX_STANDARD=$cxxstd" + build_dir="build-$cxxstd" + fi + cmake -S . -B $build_dir -D CMAKE_BUILD_TYPE=${{ inputs.build-type }} $cmake_toolchain_arg $cmake_cxxstd_arg ${{ inputs.extra-args }} -D CMAKE_INSTALL_PREFIX=${{ inputs.install-prefix }} $cmake_cc_path_args $cmake_cxx_path_args $cmake_enable_test_args + cmake --build $build_dir --config ${{ inputs.build-type }} -j ${{ steps.cpu-cores.outputs.count }} $target_args + cmake --install $build_dir --config ${{ inputs.build-type }} --prefix prefix + if [ "$run_tests" == "true" ]; then + ctest --test-dir "$build_dir" -j ${{ steps.cpu-cores.outputs.count }} -C ${{ inputs.build-type }} --no-tests=error --progress --output-on-failure + fi + done + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..5658da618 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,65 @@ +name: Continuous Integration + +on: + push: + branches: + - '*' + - '*/*' + + pull_request: + branches: + - develop + +jobs: + build: + name: ${{ matrix.name }} + + strategy: + fail-fast: false + matrix: + include: + - { name: "MSVC 14.3 - C++17-20", os: windows-2022, cxxstd: '17,20', cmake_args: -G "Visual Studio 17 2022" -A x64, } + - { name: "clang-cl 14.3 - C++14,20", os: windows-2022, cxxstd: '14,20', cmake_args: -G "Visual Studio 17 2022" -T ClangCL -A x64, } +# - { name: "MSVC 14.2 - C++14-17", os: windows-2019, cxxstd: '14,17', cmake_args: -G "Visual Studio 16 2019" -A x64, } + + - { name: "GCC 12 - C++17-20", os: ubuntu-22.04, cc: gcc-12, cxx: g++-12, cxxstd: '17,20', install: g++-12, } + - { name: "GCC 11 - C++14,20", os: ubuntu-22.04, cc: gcc-11, cxx: g++-11, cxxstd: '14,20', install: g++-11, } +# The GCC 10 this runner has is broken +# - { name: "GCC 10 - C++17-20", os: ubuntu-22.04, cc: gcc-10, cxx: g++-10, cxxstd: '17,20', } +# - { name: "GCC 9 - C++17-20", os: ubuntu-22.04, cc: gcc-9, cxx: g++-9, cxxstd: '17,20', install: g++-9, } +# - { name: "GCC 8 - C++14-17", os: ubuntu-20.04, cc: gcc-8, cxx: g++-8, cxxstd: '14,17', install: g++-8, } +# - { name: "GCC 7 - C++14", os: ubuntu-20.04, cc: gcc-7, cxx: g++-7, cxxstd: 14, install: g++-7, } + + - { name: "Clang 14 - C++17-20", os: ubuntu-22.04, cc: clang-14, cxx: clang++-14, cxxstd: '17,20', install: clang-14, } + - { name: "Clang 13 - C++14,20", os: ubuntu-22.04, cc: clang-13, cxx: clang++-13, cxxstd: '14,20', install: clang-13, } + - { name: "Clang 12 - C++17-20", os: ubuntu-22.04, cc: clang-12, cxx: clang++-12, cxxstd: '17,20', install: clang-12, } +# - { name: "Clang 11 - C++14-17", os: ubuntu-20.04, cc: clang-11, cxx: clang++-11, cxxstd: '14,17', install: clang-11, } + + - { name: "AppleClang 13", os: macos-12, cxxstd: 17 } + + runs-on: ${{ matrix.os }} + + steps: + - name: Outcome checkout + uses: actions/checkout@v4 + + - name: Install packages + if: startsWith(matrix.os, 'ubuntu') && matrix.install + run: sudo apt-get install -y ${{ matrix.install }} + + - name: Boost checkout + uses: ./.github/actions/boost_clone + with: + boost_dir: boost + branch: ${{ (github.ref_name == 'master' && 'master') || 'develop' }} + modules: config,exception,system,throw_exception,test + + - name: CMake Run (C++${{ matrix.cxxstd }}) + uses: ./.github/actions/cmake_run + with: + cxxstd: ${{ matrix.cxxstd }} + cxx: ${{ matrix.cxx }} + cc: ${{ matrix.cc }} + extra-args: ${{ format('-D BOOST_SRC_DIR=boost {0}', matrix.cmake_args) }} + build-target: tests + diff --git a/CMakeLists.txt b/CMakeLists.txt index ac4f0118c..7d051f08e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,10 +3,38 @@ # Distributed under the Boost Software License, Version 1.0. # https://www.boost.org/LICENSE_1_0.txt -cmake_minimum_required(VERSION 3.5...3.16) +cmake_minimum_required(VERSION 3.10...4.0 FATAL_ERROR) project(boost_outcome VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) +if (${PROJECT_SOURCE_DIR} STREQUAL ${CMAKE_SOURCE_DIR}) + # Project is root. Find Boost source. + set(BOOST_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../.." CACHE STRING "Boost source dir to use when running CMake from this directory") + if (NOT IS_ABSOLUTE ${BOOST_SRC_DIR}) + set(BOOST_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${BOOST_SRC_DIR}") + endif() + set(BOOST_SRC_DIR_IS_VALID ON) + foreach (F "CMakeLists.txt" "Jamroot" "boost-build.jam" "bootstrap.sh" "libs") + if (NOT EXISTS "${BOOST_SRC_DIR}/${F}") + message(STATUS "${BOOST_SRC_DIR}/${F} does not exist. Fallback to find_package.") + set(BOOST_SRC_DIR_IS_VALID OFF) + break() + endif() + endforeach() + # Create Boost targets from source dir or boost package. + set(BOOST_INCLUDE_LIBRARIES config exception system throw_exception test) + if (BOOST_SRC_DIR_IS_VALID) + set(BOOST_EXCLUDE_LIBRARIES ${PROJECT_NAME}) + add_subdirectory(${BOOST_SRC_DIR} deps_/boost EXCLUDE_FROM_ALL) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${BOOST_SRC_DIR}/tools/cmake/include") + else() + find_package(Boost 1.81.0 REQUIRED) + foreach (BOOST_INCLUDE_LIBRARY ${BOOST_INCLUDE_LIBRARIES}) + add_library(Boost::${BOOST_INCLUDE_LIBRARY} ALIAS Boost::headers) + endforeach () + endif() +endif() + add_library(boost_outcome INTERFACE) add_library(Boost::outcome ALIAS boost_outcome) @@ -25,8 +53,11 @@ target_compile_features(boost_outcome cxx_std_14 ) -if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt") +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11) + target_compile_options(boost_outcome INTERFACE -ftemplate-depth=5000) +endif () +if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt") + enable_testing() add_subdirectory(test) - endif() diff --git a/build.jam b/build.jam new file mode 100644 index 000000000..99db56fa5 --- /dev/null +++ b/build.jam @@ -0,0 +1,26 @@ +# Copyright René Ferdinand Rivera Morell 2023-2024 +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +require-b2 5.2 ; + +constant boost_dependencies : + /boost/assert//boost_assert + /boost/config//boost_config + /boost/exception//boost_exception + /boost/system//boost_system + /boost/throw_exception//boost_throw_exception ; + +project /boost/outcome + : common-requirements + include + ; + +explicit + [ alias boost_outcome : : : : $(boost_dependencies) ] + [ alias all : boost_outcome test ] + ; + +call-if : boost-library outcome + ; diff --git a/doc/html/categories/special.html b/doc/html/categories/special.html index 66a95f6d6..40e0ebe02 100644 --- a/doc/html/categories/special.html +++ b/doc/html/categories/special.html @@ -36,7 +36,7 @@ -

Last revised: January 22, 2019 at 01:11:40 UTC

+

Last revised: December 16, 2023 at 20:51:26 UTC


Prev diff --git a/doc/html/changelog.html b/doc/html/changelog.html index 774fa7134..7f811d3d7 100644 --- a/doc/html/changelog.html +++ b/doc/html/changelog.html @@ -15,60 +15,97 @@
-
v2.2.6 24th March 2023 (Boost 1.82) [release] +
v2.2.14 ? (Boost 1.90) [release]
Enhancements:
Bug fixes:
-
v2.2.4 11th August 2022 (Boost 1.80) [release] +
v2.2.13 6th August 2025 (Boost 1.89) [release]
Enhancements:
+
+
v2.2.12 10th April 2025 (Boost 1.88) [release] +
Bug fixes:
-
v2.2.3 17th March 2022 (Boost 1.79) [release] +
v2.2.11 12th December 2024 (Boost 1.87) [release]
Enhancements:
Bug fixes:
-
v2.2.2 8th December 2021 (Boost 1.78) [release] +
v2.2.10 14th August 2024 (Boost 1.86) [release]
Enhancements:
Bug fixes:
-
v2.2.1 13th August 2021 (Boost 1.77) [release] +
v2.2.9 15th April 2024 (Boost 1.85) [release]
+
Enhancements:
+
+
v2.2.8 13th December 2023 (Boost 1.84) [release] +
+
Enhancements:
Bug fixes:
-
v2.2.0 16th April 2021 (Boost 1.76) [release] +
v2.2.7 13th August 2023 (Boost 1.83) [release]
-
Enhancements:
+
Enhancements:
Bug fixes:
-
v2.1.5 11th December 2020 (Boost 1.75) [release] +
v2.2.6 24th March 2023 (Boost 1.82) [release]
-
Enhancements:
+
Enhancements:
Bug fixes:
-
v2.1.4 14th August 2020 (Boost 1.74) [release] +
v2.2.4 11th August 2022 (Boost 1.80) [release]
-
Enhancements:
+
Enhancements:
Bug fixes:
-
v2.1.3 29th April 2020 (Boost 1.73) [release] +
v2.2.3 17th March 2022 (Boost 1.79) [release]
-
Enhancements:
+
Enhancements:
Bug fixes:
-
v2.1.2 11th December 2019 (Boost 1.72) [release] +
v2.2.2 8th December 2021 (Boost 1.78) [release]
-
Enhancements:
+
Enhancements:
Bug fixes:
-
v2.1.1 19th August 2019 (Boost 1.71) [release] +
v2.2.1 13th August 2021 (Boost 1.77) [release]
-
Enhancements:
Bug fixes:
+
v2.2.0 16th April 2021 (Boost 1.76) [release] +
+
Enhancements:
+
Bug fixes:
+
+
v2.1.5 11th December 2020 (Boost 1.75) [release] +
+
Enhancements:
+
Bug fixes:
+
+
v2.1.4 14th August 2020 (Boost 1.74) [release] +
+
Enhancements:
+
Bug fixes:
+
+
v2.1.3 29th April 2020 (Boost 1.73) [release] +
+
Enhancements:
+
Bug fixes:
+
+
v2.1.2 11th December 2019 (Boost 1.72) [release] +
+
Enhancements:
+
Bug fixes:
+
+
v2.1.1 19th August 2019 (Boost 1.71) [release] +
+
Enhancements:
+
Bug fixes:
+
v2.1 12th Apr 2019 (Boost 1.70) [release]
v2.0 18th Jan 2018 [release]
@@ -78,16 +115,190 @@
-

v2.2.6 24th March 2023 (Boost 1.82) [release]

+

v2.2.14 ? (Boost 1.90) [release]

Enhancements:

+

#314 +- Bump Boost min cmake required to 3.10 to match standalone Outcome. Also bump minium cmake to 3.10 +everywhere else in Outcome, as CI is now failing due to us requested too old a cmake.

+ +

Bug fixes:

+ +
+ +

v2.2.13 6th August 2025 (Boost 1.89) [release]

+ +

Enhancements:

+ +

#312 +- iostream_support.hpp has been split into iostream_support_result.hpp and iostream_support.hpp.

+ +

#313 +- Bump min cmake required to 3.10 amongst other cmake modernisation fixes to please cmake 4.0.

+ +
+ +

v2.2.12 10th April 2025 (Boost 1.88) [release]

+ +

Bug fixes:

+ +
    +
  • The Android build had become broken, fixed.
  • +
+ +
+ +

v2.2.11 12th December 2024 (Boost 1.87) [release]

+ +

Enhancements:

+ +
    +
  • Outcome.Experimental has had C representation support since the beginning, however it had +been mainly intended that C++ would originate Results, they would pass through C, and back +into C++. It hadn’t really been expected that C would want to do much with Results other than +inspect them for happy or sad path.
  • +
+ +

It turns out there is more demand than expected for a more functional Result from within C, +so this release adds the power to create Results in success and two types of failure, semantic +comparison of Results, and printing of Result messages. You can also wrap a C enum into a +quick status code from enum, allowing easy custom C error coding from 100% within C.

+ +

The documentation for the C support has been updated +to reflect the new facilities.

+ +

Bug fixes:

+ +
    +
  • This was fixed in Standalone Outcome in the last release, but the fix came too late for Boost.Outcome +which ended up shipping with inline GDB pretty printers with the wrong escaping which caused +failure to load.
  • +
+ +
+ +

v2.2.10 14th August 2024 (Boost 1.86) [release]

+ +

Enhancements:

+ +
    +
  • Something I’ve been meaning to do for far too long now is make the GDB pretty printers +auto-loading so you don’t have to set up .gdbinit. This is now done. I also improved +the pretty printers to also pretty print the C result type which can be very useful if +working with that type, as it will print the error message in GDB.
  • +
+ +

Experimental Outcome’s status_code has also gained its own auto-loading GDB pretty printer +with display of strerror() if the code domain is POSIX or generic.

+ +

Bug fixes:

+ +
    +
  • The status enumeration used to track state internally did not list all possible enum +values. This caused static analysers to complain.
  • +
+ +
+ +

v2.2.9 15th April 2024 (Boost 1.85) [release]

+ +

Enhancements:

+ +

#293 +- Some users wished that Outcome would be clean with -Wpedantic, this is now turned on for +the test suite.

+ +

#294 +- All use of assert() has been replaced with BOOST_OUTCOME_ASSERT, which can be user overridden +at compile time.

+ +

#295 +- In git commit 12b14e1533848e9a0f7f3c38e41da0ee4e819770 (Aug 11 2022) status code had its +paths changed due to its headers not previously having the right path convention. It was not +realised at the time that in Boost.Outcome this resulted in +<boost/outcome/experimental/status-code/status-code/headers.hpp> which is not desirable. +This has now been remedied to remove the double status-code, which will obviously break +any Boost.Outcome code which relies on the double status-code. Standalone Outcome is unaffected.

+ +
+ +

v2.2.8 13th December 2023 (Boost 1.84) [release]

+ +

Enhancements:

+ +
    +
  • cmake 3.9 is now the minimum required for standalone Outcome. This fixes a long standing +cmake issue with probing for standard library facilities. cmake 3.9 is what RHEL7 ships with, +when RHEL7 EOLs we may raise the minimum cmake version at that point.
  • +
+ +

Bug fixes:

+ +
    +
  • There was a bug in the Outcome C++ coroutine awaitables whereby we were over eagerly resuming +execution of coroutines which return one of our awaitables. It is surprising how many years have +passed before this was noticed, but it is now fixed. It is believed that this has been fixed +without affecting ABI stability, however mixing old Outcome and new Outcome in the same binary +without recompiling all the C++ coroutine code to use new Outcome will not fix the bug.
  • +
+ +

#291 +- A Result or Outcome with void value type and move-only non-value type was only usable in +const use cases, due to the lack of provision of non-const member functions in relevant observers +injection layers for the void specialisation. The missing non-const member functions have now +been added.

+ +
+ +

v2.2.7 13th August 2023 (Boost 1.83) [release]

+ +

Enhancements:

+ +
    +
  • Update the list of known compiler issues in the docs.

  • + +
  • Update Outcome.Experimental to match latest changes requested of status_code by WG21. +This as usual will cause minor breakage due to LEWG renaming of things.

  • + +
  • Outcome previously took addresses of things not using std::addressof(), and until now +nobody complained because custom operator& which doesn’t return an address is an +abomination not used in much modern C++. But finally someone did complain, so +for both normal Outcome and Experimental.Outcome, if you set BOOST_OUTCOME_USE_STD_ADDRESSOF = 1, +Outcome will use std::addressof()

  • +
+ +

Bug fixes:

+ +

#273 +- Changes to other Boost libraries had caused Boost.Outcome’s test suite to fail to compile for some +compiler and C++ language configurations in recent releases. Thanks to work contributed by @alandefreitas +and @pdimov, Boost.Outcome now CI tests a wide range of compilers and configurations and it +is believed all those corner case issues have been fixed or worked around, for the compilers +and configurations within that CI matrix.

+ +

Standalone Outcome’s test suite was never affected, as it did not have Boost changing underneath it. +Nevertheless, a few of the compiler parse bug workarounds will have improved compatibility there +too for atyical toolchain choices.

+ +
    +
  • Experimental.Outcome now supports big endian architectures. Implementation for them simply wasn’t done +before under the assumption that nobody would be using Experimental.Outcome on big endian architectures. +Turns out that was a wrong assumption!
  • +
+ +
+ +

v2.2.6 24th March 2023 (Boost 1.82) [release]

+ +

Enhancements:

+
  • Update to latest status-code in Experimental.Outcome, which relocates its header files and may cause some end user inclusions to need path changes.
-

Bug fixes:

+

Bug fixes:

  • Latest status-code fixes a number of nasty bugs, some mild build breakage in Experimental.Outcome @@ -98,7 +309,7 @@

    Bug fixes:

    v2.2.4 11th August 2022 (Boost 1.80) [release]

    -

    Enhancements:

    +

    Enhancements:

    • Update to latest status-code in Experimental.Outcome, which has some breaking changes and important @@ -123,7 +334,7 @@

      Enhancements:

      temporary. Thanks to RVO pre-17 and copy elision since, this should add no runtime overhead.

    -

    Bug fixes:

    +

    Bug fixes:

    #261
    @@ -134,7 +345,7 @@

    Bug fixes:

    v2.2.3 17th March 2022 (Boost 1.79) [release]

    -

    Enhancements:

    +

    Enhancements:

    Standalone Outcome permanently locks ABI to v2.2.3 release
    @@ -145,7 +356,7 @@

    Enhancements:

    which permutes per commit.
    -

    Bug fixes:

    +

    Bug fixes:

    #255
    @@ -156,7 +367,7 @@

    Bug fixes:

    v2.2.2 8th December 2021 (Boost 1.78) [release]

    -

    Enhancements:

    +

    Enhancements:

    #255
    @@ -166,7 +377,7 @@

    Enhancements:

    Coroutine support in GCCs after 10 is now correctly detected.
    -

    Bug fixes:

    +

    Bug fixes:

    • None.
    • @@ -176,7 +387,7 @@

      Bug fixes:

      v2.2.1 13th August 2021 (Boost 1.77) [release]

      -

      Bug fixes:

      +

      Bug fixes:

      #251
      @@ -197,7 +408,7 @@

      the list of v2.2 major changes.

      -

      Enhancements:

      +

      Enhancements:

      VS2019.8 compatibility
      @@ -221,7 +432,7 @@

      Enhancements:

      propagate through TRY, now they do, which is a breaking change.

      -

      Bug fixes:

      +

      Bug fixes:

      BREAKING CHANGE #244
      @@ -295,7 +506,7 @@

      Bug fixes:

      v2.1.5 11th December 2020 (Boost 1.75) [release]

      -

      Enhancements:

      +

      Enhancements:

      The ADL discovered event hooks have been replaced with policy-specified event hooks instead
      @@ -315,7 +526,7 @@

      Enhancements:

      failures on GCC.
      -

      Bug fixes:

      +

      Bug fixes:

      Boost.Outcome should now compile with BOOST_NO_EXCEPTIONS defined
      @@ -334,7 +545,7 @@

      Bug fixes:

      v2.1.4 14th August 2020 (Boost 1.74) [release]

      -

      Enhancements:

      +

      Enhancements:

      BREAKING CHANGE void results and outcomes no longer default construct types during explicit construction
      @@ -371,7 +582,7 @@

      Enhancements:

      the list of changes between Outcome v2.1 and v2.2.
      -

      Bug fixes:

      +

      Bug fixes:

      #224
      @@ -392,7 +603,7 @@

      Bug fixes:

      v2.1.3 29th April 2020 (Boost 1.73) [release]

      -

      Enhancements:

      +

      Enhancements:

      Performance of Outcome-based code compiled by clang has been greatly improved
      @@ -409,7 +620,7 @@

      Enhancements:

      Precompiled headers are automatically enabled on new enough cmake’s for standalone Outcome

      If on cmake 3.16 or later, its new precompiled headers build support is used to tell consumers of the outcome::hl cmake target to precompile Outcome, if -and only if PROJECT_IS_DEPENDENCY is false. PROJECT_IS_DEPENDENCY is set +and only if outcome_IS_DEPENDENCY is false. outcome_IS_DEPENDENCY is set by Outcome’s CMakeLists.txt if it detects that it was included using add_subdirectory(), so for the vast majority of Outcome end users, the use of precompiled headers will NOT be enabled.

      @@ -425,7 +636,7 @@

      Enhancements:

      The coroutines support added in v2.1.2 has now been properly documented.

      -

      Bug fixes:

      +

      Bug fixes:

      #214
      @@ -452,7 +663,7 @@

      Bug fixes:

      v2.1.2 11th December 2019 (Boost 1.72) [release]

      -

      Enhancements:

      +

      Enhancements:

      Improved compatibility with cmake tooling
      @@ -513,7 +724,7 @@

      Enhancements:

      this mechanism conversions from basic_result<> and failure_type<>.
      -

      Bug fixes:

      +

      Bug fixes:

      #184
      @@ -524,7 +735,7 @@

      Bug fixes:

      v2.1.1 19th August 2019 (Boost 1.71) [release]

      -

      Enhancements:

      +

      Enhancements:

      #184
      @@ -547,7 +758,7 @@

      Enhancements:

      Added a separate motivation/plug_error_code specifically for Boost.
      -

      Bug fixes:

      +

      Bug fixes:

      -
      @@ -710,7 +921,7 @@

      Prev diff --git a/doc/html/credits.html b/doc/html/credits.html index 479aba89a..3b9beb0c3 100644 --- a/doc/html/credits.html +++ b/doc/html/credits.html @@ -41,7 +41,7 @@

      github contributors

      - 1440 commits + 1537 commits
      @@ -65,7 +65,7 @@

      github contributors

      - 6 commits + 7 commits
      @@ -98,6 +98,12 @@

      github contributors

      1 commits
      +
      + + + 1 commits +
      +
      @@ -110,6 +116,12 @@

      github contributors

      1 commits
      +
      + + + 1 commits +
      +
      diff --git a/doc/html/experimental.html b/doc/html/experimental.html index 02c62c85a..cfba591fa 100644 --- a/doc/html/experimental.html +++ b/doc/html/experimental.html @@ -7,7 +7,7 @@
      -Prev +Prev Up HomeNext
      @@ -106,7 +106,7 @@

      Last revised: December 17, 2020 at 11:27:06 UTC


      -Prev +Prev Up HomeNext
      diff --git a/doc/html/experimental/advantages.html b/doc/html/experimental/advantages.html index 91164c1f1..f05461d7f 100644 --- a/doc/html/experimental/advantages.html +++ b/doc/html/experimental/advantages.html @@ -36,6 +36,10 @@ and the dual target source code, being written to tighter discipline, is faster and more deterministic in the default target than it was before the (non-trivial) port to <boost/outcome/experimental>.

      + +
    • If you want ‘official’ C support, experimental Outcome is able to +provide that in a way not possible for default Outcome which cannot make +sufficiently strong C compatibility assumptions about std::error_code.

    • If you are building a codebase on top of Outcome expecting long term @@ -57,7 +61,7 @@

      -

      Last revised: February 05, 2019 at 21:41:47 UTC

      +

Last revised: July 16, 2024 at 21:33:35 +0100


Prev diff --git a/doc/html/experimental/c-api.html b/doc/html/experimental/c-api.html index 1043486a1..2a999e0bd 100644 --- a/doc/html/experimental/c-api.html +++ b/doc/html/experimental/c-api.html @@ -7,9 +7,9 @@
-Prev +Prev Up - HomeNext
+ HomeNext

Using Outcome from C code

A long standing problem for C code (or more usually nowadays, the many other programming @@ -52,7 +52,7 @@

Last revised: February 05, 2019 at 17:14:18 UTC


-Prev +Prev Up - HomeNext
+ HomeNext
diff --git a/doc/html/experimental/c-api/from-c.html b/doc/html/experimental/c-api/from-c.html new file mode 100644 index 000000000..cd9bc2f29 --- /dev/null +++ b/doc/html/experimental/c-api/from-c.html @@ -0,0 +1,64 @@ + + +C Results - Boost.Outcome documentation + + + + + +
+Prev + Up + HomeNext
+ +

C Results

+

The C macro API header <boost/outcome/experimental/result.h> has some macros for working with any kind of Result:

+ +
+
BOOST_OUTCOME_C_DECLARE_RESULT(ident, T, E) +
Declares to C a basic_result type uniquely +identified by ident. T is available at the +member variable .value, and E is available +at the member variable .error. If you call this from within +C++, make SURE it is not within a extern "C" block! + +
BOOST_OUTCOME_C_RESULT(ident) +
A reference to a previously declared result type with +unique ident. + +
BOOST_OUTCOME_C_RESULT_HAS_VALUE(r) +
Evaluates to 1 (true) if the input result has a value. + +
BOOST_OUTCOME_C_RESULT_HAS_ERROR(r) +
Evaluates to 1 (true) if the input result has an error. + +
BOOST_OUTCOME_C_RESULT_ERROR_IS_ERRNO(r) +
Evaluates to 1 (true) if the input result's error value +is a code in the POSIX errno domain. +
+ +

The above let you work, somewhat awkwardly, with any C-compatible +basic_result<T, E>. basic_result<T, E> is trivially copyable and +standard layout if its T and E are both so, and it has the C layout:

+
struct cxx_result_##ident
+{
+  union
+  {
+    T value;
+    E error;
+  };
+  unsigned flags;
+};
+
+

Note that this layout is different to that of BOOST_OUTCOME_C_DECLARE_STATUS_CODE +as the C++ result has a different layout if E is a status code.

+ + + +

Last revised: July 17, 2024 at 17:54:05 +0100

+
+
+Prev + Up + HomeNext
+ diff --git a/doc/html/experimental/c-api/from-c/declare.html b/doc/html/experimental/c-api/from-c/declare.html new file mode 100644 index 000000000..896be573a --- /dev/null +++ b/doc/html/experimental/c-api/from-c/declare.html @@ -0,0 +1,52 @@ + + +Declare a Result - Boost.Outcome documentation + + + + + +
+Prev + Up + HomeNext
+

Declare a Result

+
// Declare to C a Result with a happy value of intptr_t
+BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(result_int, intptr_t)
+
+// Save oneself typing out BOOST_OUTCOME_C_RESULT_SYSTEM(result_int) all the time
+typedef BOOST_OUTCOME_C_RESULT_SYSTEM(result_int) result;
+
+// Our custom C enum
+enum c_enum
+{
+  c_enum_not_found,
+  c_enum_bad_argument
+};
+
+// Make a custom status code domain for this C enum
+BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM_FROM_ENUM(result_int,                                // The C Result type declared above
+                                    c_enum,                                    // The C enum we wish to wrap
+                                    "{74ceb994-7622-3a21-07f0-b016aa705585}",  // Unique UUID for this domain
+                                    // Mappings of C enum values to textual description and semantic equivalances to generic codes
+                                    {c_enum::c_enum_not_found, "item not found", {errc::no_such_file_or_directory}},
+                                    {c_enum::c_enum_bad_argument, "invoked wrong", {errc::invalid_argument}})
+
+// Make helper macros
+#define SUCCESS(v) BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_SUCCESS(result_int, (v))
+#define FAILURE(v) BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FROM_ENUM(result_int, c_enum, (v))
+
View this code on Github
+ + +

The key to making C programming easy is to alias the long complex things +into short easy thing. Obviously SUCCESS(expr) and FAILURE(expr) is too +generic, but for the purposes of this documentation it makes thing easier.

+ + +

Last revised: July 16, 2024 at 21:33:35 +0100

+
+
+Prev + Up + HomeNext
+ diff --git a/doc/html/experimental/c-api/from-c/system_code.html b/doc/html/experimental/c-api/from-c/system_code.html new file mode 100644 index 000000000..c051742cd --- /dev/null +++ b/doc/html/experimental/c-api/from-c/system_code.html @@ -0,0 +1,117 @@ + + +C system error results - Boost.Outcome documentation + + + + + +
+Prev + Up + HomeNext
+

C system error results

+

In v2.2.11, C Result support went from second tier to first tier status, and +now you can create, query and manipulate a subset of Result types entirely from +within C by including <boost/outcome/experimental/result.h>.

+ +

The subset supported are those result<T, E> which are a status_result<T> +i.e. the E is hardcoded to experimental::error which is the type erased runtime +polymorphic holder for any errored status_code whose payload is not bigger +than an intptr_t. This is the most useful subset of Outcome Experimental’s +possible Result types, allowing arbitrary custom error coding schemes from +any unknown source to work seamlessly with all others, including errors from +the system or third party libraries.

+ +

The operations available to C are:

+ +
+
BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(ident, T) +
Declares to C a status_result type uniquely +identified by ident. T is available at the +member variable .value, and struct cxx_status_code_system +is available at the member variable .error. If in C++, +implements C extern functions for making successful and failure results +of this type. If you call this from within +C++, make SURE it is not within a extern "C" block! + +
BOOST_OUTCOME_C_RESULT_SYSTEM(ident) +
A reference to a previously declared status_result type with +unique ident. + +
BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_SUCCESS(ident, expr) (needs C++ counterpart linked into final binary) +
This invokes the aforementioned extern function which creates a status_result +with a successful value of type T. +
BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FAILURE_POSIX(ident, expr) (needs C++ counterpart linked into final binary) +
This invokes the aforementioned extern function which creates a status_result +with a failure of type posix_code representing a POSIX errno. +
BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FAILURE_SYSTEM(ident, expr) (needs C++ counterpart linked into final binary) +
This invokes the aforementioned extern function which creates a status_result +with a failure of type posix_code representing a POSIX errno +if on POSIX; if on Windows then a failure of type win32_code +representing a Win32 error code from a Windows API. + +

+
BOOST_OUTCOME_C_RESULT_HAS_VALUE(r) +
Evaluates to 1 (true) if the input result has a value. + +
BOOST_OUTCOME_C_RESULT_HAS_ERROR(r) +
Evaluates to 1 (true) if the input result has an error. + +
BOOST_OUTCOME_C_RESULT_ERROR_IS_ERRNO(r) +
Evaluates to 1 (true) if the input result's error value +is a code in the POSIX errno domain. +

+
BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(expr) +
If the status_result returned by expr is +errored, exit the current function returning the result. This obviously +requires that the return type of the current function matches that of +expr. + +
BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(cleanup, expr) +
Same as the above, but execute cleanup just before exiting the function +if returning failure. + +
BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(var, cleanup, expr) +
Same as the above, but set var equal to the result's .value on success. + +
BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(var, ident, cleanup, expr) +
Same as the above, but use ident as the return type instead. This allows +the return type of the calling function to differ from that of expr. +

+
BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, uuid, {enum mapping-sequence, ...}) +
This declares to C an extern function which creates a status_result +from a C enum. If in C++, it implements a quick_status_code_from_enum for +the C enum and the associated extern function, and you will need to supply uuid +and the appropriate enum value mapping sequence +as per the quick_status_code_from_enum documentation. +
BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, expr) (needs C++ counterpart linked into final binary) +
This invokes the aforementioned extern function which creates a status_result +from a C enum. +
+ +

The operations available to C++ are:

+ +
+
BOOST_OUTCOME_C_TO_RESULT_SYSTEM_CODE(ident, status_code<T>) +
Returns a previously declared C Result from its matching C++ status_code. +NOTE that the destructor of the C++ status code is NOT called. If this is important +to your status code, it is 100% on you to ensure that your C Result reenters a C++ +Result at the end of its lifetime. + +
to_result(any C Result) +
This is an overloaded C++ free function which returns the C++ status_code<T> +matching its input C Result. +
+ +

Using the above you can write C code using Outcome.Experimental’s Result type +quite effectively. Let’s look at an example of use next.

+ + +

Last revised: July 17, 2024 at 17:54:05 +0100

+
+
+Prev + Up + HomeNext
+ diff --git a/doc/html/experimental/c-api/from-c/try.html b/doc/html/experimental/c-api/from-c/try.html new file mode 100644 index 000000000..13a9092c2 --- /dev/null +++ b/doc/html/experimental/c-api/from-c/try.html @@ -0,0 +1,43 @@ + + +TRY a C Result - Boost.Outcome documentation + + + + + +
+Prev + Up + HomeNext
+

TRY a C Result

+

Thanks to much of the magic of BOOST_OUTCOME_TRY(var, expr) + being implemented +using C preprocessor metaprogramming, we can offer a very similar experience for the +C try operation and without needing anything compiled in C++ as support functions:

+ +
result test2(int x)
+{
+  BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(int v,                                        // what to set to value if successful
+                        fprintf(stderr, "Positive numbers only!\n"),  // what cleanup to run if unsuccessful
+                        positive_only(x));
+  return SUCCESS(v + 1);
+}
+
View this code on Github
+ + +

The principle difference is that you can specify a cleanup routine to perform if +failure is encountered. This is especially useful in C, which has no stack unwinding.

+ +

Also due to lack of type sugaring and user defined implicit conversions, if your +callers result type isn’t your callee’s, you may need to specify what your caller’s +result type is so the error state can be correctly propagated.

+ + +

Last revised: July 16, 2024 at 21:33:35 +0100

+
+
+Prev + Up + HomeNext
+ diff --git a/doc/html/experimental/c-api/from-c/use.html b/doc/html/experimental/c-api/from-c/use.html new file mode 100644 index 000000000..bba38f581 --- /dev/null +++ b/doc/html/experimental/c-api/from-c/use.html @@ -0,0 +1,53 @@ + + +Using a Result - Boost.Outcome documentation + + + + + +
+Prev + Up + HomeNext
+

Using a Result

+

This models the earlier C++ example of use, +and its C equivalent isn’t much more verbose thanks to our helper typedefs and macros:

+ +
result positive_only(int x)
+{
+  if(x < 0)
+  {
+    return FAILURE(c_enum_bad_argument);
+  }
+  return SUCCESS(x);
+}
+
+bool test(int x)
+{
+  result r = positive_only(x);
+  if(BOOST_OUTCOME_C_RESULT_HAS_ERROR(r))
+  {
+    if(outcome_status_code_equal_generic(&r.error, EINVAL))
+    {
+      fprintf(stderr, "Positive numbers only!\n");
+      return false;
+    }
+  }
+  return true;
+}
+
View this code on Github
+ + +

For this to link, the BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM_FROM_ENUM macro needs to be +compiled at least once within C++ within the final binary to emit the extern +functions needed by C.

+ + +

Last revised: July 16, 2024 at 21:33:35 +0100

+
+
+Prev + Up + HomeNext
+ diff --git a/doc/html/experimental/c-api/limitations.html b/doc/html/experimental/c-api/from-cxx.html similarity index 88% rename from doc/html/experimental/c-api/limitations.html rename to doc/html/experimental/c-api/from-cxx.html index 72ee2961a..4fe2be58d 100644 --- a/doc/html/experimental/c-api/limitations.html +++ b/doc/html/experimental/c-api/from-cxx.html @@ -1,6 +1,6 @@ -Limitations - Boost.Outcome documentation +Calling C++ from C - Boost.Outcome documentation @@ -9,9 +9,10 @@
Prev Up - HomeNext
-

Limitations

-

C++ has excellent two-way compatibility with the C ABI, but there are some + HomeNext

+ +

Calling C++ from C

+

C++ has excellent two-way compatibility with the C ABI, but there are some limitations you must observe to write C++ code which C code can call without marshalling at the ABI boundary:

@@ -72,10 +73,11 @@ an “almost-standard-layout” C++ type.

-

Last revised: February 05, 2019 at 17:14:18 UTC

+ +

Last revised: July 16, 2024 at 21:33:35 +0100


Prev Up - HomeNext
+ HomeNext diff --git a/doc/html/experimental/c-api/example.html b/doc/html/experimental/c-api/from-cxx/example.html similarity index 82% rename from doc/html/experimental/c-api/example.html rename to doc/html/experimental/c-api/from-cxx/example.html index 24ad8a9ac..e004995f9 100644 --- a/doc/html/experimental/c-api/example.html +++ b/doc/html/experimental/c-api/from-cxx/example.html @@ -1,15 +1,15 @@ Example C++ function - Boost.Outcome documentation - + - +
-Prev - Up - HomeNext
+Prev + Up + HomeNext

Example C++ function

Let us start with a simple C++ function which we wish to make available to C code:

@@ -42,7 +42,7 @@ // win32_code). // // Note that using this function requires including - // <boost/outcome/experimental/status-code/system_code_from_exception.hpp> + // <boost/outcome/experimental/system_code_from_exception.hpp> // It is NOT included by Experimental Outcome by default. return outcome_e::system_code_from_exception(); } @@ -61,10 +61,10 @@ function taking similar parameters, but it returns a outcome_e::system_code (status_code<erased<intptr_t>>) instead of a std::error_code.

-

Last revised: February 05, 2019 at 17:14:18 UTC

+

Last revised: July 16, 2024 at 21:33:35 +0100


-Prev - Up - HomeNext
+Prev + Up + HomeNext diff --git a/doc/html/experimental/c-api/example2.html b/doc/html/experimental/c-api/from-cxx/example2.html similarity index 79% rename from doc/html/experimental/c-api/example2.html rename to doc/html/experimental/c-api/from-cxx/example2.html index fe856c2a6..facaf3a87 100644 --- a/doc/html/experimental/c-api/example2.html +++ b/doc/html/experimental/c-api/from-cxx/example2.html @@ -1,15 +1,15 @@ Calling it from C - Boost.Outcome documentation - + - +
-Prev - Up - HomeNext
+Prev + Up + HomeNext

Calling it from C

Firstly we need to declare to C our result returning C++ function:

@@ -19,10 +19,10 @@ // // The first parameter is some unique identifier for this type which will be used // whenever we reference this type in the future. -CXX_DECLARE_RESULT_SYSTEM(to_string_rettype, size_t); +CXX_DECLARE_RESULT_SYSTEM(to_string_rettype, size_t) // Tell C about our extern C++ function `to_string()` -extern CXX_RESULT_SYSTEM(to_string_rettype) _Z9to_stringPcmi(char *buffer, size_t bufferlen, int v); +extern CXX_RESULT_SYSTEM(to_string_rettype) _Z9to_stringPcmi(char *buffer, size_t bufferlen, int v);
View this code on Github @@ -68,10 +68,10 @@ -

Last revised: February 05, 2019 at 17:14:18 UTC

+

Last revised: July 16, 2024 at 21:33:35 +0100


-Prev - Up - HomeNext
+Prev + Up + HomeNext diff --git a/doc/html/experimental/c-api/reference.html b/doc/html/experimental/c-api/reference.html deleted file mode 100644 index f9e177a83..000000000 --- a/doc/html/experimental/c-api/reference.html +++ /dev/null @@ -1,114 +0,0 @@ - - -C Macro API Reference - Boost.Outcome documentation - - - - - -
-Prev - Up - HomeNext
-

C Macro API Reference

- - -

The C macro API header <boost/outcome/experimental/result.h> consists of these macros:

- -
-
BOOST_OUTCOME_C_DECLARE_RESULT(ident, T, E) -
Declares to C a basic_result type uniquely -identified by ident. T is available at the -member variable .value, and E is available -at the member variable .error. - -
BOOST_OUTCOME_C_RESULT(ident) -
A reference to a previously declared result type with -unique ident. - -
BOOST_OUTCOME_C_RESULT_HAS_VALUE(r) -
Evaluates to 1 (true) if the input result has a value. - -
BOOST_OUTCOME_C_RESULT_HAS_ERROR(r) -
Evaluates to 1 (true) if the input result has an error. - -
BOOST_OUTCOME_C_RESULT_ERROR_IS_ERRNO(r) -
Evaluates to 1 (true) if the input result's error value -is a code in the POSIX errno domain. -
- -

The above let you work, somewhat awkwardly, with any C-compatible -basic_result<T, E>. basic_result<T, E> is trivially copyable and -standard layout if its T and E are both so, and it has the C layout:

-
struct cxx_result_##ident
-{
-  union
-  {
-    T value;
-    E error;
-  };
-  unsigned flags;
-};
-
-

<system_error2> support

- -

Because erased status codes are not trivially copyable and -therefore do not have union based storage, we have separate C macros -for results whose E is an erased status code:

- -
-
BOOST_OUTCOME_C_DECLARE_STATUS_CODE(ident, value_type) -
Declares to C a status code type with domain value_type -available at the member variable .value. The ident -must be any identifier fragment unique in this translation unit. It is -used to uniquely identify this status code type in other macros. - -
BOOST_OUTCOME_C_STATUS_CODE(ident) -
A reference to a previously declared status code type with unique -ident. - -
BOOST_OUTCOME_C_DECLARE_RESULT_STATUS_CODE(ident, T, E) -
Declares to C a basic_result type uniquely -identified by ident. T is available at the -member variable .value, and E is available -at the member variable .error. - -
BOOST_OUTCOME_C_RESULT_STATUS_CODE(ident) -
A reference to a previously declared result type with -unique ident. -
- -

There is a high likelihood that C++ functions regularly called by C -code will return their failures either in erased system_code -or in posix_code (i.e. errno code domain). Via querying the -returned value using BOOST_OUTCOME_C_RESULT_ERROR_IS_ERRNO(r), one can determine -if the returned code is in the errno code domain, and thus can be -fed to strerror() and so on. Therefore there are -convenience macro APIs for those particular use cases.

- -
-
BOOST_OUTCOME_C_DECLARE_RESULT_ERRNO(ident, T) -
Declares to C a basic_result<T, posix_code> -type uniquely identified by ident. - -
BOOST_OUTCOME_C_RESULT_ERRNO(ident) -
A reference to a previously declared basic_result<T, posix_code> -type with unique ident. - -
BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(ident, T) -
Declares to C a basic_result<T, system_code> -type uniquely identified by ident. - -
BOOST_OUTCOME_C_RESULT_SYSTEM(ident) -
A reference to a previously declared basic_result<T, system_code> -type with unique ident. -
- - -

Last revised: December 17, 2020 at 11:27:06 UTC

-
-
-Prev - Up - HomeNext
- diff --git a/doc/html/experimental/outcome.html b/doc/html/experimental/outcome.html index e44b8fb5b..dedd5ec99 100644 --- a/doc/html/experimental/outcome.html +++ b/doc/html/experimental/outcome.html @@ -7,9 +7,9 @@
-Prev +Prev Up - HomeNext
+ HomeNext

Tying it all together

@@ -18,7 +18,7 @@
template <class T, class E = outcome_e::error>
 using result =  //
 outcome_e::status_result<T, E, outcome_e::policy::default_status_result_policy<T, E>>;
-
View this code on Github
+
View this code on Github

(The defaulting of default_result_policy is superfluous, it’s already the default)

@@ -66,7 +66,7 @@ printf("\nAnd semantically comparing it to 'errc::no_such_file_or_directory' = %d\n", e == outcome_e::errc::no_such_file_or_directory); } } -View this code on Github +View this code on Github

And running this program yields:

@@ -92,10 +92,10 @@

Conclusion

please do get in touch! This especially includes successful experiences!!!

-

Last revised: February 05, 2019 at 17:14:18 UTC

+

Last revised: July 16, 2024 at 21:33:35 +0100


-Prev +Prev Up - HomeNext
+ HomeNext diff --git a/doc/html/experimental/status_result.html b/doc/html/experimental/status_result.html index 4b98ebd2c..a57b5587a 100644 --- a/doc/html/experimental/status_result.html +++ b/doc/html/experimental/status_result.html @@ -9,7 +9,7 @@
Prev Up - HomeNext
+ HomeNext

status_result and status_outcome

status_result and status_outcome are type aliases to basic_result<T, E, NoValuePolicy> @@ -77,5 +77,5 @@

Prev Up - HomeNext
+ HomeNext
diff --git a/doc/html/experimental/worked-example-long.html b/doc/html/experimental/worked-example-long.html new file mode 100644 index 000000000..b43d096de --- /dev/null +++ b/doc/html/experimental/worked-example-long.html @@ -0,0 +1,57 @@ + + +Worked example: Custom domain (the long way) - Boost.Outcome documentation + + + + + +
+Prev + Up + HomeNext
+ +

Worked example: Custom domain (the long way)

+ + +

Here follows a longer worked example of use of Experimental Outcome. It presents +the same sample program I sent to the San Diego 2018 WG21 standards meeting +after I was asked by the committee to demonstrate how P1095 implements P0709 +in a working code example they could study and discuss.

+ +

We will walk through this worked example, step by step, explaining how each +part works in detail. This will help you implement your own code based on +Experimental Outcome.

+ +

Most users will not need this level of customisation, and for them the preceding +quick and easy approach will be much easier.

+ +

You may find it useful to open now in a separate browser tab the reference API +documentation for proposed <system_error2> at https://ned14.github.io/status-code/ +(scroll half way down). The references in the comments to P1028 are to +P1028 SG14 status_code and standard error object for P0709 Zero-overhead +deterministic exceptions, which is the WG21 proposal +paper for potential <system_error2>.

+ +

Goal of this section

+ +

We are going to define a simple custom code domain which defines that +the status code’s payload will consist of a POSIX error code, and the +__FILE__ and __LINE__ where the failure occurred. This custom status +code will have an implicit conversion to type erased error defined, which dynamically +allocates memory for the original status code, and outputs an error +which manages that dynamic allocation, indirecting all queries etc +to the erased custom status code type such that the error instance +quacks as if just like the original. This demonstrates that error could +just as equally convey a std::exception_ptr, for example, or indeed +manage the lifetime of any pointer.

+ + + +

Last revised: July 16, 2024 at 21:33:35 +0100

+
+
+Prev + Up + HomeNext
+ diff --git a/doc/html/experimental/worked-example/constructor.html b/doc/html/experimental/worked-example-long/constructor.html similarity index 82% rename from doc/html/experimental/worked-example/constructor.html rename to doc/html/experimental/worked-example-long/constructor.html index a1af370c8..0c250116e 100644 --- a/doc/html/experimental/worked-example/constructor.html +++ b/doc/html/experimental/worked-example-long/constructor.html @@ -7,9 +7,9 @@
-Prev - Up - HomeNext
+Prev + Up + HomeNext

The constructor

Code domains are 100% constexpr to construct and destruct, as are status codes. This enables the compiler to 100% instantiate both only in its mind, and to emit @@ -36,7 +36,7 @@ // Do NOT make up your own value. Do NOT use zero. constexpr explicit _file_io_error_domain(typename _base::unique_id_type id = 0x230f170194fcc6c7) noexcept : _base(id) {} static inline constexpr const _file_io_error_domain &get(); -

View this code on Github +View this code on Github

A nice side effect of this approach is that custom error domains in header-only @@ -45,10 +45,10 @@ safe in header only library use cases.

-

Last revised: January 26, 2019 at 23:38:56 UTC

+

Last revised: July 16, 2024 at 21:33:35 +0100


-Prev - Up - HomeNext
+Prev + Up + HomeNext diff --git a/doc/html/experimental/worked-example/implicit_conversion.html b/doc/html/experimental/worked-example-long/implicit_conversion.html similarity index 76% rename from doc/html/experimental/worked-example/implicit_conversion.html rename to doc/html/experimental/worked-example-long/implicit_conversion.html index 19a52c002..376dd6046 100644 --- a/doc/html/experimental/worked-example/implicit_conversion.html +++ b/doc/html/experimental/worked-example-long/implicit_conversion.html @@ -7,11 +7,11 @@
-Prev - Up - HomeNext
+Prev + Up + HomeNext

Implicit conversion

-

Back in The payload, we +

Back in The payload, we mentioned that there was no default implicit conversion of file_io_error (status_code<_file_io_error_domain>) to error, as error is too small to hold _file_io_error_domain::value_type.

@@ -32,7 +32,7 @@ // is guaranteed to do nothing. inline outcome_e::system_code make_status_code(file_io_error v) { - // `make_status_code_ptr()` dynamically allocates memory to store an + // `make_nested_status_code()` dynamically allocates memory to store an // instance of `file_io_error`, then returns a status code whose domain // specifies that its value type is a pointer to `file_io_error`. The // domain is a templated instance which indirects all observers of the @@ -42,19 +42,19 @@ // by definition fits into `intptr_t` and is trivially copyable. // Therefore `system_code` (which is also a type alias to // `status_code<erased<intptr_t>>`) is happy to implicitly construct - // from the status code returned by `make_status_code_ptr()`. - return make_status_code_ptr(std::move(v)); + // from the status code returned by `make_nested_status_code()`. + return make_nested_status_code(std::move(v)); } -
View this code on Github +View this code on Github

We are now ready to use Experimental Outcome!

-

Last revised: January 26, 2019 at 23:38:56 UTC

+

Last revised: July 16, 2024 at 21:33:35 +0100


-Prev - Up - HomeNext
+Prev + Up + HomeNext diff --git a/doc/html/experimental/worked-example/message.html b/doc/html/experimental/worked-example-long/message.html similarity index 88% rename from doc/html/experimental/worked-example/message.html rename to doc/html/experimental/worked-example-long/message.html index 4c9a80837..b2c6a5254 100644 --- a/doc/html/experimental/worked-example/message.html +++ b/doc/html/experimental/worked-example-long/message.html @@ -7,9 +7,9 @@
-Prev - Up - HomeNext
+Prev + Up + HomeNext

Redefining message()

You may remember that our custom _file_io_error_domain inherits from outcome_e::posix_code::domain_type, and thus does not have to @@ -50,14 +50,14 @@ return _base::atomic_refcounted_string_ref(p, length); } }; -

View this code on Github +View this code on Github -

Last revised: January 26, 2019 at 23:38:56 UTC

+

Last revised: July 16, 2024 at 21:33:35 +0100


-Prev - Up - HomeNext
+Prev + Up + HomeNext diff --git a/doc/html/experimental/worked-example/preamble.html b/doc/html/experimental/worked-example-long/preamble.html similarity index 83% rename from doc/html/experimental/worked-example/preamble.html rename to doc/html/experimental/worked-example-long/preamble.html index ce6ff97cf..1bc2b5006 100644 --- a/doc/html/experimental/worked-example/preamble.html +++ b/doc/html/experimental/worked-example-long/preamble.html @@ -7,9 +7,9 @@
-Prev - Up - HomeNext
+Prev + Up + HomeNext

Define a custom code domain

Firstly let’s alias the experimental Outcome namespace into something less tedious to type, declare our custom status code type, and get @@ -31,7 +31,7 @@ class _file_io_error_domain : public outcome_e::posix_code::domain_type { using _base = typename outcome_e::posix_code::domain_type; -

View this code on Github +View this code on Github

Note that we inherit from outcome_e::posix_code::domain_type, not @@ -45,10 +45,10 @@ useful in the next few pages .

-

Last revised: January 26, 2019 at 23:38:56 UTC

+

Last revised: July 16, 2024 at 21:33:35 +0100


-Prev - Up - HomeNext
+Prev + Up + HomeNext diff --git a/doc/html/experimental/worked-example/source.html b/doc/html/experimental/worked-example-long/source.html similarity index 68% rename from doc/html/experimental/worked-example/source.html rename to doc/html/experimental/worked-example-long/source.html index 320a12f80..b56dae017 100644 --- a/doc/html/experimental/worked-example/source.html +++ b/doc/html/experimental/worked-example-long/source.html @@ -7,11 +7,11 @@
-Prev - Up - HomeNext
+Prev + Up + HomeNext

Constexpr domain source

-

Back in The constructor, we +

Back in The constructor, we declared but did not implement a .get() function which returns a constexpr static instance of the domain. We implement this now:

@@ -21,17 +21,17 @@ { return file_io_error_domain; } -
View this code on Github +View this code on Github

As this is 100% constexpr, it can be (and is under optimisation) implemented entirely in the mind of the compiler with no run time representation.

-

Last revised: January 26, 2019 at 23:38:56 UTC

+

Last revised: July 16, 2024 at 21:33:35 +0100


-Prev - Up - HomeNext
+Prev + Up + HomeNext diff --git a/doc/html/experimental/worked-example/string_ref.html b/doc/html/experimental/worked-example-long/string_ref.html similarity index 80% rename from doc/html/experimental/worked-example/string_ref.html rename to doc/html/experimental/worked-example-long/string_ref.html index 701000896..e38bcca4d 100644 --- a/doc/html/experimental/worked-example/string_ref.html +++ b/doc/html/experimental/worked-example-long/string_ref.html @@ -7,9 +7,9 @@
-Prev - Up - HomeNext
+Prev + Up + HomeNext

String refs

<system_error2> does not use std::string to return possibly statically or dynamically allocated strings, and thus avoids dragging in a lot of the @@ -32,7 +32,7 @@ static string_ref v("file i/o error domain"); return v; // NOLINT } -

View this code on Github +View this code on Github

Now you understand what string_ref does, returning the name of the @@ -42,10 +42,10 @@ storage.

-

Last revised: January 26, 2019 at 23:38:56 UTC

+

Last revised: July 16, 2024 at 21:33:35 +0100


-Prev - Up - HomeNext
+Prev + Up + HomeNext diff --git a/doc/html/experimental/worked-example/value_type.html b/doc/html/experimental/worked-example-long/value_type.html similarity index 78% rename from doc/html/experimental/worked-example/value_type.html rename to doc/html/experimental/worked-example-long/value_type.html index 0b753d5e5..7f9857447 100644 --- a/doc/html/experimental/worked-example/value_type.html +++ b/doc/html/experimental/worked-example-long/value_type.html @@ -7,9 +7,9 @@
-Prev - Up - HomeNext
+Prev + Up + HomeNext

The payload

We define the code domain’s value_type – the payload to be transported by status codes using this code domain – to be a POSIX errno value, an integer @@ -26,7 +26,7 @@ const char *file; // from __FILE__ // Could also place a backtrace of void *[16] here ... }; -

View this code on Github +View this code on Github

You will note that this is a TriviallyCopyable type, and so gains an implicit @@ -38,10 +38,10 @@ shall see later.

-

Last revised: January 26, 2019 at 23:38:56 UTC

+

Last revised: July 16, 2024 at 21:33:35 +0100


-Prev - Up - HomeNext
+Prev + Up + HomeNext diff --git a/doc/html/experimental/worked-example.html b/doc/html/experimental/worked-example.html index 91efcb341..2baf149f4 100644 --- a/doc/html/experimental/worked-example.html +++ b/doc/html/experimental/worked-example.html @@ -1,54 +1,85 @@ -Worked example: Custom domain - Boost.Outcome documentation +Worked example: Custom domain (the short way) - Boost.Outcome documentation
-Prev +Prev Up - HomeNext
+ HomeNext
-

Worked example: Custom domain

+

Worked example: Custom domain (the short way)

+

The section after this one will take the long way through defining a custom domain +which sometimes is necessary if you have particularly bespoke requirements. +If however you just want to wrap a custom enum type of yours into its +own custom code domain, the boilerplate can be automated away +by filling in a few simple fields like this:

+
// My custom enum type
+enum class custom_failure
+{
+  not_found,
+  bad_argument
+};
 
-

Here follows a worked example of use of Experimental Outcome. It presents -the same sample program I sent to the San Diego 2018 WG21 standards meeting -after I was asked by the committee to demonstrate how P1095 implements P0709 -in a working code example they could study and discuss.

+// Tell `status_code` to stamp out a custom code domain for this enum type +BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN +template <> struct quick_status_code_from_enum<custom_failure> : quick_status_code_from_enum_defaults<custom_failure> +{ + // Text name of the enum + static constexpr const auto domain_name = "My custom failure"; + // Unique UUID for the enum. PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h + static constexpr const auto domain_uuid = "{be201f65-3962-dd0e-1266-a72e63776a42}"; + // Map of each enum value to its text string, and list of semantically equivalent errc's + static const std::initializer_list<mapping> &value_mappings() + { + static const std::initializer_list<mapping> v = { + // Format is: { enum value, "string representation", { list of errc mappings ... } } + {custom_failure::not_found, "item not found", {errc::no_such_file_or_directory}}, // + {custom_failure::bad_argument, "invoked wrong", {errc::invalid_argument}}, // + }; + return v; + } + // Completely optional definition of mixin for the status code synthesised from `Enum`. It can be omitted. + template <class Base> struct mixin : Base + { + using Base::Base; + constexpr int custom_method() const { return 42; } + }; +}; +BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END +
View this code on Github
-

We will walk through this worked example, step by step, explaining how each -part works in detail. This will help you implement your own code based on -Experimental Outcome.

-

You may find it useful to open now in a separate browser tab the reference API -documentation for proposed <system_error2> at https://ned14.github.io/status-code/ -(scroll half way down). The references in the comments to P1028 are to -P1028 SG14 status_code and standard error object for P0709 Zero-overhead -deterministic exceptions, which is the WG21 proposal -paper for potential <system_error2>.

+

Here we supply the bare minimum requirements for a status code domain:

-

Goal of this section

+
    +
  1. The name in text, so it can be printed.
  2. +
  3. The unique UUID to identify when multiple copies of the domain are the same. +PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h to generate +this, do not twiddle a few bits.
  4. +
  5. For each enum value, its printable text and a sequence of errc:: enumerations +which you think it is semantically equivalent to i.e. its mapping onto generic_code +which is how status code defines the common mapping between status codes. If you later compare the +status code to one of those values (or to another status code which also provides +a mapping), if the generic codes are equivalent then the comparison will return true. +This means code like if(sc == errc::no_such_file_or_directory) ... would match +all custom error codes which mean ‘something was not found’.
  6. +
  7. OPTIONAL: if you would like the custom status code type generated by this +to have additional member functions or additional payload, you can define a mixin +here to inject either data or functions or both. If you omit this, nothing gets +injected.
  8. +
-

We are going to define a simple custom code domain which defines that -the status code’s payload will consist of a POSIX error code, and the -__FILE__ and __LINE__ where the failure occurred. This custom status -code will have an implicit conversion to type erased error defined, which dynamically -allocates memory for the original status code, and outputs an error -which manages that dynamic allocation, indirecting all queries etc -to the erased custom status code type such that the error instance -quacks as if just like the original. This demonstrates that error could -just as equally convey a std::exception_ptr, for example, or indeed -manage the lifetime of any pointer.

- -

Last revised: January 26, 2019 at 23:38:56 UTC

+

Last revised: July 16, 2024 at 21:33:35 +0100


-Prev +Prev Up - HomeNext
+ HomeNext diff --git a/doc/html/experimental/worked-example/implicit-construction.html b/doc/html/experimental/worked-example/implicit-construction.html new file mode 100644 index 000000000..efa3bfb72 --- /dev/null +++ b/doc/html/experimental/worked-example/implicit-construction.html @@ -0,0 +1,62 @@ + + +Implicit construction - Boost.Outcome documentation + + + + + +
+Prev + Up + HomeNext
+

Implicit construction

+

The preceding code had the compiler stamp out a custom status code domain +for a user supplied enum. You now get the following types:

+ +
// This is the status code generated for your custom enum type. It will implicitly construct from
+// values of enum custom_failure.
+using custom_failure_code = BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::quick_status_code_from_enum_code<custom_failure>;
+
+namespace outcome_e = BOOST_OUTCOME_V2_NAMESPACE::experimental;
+
+// You don't usually need to use the status code type explicitly, because this "just works":
+outcome_e::status_result<int> positive_only(int x)
+{
+  if(x < 0)
+  {
+    // Outcome's result sees that status_code will implicitly construct from this enum,
+    // and it returns an errored result
+    return custom_failure::bad_argument;
+  }
+  return x;
+}
+
+// Semantic comparisons work
+bool test(int x)
+{
+  if(auto r = positive_only(x); !r)
+  {
+    if(r.error() == outcome_e::errc::invalid_argument)
+    {
+      std::cerr << "Positive numbers only!" << std::endl;
+      return false;
+    }
+  }
+  return true;
+}
+
View this code on Github
+ + +

As you can see, this is less work than plugging your custom enum +into std::error_code. +It also has C compatibility, and generates better codegen.

+ + +

Last revised: July 16, 2024 at 21:33:35 +0100

+
+
+Prev + Up + HomeNext
+ diff --git a/doc/html/index.html b/doc/html/index.html index 64d649549..1b25d6d6e 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -18,7 +18,7 @@

Outcome 2.2 library

Niall Douglas

-
+

Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -59,6 +59,7 @@

Niall Do
ASIO/Networking TS : Boost < 1.70
ASIO/Networking TS: Boost >= 1.70
Extending BOOST_OUTCOME_TRY
+
Rust FFI
Experimental
@@ -66,8 +67,9 @@

Niall Do
Approximate map between error code designs
Major differences
status_result and status_outcome
-
Worked example: Custom domain
Tying it all together
+
Worked example: Custom domain (the short way)
+
Worked example: Custom domain (the long way)
Using Outcome from C code

API reference
@@ -126,6 +128,8 @@

Introduction

  • there is an external requirement (such as a company-wide policy) that failure handling paths are explicitly indicated in the code.

  • where interoperation with C code, without having to resort to C++ exception wrapper shims, is important.

  • + +
  • where your mostly C code base needs exception-like error handling, and the subset of Outcome’s functionality available in C is sufficient for your needs.

  • Outcome addresses failure handling through returning a special type from functions, which is able to store either a successfully computed value (or void), or the information about failure. Outcome also comes with a set of idioms for dealing with such types.

    @@ -141,7 +145,7 @@

    Introduction

    and generator<T, Executor = void> awaitables which work for any user type.

    -

    Sample usage

    +

    Sample usage (C++)

    The main workhorse in the Outcome library is result<T>: it represents either a successfully computed value of type T, or a std::error_code/boost::system::error_code2 representing the reason for failure. You use it in the function’s return type:

    @@ -174,6 +178,43 @@

    Sample usage

    BOOST_OUTCOME_TRY is a control statement. If the returned result<T> object contains an error information, the enclosing function is immediately returned with result<U> containing the same failure information; otherwise an automatic object of type T is available in scope.

    +

    Sample usage (C)

    + +

    Equivalent to the C++ API: BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(ident, T) declares the C type, thereafter BOOST_OUTCOME_C_RESULT_SYSTEM(ident) refers to it. You use it in the function’s return type:

    + +
    BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(result_string, const char *)
    +
    +BOOST_OUTCOME_C_RESULT_SYSTEM(result_string) data_from_file(const char *path);
    +
    View this code on Github
    + + +

    It is possible to inspect the state manually:

    + +
      BOOST_OUTCOME_C_RESULT_SYSTEM(result_string) rslt = data_from_file("config.cfg");
    +  if(BOOST_OUTCOME_C_RESULT_HAS_VALUE(rslt))
    +    use_string(rslt.value);  // returns string
    +  else
    +    fprintf(stderr, "%s\n", outcome_status_code_message(&rslt.error));
    +
    View this code on Github
    + + +

    Or, if this function is called in another function that also returns BOOST_OUTCOME_C_RESULT_SYSTEM(ident), you can use a dedicated control statement:

    + +
    BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(result_int, int)
    +
    +BOOST_OUTCOME_C_RESULT_SYSTEM(result_int) process(const char *content);
    +
    +BOOST_OUTCOME_C_RESULT_SYSTEM(result_int) int_from_file(const char *path)
    +{
    +  BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(const char *str, result_int, /* cleanup on fail */, data_from_file(path));
    +  // if control gets here data_from_file() has succeeded
    +  return process(str);  // decltype(str) == string
    +}
    +
    View this code on Github
    + + +

    The C Result is guaranteed to be layout identical to its C++ equivalent. Convenience conversion functions are available, but you can reinterpret cast too.

    +
    note

    This library joined the Boost C++ libraries in the 1.70 release (Spring 2019). It can be grafted into much older Boost releases if desired.

    @@ -193,7 +234,7 @@

    Sample usage

    -

    Last revised: March 18, 2022 at 14:45:32 UTC

    +

    Last revised: July 16, 2024 at 21:33:35 +0100


    Prev diff --git a/doc/html/recipes.html b/doc/html/recipes.html index e76b3f1cf..6db9b776d 100644 --- a/doc/html/recipes.html +++ b/doc/html/recipes.html @@ -18,7 +18,9 @@ ASIO/Networking TS: Boost >= 1.70

    How to teach ASIO/Networking TS about Outcome.

  • Extending BOOST_OUTCOME_TRY -

    How to informing BOOST_OUTCOME_TRY about foreign Result types.

  • +

    How to informing BOOST_OUTCOME_TRY about foreign Result types.

  • + Rust FFI +

    How to teach Rust about result<T>.

  • diff --git a/doc/html/recipes/foreign-try.html b/doc/html/recipes/foreign-try.html index a3fd99a97..9ccda001c 100644 --- a/doc/html/recipes/foreign-try.html +++ b/doc/html/recipes/foreign-try.html @@ -9,7 +9,7 @@
    Prev Up - HomeNext
    + HomeNext

    Extending BOOST_OUTCOME_TRY

    @@ -174,5 +174,5 @@

    A very foreign pseudo-Expected type
    Prev Up - HomeNext
    + HomeNext

    diff --git a/doc/html/recipes/rust.html b/doc/html/recipes/rust.html new file mode 100644 index 000000000..e05186ddd --- /dev/null +++ b/doc/html/recipes/rust.html @@ -0,0 +1,146 @@ + + +Rust FFI - Boost.Outcome documentation + + + + + +
    +Prev + Up + HomeNext
    +

    Rust FFI

    +

    A nice side effect of Outcome.Experimental’s excellent C support +is that teaching Rust about Outcome’s result<T> becomes trivially +easy. C and C++ results propagate losslessly into Rust Results, and the +full power of the Outcome C API is available to Rust code for semantic +equivalence comparison et al.

    + +

    Here’s a quick snippet to get you started. This assumes that you have declared +your C result using BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(outcome, intptr_t) in order +to produce a C result named “outcome” compatible with an erased system code C++ result:

    +
    // Rust representation of an Outcome.Experimental Result
    +pub type OutcomeCResult<T> = Result<T, cxx_status_code_system>;
    +
    +unsafe impl Send for cxx_status_code_system {}
    +unsafe impl Sync for cxx_status_code_system {}
    +
    +impl std::fmt::Display for cxx_status_code_system {
    +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    +        write!(
    +            f,
    +            "{}",
    +            String::from_utf8_lossy(unsafe {
    +                CStr::from_ptr(outcome_status_code_message(
    +                    self as *const _ as *const ::std::os::raw::c_void,
    +                ))
    +                .to_bytes()
    +            })
    +        )
    +    }
    +}
    +
    +impl std::fmt::Debug for cxx_status_code_system {
    +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    +        write!(
    +            f,
    +            "{}",
    +            String::from_utf8_lossy(unsafe {
    +                CStr::from_ptr(outcome_status_code_message(
    +                    self as *const _ as *const ::std::os::raw::c_void,
    +                ))
    +                .to_bytes()
    +            })
    +        )
    +    }
    +}
    +
    +// Tell Rust that this meets the `Error` trait
    +impl std::error::Error for cxx_status_code_system {
    +    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
    +        None
    +    }
    +}
    +
    +// Tell Rust how to make an `io::Result<T>` from a `OutcomeCResult<T>`
    +impl From<cxx_status_code_system> for std::io::Error {
    +    fn from(val: cxx_status_code_system) -> Self {
    +        // This is nasty, and we ought to figure out a better way of doing this
    +        for n in 1..200 {
    +            if unsafe {
    +                outcome_status_code_equal_generic(
    +                    &val as *const cxx_status_code_system as *const ::std::os::raw::c_void,
    +                    n,
    +                ) != 0
    +            } {
    +                return Self::new(Self::from_raw_os_error(n).kind(), val);
    +            }
    +        }
    +        Self::new(std::io::ErrorKind::Other, val)
    +    }
    +}
    +
    +impl std::fmt::Debug for cxx_result_status_code_system_outcome {
    +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    +        if (self.flags & 1) == 1 {
    +            let v: OutcomeCResult<isize> = Ok(self.value);
    +            return v.fmt(f);
    +        } else {
    +            let v: OutcomeCResult<isize> = Err(self.error);
    +            return v.fmt(f);
    +        }
    +    }
    +}
    +
    +pub fn to_result(res: cxx_result_status_code_system_outcome) -> OutcomeCResult<isize> {
    +    if (res.flags & 1) == 1 {
    +        return Ok(res.value);
    +    }
    +    Err(res.error)
    +}
    +
    +pub fn success(val: isize) -> outcome_c_result {
    +    // You will need to export BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_SUCCESS(outcome, v) as
    +    // FFI outcome_c_make_success()
    +    unsafe { outcome_c_make_success(val) }
    +}
    +
    +pub fn failure_from_errno(val: ::std::os::raw::c_int) -> outcome_c_result {
    +    // You will need to export BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FAILURE_SYSTEM(outcome, v) as
    +    // FFI outcome_c_make_failure()
    +    unsafe { outcome_c_make_failure(val) }
    +}
    +
    +

    Let’s say there is an FFI function like this:

    +
    unsafe extern "C" {
    +    pub fn file_read(
    +        arg1: *mut db,
    +        buffer: *mut u8,
    +        bytes: usize,
    +    ) -> outcome_c_result;
    +}
    +
    +

    You can now do:

    +
    // Make a Rust Result equivalent to the Outcome Result
    +let res = to_result(unsafe { file_read(db, buffer, 256) });
    +// Asks Outcome for the message automatically
    +println!("Message: {}", res.err().unwrap());
    +
    +

    You can use the standard Rust ? to TRY Rust Results, same as anywhere else in Rust.

    + +

    As we taught Rust how to lossy map OutcomeCResult<T> into an io::Result<T>, you +can also ? from an Outcome C Result function into an i/o Result function. Note that +this is lossy, and you may not wish to allow that by removing the From trait +definition above.

    + +

    Easy as pie!

    + + +

    Last revised: April 30, 2025 at 22:36:03 +0100

    +
    +
    +Prev + Up + HomeNext
    + diff --git a/doc/html/reference.html b/doc/html/reference.html index a69ed9261..fbde8745e 100644 --- a/doc/html/reference.html +++ b/doc/html/reference.html @@ -7,7 +7,7 @@
    -Prev +Prev Up HomeNext
    @@ -202,7 +202,7 @@

    Last revised: December 10, 2018 at 20:32:00 UTC


    -Prev +Prev Up HomeNext
    diff --git a/doc/html/reference/policies/base/ub.html b/doc/html/reference/policies/base/ub.html index cbae7514f..1640fb37b 100644 --- a/doc/html/reference/policies/base/ub.html +++ b/doc/html/reference/policies/base/ub.html @@ -15,7 +15,7 @@

    This may seem highly undesirable. However, it also means that the optimiser can optimise more strongly, and so long as you never actually do execute this branch, you do get higher quality code generation.

    -

    If the NDEBUG macro is not defined, an assert(false) is present. This will cause attempts to execute this function to fail in a very obvious way, but it also generates runtime code to trigger the obvious failure.

    +

    If the NDEBUG macro is not defined, an BOOST_OUTCOME_ASSERT(false) is present. This will cause attempts to execute this function to fail in a very obvious way, but it also generates runtime code to trigger the obvious failure.

    If the NDEBUG macro is defined, and the program is compiled with the undefined behaviour sanitiser, attempts to execute this function will trigger an undefined behaviour sanitiser action.

    @@ -26,7 +26,7 @@

    Guarantees: An exception is never thrown.

    -

    Last revised: January 22, 2019 at 01:11:40 UTC

    +

    Last revised: December 16, 2023 at 20:51:26 UTC


    Prev diff --git a/doc/html/requirements.html b/doc/html/requirements.html index 1b7e5f6b2..68ecb8abf 100644 --- a/doc/html/requirements.html +++ b/doc/html/requirements.html @@ -30,19 +30,28 @@ performance improvements. Any Concepts TS or Coroutines TS implemented by your compiler is automatically detected and used.

    -

    Partially working compilers (this was last updated January 2019):

    +

    Known compiler issues (this was last updated April 2023):

      -
    • clang 3.5 - 3.9 can compile varying degrees of the test suite, the -problem is lack of complete and unbuggy C++ 14 language support.
    • -
    • Older point releases of GCCs 7 and 8 have internal compiler error bugs +
    • clang 3.5 - 3.9 can compile varying degrees of the test suite, the +problem is lack of complete and unbuggy C++ 14 language support.

    • + +
    • Older point releases of GCCs 7 and 8 have internal compiler error bugs in their constexpr implementation which tend to be triggered by using Outcome in constexpr. If you don’t use Outcome in constexpr, you won’t see these problems. If you need your GCC to not ICE, upgrade to the -very latest point release, the constexpr ICE has been since fixed.

    • -
    • Early editions of Visual Studio 2017 have many corner case problems. -The latest point release, VS2017.9, only has a few known problems, -and should be relatively unsurprising for most use cases.
    • +very latest point release, the constexpr ICE has been since fixed.

      + +
    • Early editions of Visual Studio 2017 have many corner case problems. +From VS2017.9 onwards there remain a number of usually untroublesome corner +case issues, but use should be relatively unsurprising for most use cases. +Be aware that only from Visual Studio 2022 onwards are almost all corner +case problems fixed.

    • + +
    • Some point releases of GCC 10 with libstdc++ 10 can induce an infinite +template instantiation, which fails the build for some rare use cases. Earlier +or later GCCs or different point releases of the 10 series do not have +this issue.


    @@ -61,7 +70,7 @@ -

    Last revised: February 25, 2020 at 11:15:13 UTC

    +

    Last revised: April 18, 2023 at 20:57:50 +0100


    Prev diff --git a/doc/html/sitemap.xml b/doc/html/sitemap.xml index 340e9be5b..3c8783605 100644 --- a/doc/html/sitemap.xml +++ b/doc/html/sitemap.xml @@ -4,7 +4,7 @@ /requirements.html - 2020-02-25T11:15:13+00:00 + 2023-04-18T20:57:50+01:00 @@ -24,7 +24,7 @@ /experimental/advantages.html - 2019-02-05T21:41:47+00:00 + 2024-07-16T21:33:35+01:00 @@ -57,14 +57,19 @@ 2020-12-16T14:14:42+00:00 + + /experimental/c-api/from-c/system_code.html + 2024-07-17T17:54:05+01:00 + + /tutorial/essential/coroutines/try.html 2020-04-07T10:22:38+01:00 - /experimental/worked-example/preamble.html - 2019-01-26T23:38:56+00:00 + /experimental/worked-example-long/preamble.html + 2024-07-16T21:33:35+01:00 @@ -78,13 +83,13 @@ - /tutorial/advanced/hooks/keeping_state.html - 2019-02-08T22:18:08+00:00 + /experimental/worked-example/implicit-construction.html + 2024-07-16T21:33:35+01:00 - /experimental/c-api/limitations.html - 2019-02-05T17:14:18+00:00 + /tutorial/advanced/hooks/keeping_state.html + 2019-02-08T22:18:08+00:00 @@ -162,6 +167,11 @@ 2019-02-09T15:18:26+00:00 + + /experimental/c-api/from-cxx.html + 2024-07-16T21:33:35+01:00 + + /reference/concepts.html 2018-12-11T14:56:04+00:00 @@ -173,8 +183,13 @@ - /experimental/c-api/example.html - 2019-02-05T17:14:18+00:00 + /experimental/c-api/from-c/declare.html + 2024-07-16T21:33:35+01:00 + + + + /experimental/c-api/from-cxx/example.html + 2024-07-16T21:33:35+01:00 @@ -193,8 +208,8 @@ - /experimental/worked-example/value_type.html - 2019-01-26T23:38:56+00:00 + /experimental/worked-example-long/value_type.html + 2024-07-16T21:33:35+01:00 @@ -243,8 +258,13 @@ - /experimental/c-api/example2.html - 2019-02-05T17:14:18+00:00 + /experimental/c-api/from-c.html + 2024-07-17T17:54:05+01:00 + + + + /experimental/c-api/from-cxx/example2.html + 2024-07-16T21:33:35+01:00 @@ -303,8 +323,8 @@ - /experimental/worked-example/constructor.html - 2019-01-26T23:38:56+00:00 + /experimental/worked-example-long/constructor.html + 2024-07-16T21:33:35+01:00 @@ -312,6 +332,11 @@ 2018-12-13T17:36:11+00:00 + + /experimental/c-api/from-c/use.html + 2024-07-16T21:33:35+01:00 + + /alternatives/expected.html 2022-01-10T14:29:13+00:00 @@ -363,8 +388,13 @@ - /experimental/worked-example/string_ref.html - 2019-01-26T23:38:56+00:00 + /experimental/worked-example-long/string_ref.html + 2024-07-16T21:33:35+01:00 + + + + /experimental/c-api/from-c/try.html + 2024-07-16T21:33:35+01:00 @@ -402,11 +432,6 @@ 2019-06-24T21:48:18+01:00 - - /experimental/c-api/reference.html - 2020-12-17T11:27:06+00:00 - - /tutorial/advanced/hooks/hook_outcome.html 2020-12-15T12:22:39+00:00 @@ -433,8 +458,8 @@ - /experimental/worked-example/message.html - 2019-01-26T23:38:56+00:00 + /experimental/worked-example-long/message.html + 2024-07-16T21:33:35+01:00 @@ -473,8 +498,8 @@ - /experimental/worked-example/source.html - 2019-01-26T23:38:56+00:00 + /experimental/worked-example-long/source.html + 2024-07-16T21:33:35+01:00 @@ -488,13 +513,18 @@ - /experimental/worked-example/implicit_conversion.html - 2019-01-26T23:38:56+00:00 + /experimental/worked-example-long/implicit_conversion.html + 2024-07-16T21:33:35+01:00 + + + + /experimental/outcome.html + 2024-07-16T21:33:35+01:00 /changelog.html - 2023-02-22T15:06:13+00:00 + 2025-10-08T16:10:15+01:00 @@ -508,13 +538,13 @@ - /experimental/outcome.html - 2019-02-05T17:14:18+00:00 + /experimental/worked-example.html + 2024-07-16T21:33:35+01:00 - /experimental/worked-example.html - 2019-01-26T23:38:56+00:00 + /experimental/worked-example-long.html + 2024-07-16T21:33:35+01:00 @@ -539,7 +569,7 @@ /reference/policies/base/ub.html - 2019-01-22T01:11:40+00:00 + 2023-12-16T20:51:26+00:00 @@ -1440,14 +1470,14 @@ - /categories/constructors.html - 2020-12-17T11:27:06+00:00 + /tags/constructors.html + 2019-06-24T21:48:18+01:00 0 - /tags/constructors.html - 2019-06-24T21:48:18+01:00 + /categories/constructors.html + 2020-12-17T11:27:06+00:00 0 @@ -1548,7 +1578,7 @@ / - 2022-03-18T14:45:32+00:00 + 2024-07-16T21:33:35+01:00 @@ -1671,9 +1701,20 @@ 0 + + /tags/rust.html + 2025-04-30T22:36:03+01:00 + 0 + + + + /recipes/rust.html + 2025-04-30T22:36:03+01:00 + + /categories/special.html - 2019-01-22T01:11:40+00:00 + 2023-12-16T20:51:26+00:00 0 diff --git a/doc/html/tags.html b/doc/html/tags.html index c185f7890..0b86b591a 100644 --- a/doc/html/tags.html +++ b/doc/html/tags.html @@ -15,7 +15,7 @@
    diff --git a/doc/html/tags/page/4.html b/doc/html/tags/page/4.html new file mode 100644 index 000000000..d2e2f16c2 --- /dev/null +++ b/doc/html/tags/page/4.html @@ -0,0 +1,50 @@ + + +Tags - Boost.Outcome documentation + + + + + +
    +Prev + + HomeNext
    + + + +
    + + +
    + + + + + +
    + + + + + + +

    Last revised: December 15, 2020 at 12:22:39 UTC

    +
    +
    +Prev + + HomeNext
    + diff --git a/doc/html/tags/rust.html b/doc/html/tags/rust.html new file mode 100644 index 000000000..0367634ff --- /dev/null +++ b/doc/html/tags/rust.html @@ -0,0 +1,45 @@ + + +Rust - Boost.Outcome documentation + + + + + +
    + + HomeNext
    + + + +
    + + +
    + + + + + +
    + + + + + + +

    Last revised: April 30, 2025 at 22:36:03 +0100

    +
    +
    +Prev + + HomeNext
    + diff --git a/doc/html/tags/rust/page/1.html b/doc/html/tags/rust/page/1.html new file mode 100644 index 000000000..38672b819 --- /dev/null +++ b/doc/html/tags/rust/page/1.html @@ -0,0 +1 @@ +/tags/rust.html \ No newline at end of file diff --git a/doc/html/tutorial/advanced/constructors/file_handle.html b/doc/html/tutorial/advanced/constructors/file_handle.html index 2bfc4961c..73f51e35e 100644 --- a/doc/html/tutorial/advanced/constructors/file_handle.html +++ b/doc/html/tutorial/advanced/constructors/file_handle.html @@ -42,7 +42,11 @@ // Moves but not copies permitted file_handle(const file_handle &) = delete; - file_handle(file_handle &&o) noexcept : _fd(o._fd) { o._fd = -1; } + file_handle(file_handle &&o) noexcept + : _fd(o._fd) + { + o._fd = -1; + } file_handle &operator=(const file_handle &) = delete; file_handle &operator=(file_handle &&o) noexcept { diff --git a/doc/html/tutorial/advanced/constructors/metaprogrammg1.html b/doc/html/tutorial/advanced/constructors/metaprogrammg1.html index 2253d0c10..a026b304f 100644 --- a/doc/html/tutorial/advanced/constructors/metaprogrammg1.html +++ b/doc/html/tutorial/advanced/constructors/metaprogrammg1.html @@ -19,7 +19,7 @@ { std::cerr << "Opening file 'hello' failed with " << fh1.error().message() << std::endl; } -
    View this code on Github
    +View this code on Github

    … and be done with it.

    @@ -31,7 +31,7 @@ { std::cerr << "Opening file 'hello' failed with " << fh2.error().message() << std::endl; } -View this code on Github +View this code on Github

    The eye is immediately drawn to the two-stage invocation pattern, so we are diff --git a/doc/html/tutorial/advanced/constructors/metaprogrammg2.html b/doc/html/tutorial/advanced/constructors/metaprogrammg2.html index 67829e2d6..d6ed2bcf6 100644 --- a/doc/html/tutorial/advanced/constructors/metaprogrammg2.html +++ b/doc/html/tutorial/advanced/constructors/metaprogrammg2.html @@ -21,7 +21,7 @@ "make<T>() was not specialised for the type T supplied"); } }; -View this code on Github +View this code on Github

    This fails a static assert if the type is ever instantiated unspecialised.

    @@ -39,7 +39,7 @@ return file_handle::file(std::move(_path)); } }; -View this code on Github +View this code on Github

    Because this is a struct, we can list initialise make, and use diff --git a/doc/html/tutorial/advanced/constructors/static-constructor.html b/doc/html/tutorial/advanced/constructors/static-constructor.html index e012967af..bae9a6944 100644 --- a/doc/html/tutorial/advanced/constructors/static-constructor.html +++ b/doc/html/tutorial/advanced/constructors/static-constructor.html @@ -37,7 +37,7 @@ default: return std::errc::invalid_argument; } - ret._fd = ::open(path.u8string().c_str(), flags); + ret._fd = ::open((const char *) path.u8string().c_str(), flags); if(-1 == ret._fd) { // Note that if we bail out here, ~file_handle() will correctly not call ::close() @@ -53,7 +53,7 @@ // so be explicit by wrapping into an initialiser list with embedded move. return {std::move(ret)}; } -View this code on Github +View this code on Github

    The static member function implementing phase 2 firstly calls phase 1 diff --git a/doc/src/content/_index.md b/doc/src/content/_index.md index 46289de5b..d15accb3b 100644 --- a/doc/src/content/_index.md +++ b/doc/src/content/_index.md @@ -34,6 +34,8 @@ Outcome is a set of tools for reporting and handling function failures in contex - where interoperation with C code, without having to resort to C++ exception wrapper shims, is important. + - where your mostly C code base needs exception-like error handling, and the subset of Outcome's functionality available in C is sufficient for your needs. + Outcome addresses failure handling through returning a special type from functions, which is able to store either a successfully computed value (or `void`), or the information about failure. Outcome also comes with a set of idioms for dealing with such types. Particular care has been taken to ensure that Outcome has the lowest possible impact on build times, @@ -44,7 +46,7 @@ Fully deterministic all-`noexcept` C++ Coroutine support in Outcome is particula supply Outcome-optimising {{< api "eager/atomic_eager" >}}, {{< api "lazy/atomic_lazy" >}} and {{" >}} awaitables which work for any user type. -## Sample usage +## Sample usage (C++) The main workhorse in the Outcome library is `result`: it represents either a successfully computed value of type `T`, or a `std::error_code`/`boost::system::error_code`[^2] representing the reason for failure. You use it in the function's return type: @@ -61,6 +63,22 @@ Or, if this function is called in another function that also returns `result` `BOOST_OUTCOME_TRY` is a control statement. If the returned `result` object contains an error information, the enclosing function is immediately returned with `result` containing the same failure information; otherwise an automatic object of type `T` is available in scope. +## Sample usage \(C) + +Equivalent to the C++ API: `BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(ident, T)` declares the C type, thereafter `BOOST_OUTCOME_C_RESULT_SYSTEM(ident)` refers to it. You use it in the function's return type: + +{{% snippet "intro_c_example.cpp" "signature" %}} + +It is possible to inspect the state manually: + +{{% snippet "intro_c_example.cpp" "inspect" %}} + +Or, if this function is called in another function that also returns `BOOST_OUTCOME_C_RESULT_SYSTEM(ident)`, you can use a dedicated control statement: + +{{% snippet "intro_c_example.cpp" "implementation" %}} + +The C Result is guaranteed to be layout identical to its C++ equivalent. Convenience conversion functions are available, but you can reinterpret cast too. + {{% notice note %}} This library joined [the Boost C++ libraries](https://www.boost.org/doc/libs/develop/libs/outcome/doc/html/index.html) in the 1.70 release (Spring 2019). [It can be grafted into much older Boost releases if desired](https://github.com/boostorg/outcome). {{% /notice %}} diff --git a/doc/src/content/changelog/_index.md b/doc/src/content/changelog/_index.md index 893cddaba..01ef5703e 100644 --- a/doc/src/content/changelog/_index.md +++ b/doc/src/content/changelog/_index.md @@ -3,6 +3,155 @@ title = "Changelog" weight = 80 +++ +--- +## v2.2.14 ? (Boost 1.90) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.14) + +### Enhancements: + +[#314](https://github.com/ned14/outcome/issues/314) +- Bump Boost min cmake required to 3.10 to match standalone Outcome. Also bump minium cmake to 3.10 +everywhere else in Outcome, as CI is now failing due to us requested too old a cmake. + +### Bug fixes: + + +--- +## v2.2.13 6th August 2025 (Boost 1.89) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.13) + +### Enhancements: + +[#312](https://github.com/ned14/outcome/issues/312) +- `iostream_support.hpp` has been split into `iostream_support_result.hpp` and `iostream_support.hpp`. + +[#313](https://github.com/ned14/outcome/issues/313) +- Bump min cmake required to 3.10 amongst other cmake modernisation fixes to please cmake 4.0. + +--- +## v2.2.12 10th April 2025 (Boost 1.88) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.12) + +### Bug fixes: + +- The Android build had become broken, fixed. + +--- +## v2.2.11 12th December 2024 (Boost 1.87) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.11) + +### Enhancements: + +- Outcome.Experimental has had C representation support since the beginning, however it had +been mainly intended that C++ would originate Results, they would pass through C, and back +into C++. It hadn't really been expected that C would want to do much with Results other than +inspect them for happy or sad path. + + It turns out there is more demand than expected for a more functional Result from within C, +so this release adds the power to create Results in success and two types of failure, semantic +comparison of Results, and printing of Result messages. You can also wrap a C enum into a +quick status code from enum, allowing easy custom C error coding from 100% within C. + + [The documentation for the C support]({{% relref "../experimental/c-api" %}}) has been updated +to reflect the new facilities. + +### Bug fixes: + +- This was fixed in Standalone Outcome in the last release, but the fix came too late for Boost.Outcome +which ended up shipping with inline GDB pretty printers with the wrong escaping which caused +failure to load. + +--- +## v2.2.10 14th August 2024 (Boost 1.86) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.10) + +### Enhancements: + +- Something I've been meaning to do for far too long now is make the GDB pretty printers +auto-loading so you don't have to set up `.gdbinit`. This is now done. I also improved +the pretty printers to also pretty print the C result type which can be very useful if +working with that type, as it will print the error message in GDB. + + Experimental Outcome's `status_code` has also gained its own auto-loading GDB pretty printer +with display of `strerror()` if the code domain is POSIX or generic. + +### Bug fixes: + +- The `status` enumeration used to track state internally did not list all possible enum +values. This caused static analysers to complain. + +--- +## v2.2.9 15th April 2024 (Boost 1.85) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.9) + +### Enhancements: + +[#293](https://github.com/ned14/outcome/issues/293) +- Some users wished that Outcome would be clean with `-Wpedantic`, this is now turned on for +the test suite. + +[#294](https://github.com/ned14/outcome/issues/294) +- All use of `assert()` has been replaced with `BOOST_OUTCOME_ASSERT`, which can be user overridden +at compile time. + +[#295](https://github.com/ned14/outcome/issues/295) +- In git commit 12b14e1533848e9a0f7f3c38e41da0ee4e819770 (Aug 11 2022) status code had its +paths changed due to its headers not previously having the right path convention. It was not +realised at the time that in Boost.Outcome this resulted in +`` which is not desirable. +This has now been remedied to remove the double `status-code`, which will obviously break +any Boost.Outcome code which relies on the double `status-code`. Standalone Outcome is unaffected. + +--- +## v2.2.8 13th December 2023 (Boost 1.84) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.8) + +### Enhancements: + +- cmake 3.9 is now the minimum required for standalone Outcome. This fixes a long standing +cmake issue with probing for standard library facilities. cmake 3.9 is what RHEL7 ships with, +when RHEL7 EOLs we may raise the minimum cmake version at that point. + +### Bug fixes: + +- There was a bug in the Outcome C++ coroutine awaitables whereby we were over eagerly resuming +execution of coroutines which return one of our awaitables. It is surprising how many years have +passed before this was noticed, but it is now fixed. It is believed that this has been fixed +without affecting ABI stability, however mixing old Outcome and new Outcome in the same binary +without recompiling all the C++ coroutine code to use new Outcome will not fix the bug. + +[#291](https://github.com/ned14/outcome/issues/291) +- A Result or Outcome with `void` value type and move-only non-value type was only usable in +const use cases, due to the lack of provision of non-const member functions in relevant observers +injection layers for the `void` specialisation. The missing non-const member functions have now +been added. + +--- +## v2.2.7 13th August 2023 (Boost 1.83) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.7) + +### Enhancements: + +- Update the list of known compiler issues in the docs. + +- Update Outcome.Experimental to match latest changes requested of `status_code` by WG21. +This as usual will cause minor breakage due to LEWG renaming of things. + +- Outcome previously took addresses of things not using `std::addressof()`, and until now +nobody complained because custom `operator&` which doesn't return an address is an +abomination not used in much modern C++. But finally someone did complain, so +for both normal Outcome and Experimental.Outcome, if you set `BOOST_OUTCOME_USE_STD_ADDRESSOF = 1`, +Outcome will use `std::addressof()` + +### Bug fixes: + +[#273](https://github.com/ned14/outcome/issues/273) +- Changes to other Boost libraries had caused Boost.Outcome's test suite to fail to compile for some +compiler and C++ language configurations in recent releases. Thanks to work contributed by @alandefreitas +and @pdimov, Boost.Outcome now CI tests a wide range of compilers and configurations and it +is believed all those corner case issues have been fixed or worked around, for the compilers +and configurations within that CI matrix. + + Standalone Outcome's test suite was never affected, as it did not have Boost changing underneath it. +Nevertheless, a few of the compiler parse bug workarounds will have improved compatibility there +too for atyical toolchain choices. + +- Experimental.Outcome now supports big endian architectures. Implementation for them simply wasn't done +before under the assumption that nobody would be using Experimental.Outcome on big endian architectures. +Turns out that was a wrong assumption! + --- ## v2.2.6 24th March 2023 (Boost 1.82) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.6) @@ -299,7 +448,7 @@ use cases. Precompiled headers are automatically enabled on new enough cmake's for standalone Outcome : If on cmake 3.16 or later, its new precompiled headers build support is used to tell consumers of the `outcome::hl` cmake target to precompile Outcome, **if -and only if** `PROJECT_IS_DEPENDENCY` is false. `PROJECT_IS_DEPENDENCY` is set +and only if** `outcome_IS_DEPENDENCY` is false. `outcome_IS_DEPENDENCY` is set by Outcome's CMakeLists.txt if it detects that it was included using `add_subdirectory()`, so for the vast majority of Outcome end users, the use of precompiled headers will NOT be enabled. diff --git a/doc/src/content/experimental/advantages.md b/doc/src/content/experimental/advantages.md index 3175c5d1c..8ec6cedbd 100644 --- a/doc/src/content/experimental/advantages.md +++ b/doc/src/content/experimental/advantages.md @@ -28,6 +28,9 @@ and the dual target source code, being written to tighter discipline, is faster and more deterministic in the default target than it was before the (non-trivial) port to ``. +5. If you want 'official' C support, experimental Outcome is able to +provide that in a way not possible for default Outcome which cannot make +sufficiently strong C compatibility assumptions about `std::error_code`. If you are building a codebase on top of Outcome expecting long term maintenance, the author's personal recommendation is that you design, write, test and diff --git a/doc/src/content/experimental/c-api/from-c/_index.md b/doc/src/content/experimental/c-api/from-c/_index.md new file mode 100644 index 000000000..af8b71612 --- /dev/null +++ b/doc/src/content/experimental/c-api/from-c/_index.md @@ -0,0 +1,51 @@ ++++ +title = "C Results" +description = "Outcome's C Result support" +weight = 30 ++++ + +The C macro API header `` has some macros for working with any kind of Result: + +

    +
    BOOST_OUTCOME_C_DECLARE_RESULT(ident, T, E) +
    Declares to C a basic_result type uniquely +identified by ident. T is available at the +member variable .value, and E is available +at the member variable .error. If you call this from within +C++, make SURE it is not within a extern "C" block! + +
    BOOST_OUTCOME_C_RESULT(ident) +
    A reference to a previously declared result type with +unique ident. + +
    BOOST_OUTCOME_C_RESULT_HAS_VALUE(r) +
    Evaluates to 1 (true) if the input result has a value. + +
    BOOST_OUTCOME_C_RESULT_HAS_ERROR(r) +
    Evaluates to 1 (true) if the input result has an error. + +
    BOOST_OUTCOME_C_RESULT_ERROR_IS_ERRNO(r) +
    Evaluates to 1 (true) if the input result's error value +is a code in the POSIX errno domain. +
    + +The above let you work, somewhat awkwardly, with any C-compatible +`basic_result`. `basic_result` is trivially copyable and +standard layout if its `T` and `E` are both so, and it has the C layout: + +```c++ +struct cxx_result_##ident +{ + union + { + T value; + E error; + }; + unsigned flags; +}; +``` + +Note that this layout is different to that of [`BOOST_OUTCOME_C_DECLARE_STATUS_CODE`]({{% relref "../from-c" %}}) +as the C++ `result` has a different layout if `E` is a status code. + + diff --git a/doc/src/content/experimental/c-api/from-c/declare.md b/doc/src/content/experimental/c-api/from-c/declare.md new file mode 100644 index 000000000..1e53443a9 --- /dev/null +++ b/doc/src/content/experimental/c-api/from-c/declare.md @@ -0,0 +1,11 @@ ++++ +title = "Declare a Result" +description = "Declaring a C Result" +weight = 20 ++++ + +{{% snippet "c_api2.cpp" "preamble" %}} + +The key to making C programming easy is to alias the long complex things +into short easy thing. Obviously `SUCCESS(expr)` and `FAILURE(expr)` is too +generic, but for the purposes of this documentation it makes thing easier. diff --git a/doc/src/content/experimental/c-api/from-c/system_code.md b/doc/src/content/experimental/c-api/from-c/system_code.md new file mode 100644 index 000000000..2f3e62106 --- /dev/null +++ b/doc/src/content/experimental/c-api/from-c/system_code.md @@ -0,0 +1,101 @@ ++++ +title = "C system error results" +description = "Status code's `std::error` in C" +weight = 10 ++++ + +In v2.2.11, C Result support went from second tier to first tier status, and +now you can create, query and manipulate a subset of Result types entirely from +within C by including ``. + +The subset supported are those `result` which are [a `status_result`]({{% relref "../../status_result" %}}) +i.e. the `E` is hardcoded to `experimental::error` which is the type erased runtime +polymorphic holder for any errored `status_code` whose payload is not bigger +than an `intptr_t`. This is the most useful subset of Outcome Experimental's +possible Result types, allowing arbitrary custom error coding schemes from +any unknown source to work seamlessly with all others, including errors from +the system or third party libraries. + +The operations available to C are: + +
    +
    BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(ident, T) +
    Declares to C a status_result type uniquely +identified by ident. T is available at the +member variable .value, and struct cxx_status_code_system +is available at the member variable .error. If in C++, +implements C extern functions for making successful and failure results +of this type. If you call this from within +C++, make SURE it is not within a extern "C" block! + +
    BOOST_OUTCOME_C_RESULT_SYSTEM(ident) +
    A reference to a previously declared status_result type with +unique ident. + +
    BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_SUCCESS(ident, expr) (needs C++ counterpart linked into final binary) +
    This invokes the aforementioned extern function which creates a status_result +with a successful value of type T. +
    BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FAILURE_POSIX(ident, expr) (needs C++ counterpart linked into final binary) +
    This invokes the aforementioned extern function which creates a status_result +with a failure of type posix_code representing a POSIX errno. +
    BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FAILURE_SYSTEM(ident, expr) (needs C++ counterpart linked into final binary) +
    This invokes the aforementioned extern function which creates a status_result +with a failure of type posix_code representing a POSIX errno +if on POSIX; if on Windows then a failure of type win32_code +representing a Win32 error code from a Windows API. + +

    +
    BOOST_OUTCOME_C_RESULT_HAS_VALUE(r) +
    Evaluates to 1 (true) if the input result has a value. + +
    BOOST_OUTCOME_C_RESULT_HAS_ERROR(r) +
    Evaluates to 1 (true) if the input result has an error. + +
    BOOST_OUTCOME_C_RESULT_ERROR_IS_ERRNO(r) +
    Evaluates to 1 (true) if the input result's error value +is a code in the POSIX errno domain. +

    +
    BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(expr) +
    If the status_result returned by expr is +errored, exit the current function returning the result. This obviously +requires that the return type of the current function matches that of +expr. + +
    BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(cleanup, expr) +
    Same as the above, but execute cleanup just before exiting the function +if returning failure. + +
    BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(var, cleanup, expr) +
    Same as the above, but set var equal to the result's .value on success. + +
    BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(var, ident, cleanup, expr) +
    Same as the above, but use ident as the return type instead. This allows +the return type of the calling function to differ from that of expr. +

    +
    BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, uuid, {enum mapping-sequence, ...}) +
    This declares to C an extern function which creates a status_result +from a C enum. If in C++, it implements a quick_status_code_from_enum for +the C enum and the associated extern function, and you will need to supply uuid +and the appropriate enum value mapping sequence +as per the quick_status_code_from_enum documentation. +
    BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, expr) (needs C++ counterpart linked into final binary) +
    This invokes the aforementioned extern function which creates a status_result +from a C enum. +
    + +The operations available to C++ are: + +
    +
    BOOST_OUTCOME_C_TO_RESULT_SYSTEM_CODE(ident, status_code<T>) +
    Returns a previously declared C Result from its matching C++ status_code. +NOTE that the destructor of the C++ status code is NOT called. If this is important +to your status code, it is 100% on you to ensure that your C Result reenters a C++ +Result at the end of its lifetime. + +
    to_result(any C Result) +
    This is an overloaded C++ free function which returns the C++ status_code<T> +matching its input C Result. +
    + +Using the above you can write C code using Outcome.Experimental's Result type +quite effectively. Let's look at an example of use next. diff --git a/doc/src/content/experimental/c-api/from-c/try.md b/doc/src/content/experimental/c-api/from-c/try.md new file mode 100644 index 000000000..653167526 --- /dev/null +++ b/doc/src/content/experimental/c-api/from-c/try.md @@ -0,0 +1,18 @@ ++++ +title = "TRY a C Result" +description = "Operation TRY on a C Result" +weight = 40 ++++ + +Thanks to much of the magic of {{< api "BOOST_OUTCOME_TRY(var, expr)" >}} being implemented +using C preprocessor metaprogramming, we can offer a very similar experience for the +C try operation and without needing anything compiled in C++ as support functions: + +{{% snippet "c_api2.cpp" "try" %}} + +The principle difference is that you can specify a cleanup routine to perform if +failure is encountered. This is especially useful in C, which has no stack unwinding. + +Also due to lack of type sugaring and user defined implicit conversions, if your +callers result type isn't your callee's, you may need to specify what your caller's +result type is so the error state can be correctly propagated. diff --git a/doc/src/content/experimental/c-api/from-c/use.md b/doc/src/content/experimental/c-api/from-c/use.md new file mode 100644 index 000000000..3d3b55d97 --- /dev/null +++ b/doc/src/content/experimental/c-api/from-c/use.md @@ -0,0 +1,14 @@ ++++ +title = "Using a Result" +description = "Using a C Result" +weight = 30 ++++ + +This models [the earlier C++ example of use]({{% relref "/experimental/worked-example/implicit-construction" %}}), +and its C equivalent isn't much more verbose thanks to our helper typedefs and macros: + +{{% snippet "c_api2.cpp" "using" %}} + +For this to link, the `BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM_FROM_ENUM` macro needs to be +compiled at least once within C++ within the final binary to emit the extern +functions needed by C. diff --git a/doc/src/content/experimental/c-api/limitations.md b/doc/src/content/experimental/c-api/from-cxx/_index.md similarity index 98% rename from doc/src/content/experimental/c-api/limitations.md rename to doc/src/content/experimental/c-api/from-cxx/_index.md index 5b71c426a..adecf4063 100644 --- a/doc/src/content/experimental/c-api/limitations.md +++ b/doc/src/content/experimental/c-api/from-cxx/_index.md @@ -1,7 +1,7 @@ +++ -title = "Limitations" +title = "Calling C++ from C" description = "" -weight = 10 +weight = 20 +++ C++ has excellent two-way compatibility with the C ABI, but there are some diff --git a/doc/src/content/experimental/c-api/example.md b/doc/src/content/experimental/c-api/from-cxx/example.md similarity index 100% rename from doc/src/content/experimental/c-api/example.md rename to doc/src/content/experimental/c-api/from-cxx/example.md diff --git a/doc/src/content/experimental/c-api/example2.md b/doc/src/content/experimental/c-api/from-cxx/example2.md similarity index 100% rename from doc/src/content/experimental/c-api/example2.md rename to doc/src/content/experimental/c-api/from-cxx/example2.md diff --git a/doc/src/content/experimental/c-api/reference.md b/doc/src/content/experimental/c-api/reference.md deleted file mode 100644 index 80bb3848e..000000000 --- a/doc/src/content/experimental/c-api/reference.md +++ /dev/null @@ -1,98 +0,0 @@ -+++ -title = "C Macro API Reference" -weight = 50 -+++ - -The C macro API header `` consists of these macros: - -
    -
    BOOST_OUTCOME_C_DECLARE_RESULT(ident, T, E) -
    Declares to C a basic_result type uniquely -identified by ident. T is available at the -member variable .value, and E is available -at the member variable .error. - -
    BOOST_OUTCOME_C_RESULT(ident) -
    A reference to a previously declared result type with -unique ident. - -
    BOOST_OUTCOME_C_RESULT_HAS_VALUE(r) -
    Evaluates to 1 (true) if the input result has a value. - -
    BOOST_OUTCOME_C_RESULT_HAS_ERROR(r) -
    Evaluates to 1 (true) if the input result has an error. - -
    BOOST_OUTCOME_C_RESULT_ERROR_IS_ERRNO(r) -
    Evaluates to 1 (true) if the input result's error value -is a code in the POSIX errno domain. -
    - -The above let you work, somewhat awkwardly, with any C-compatible -`basic_result`. `basic_result` is trivially copyable and -standard layout if its `T` and `E` are both so, and it has the C layout: - -```c++ -struct cxx_result_##ident -{ - union - { - T value; - E error; - }; - unsigned flags; -}; -``` - -### `` support - -Because erased status codes are not trivially copyable and -therefore do not have union based storage, we have separate C macros -for results whose `E` is an erased status code: - -
    -
    BOOST_OUTCOME_C_DECLARE_STATUS_CODE(ident, value_type) -
    Declares to C a status code type with domain value_type -available at the member variable .value. The ident -must be any identifier fragment unique in this translation unit. It is -used to uniquely identify this status code type in other macros. - -
    BOOST_OUTCOME_C_STATUS_CODE(ident) -
    A reference to a previously declared status code type with unique -ident. - -
    BOOST_OUTCOME_C_DECLARE_RESULT_STATUS_CODE(ident, T, E) -
    Declares to C a basic_result type uniquely -identified by ident. T is available at the -member variable .value, and E is available -at the member variable .error. - -
    BOOST_OUTCOME_C_RESULT_STATUS_CODE(ident) -
    A reference to a previously declared result type with -unique ident. -
    - -There is a high likelihood that C++ functions regularly called by C -code will return their failures either in erased `system_code` -or in `posix_code` (i.e. `errno` code domain). Via querying the -returned value using `BOOST_OUTCOME_C_RESULT_ERROR_IS_ERRNO(r)`, one can determine -if the returned code is in the `errno` code domain, and thus can be -fed to `strerror()` and so on. Therefore there are -convenience macro APIs for those particular use cases. - -
    -
    BOOST_OUTCOME_C_DECLARE_RESULT_ERRNO(ident, T) -
    Declares to C a basic_result<T, posix_code> -type uniquely identified by ident. - -
    BOOST_OUTCOME_C_RESULT_ERRNO(ident) -
    A reference to a previously declared basic_result<T, posix_code> -type with unique ident. - -
    BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(ident, T) -
    Declares to C a basic_result<T, system_code> -type uniquely identified by ident. - -
    BOOST_OUTCOME_C_RESULT_SYSTEM(ident) -
    A reference to a previously declared basic_result<T, system_code> -type with unique ident. -
    diff --git a/doc/src/content/experimental/outcome.md b/doc/src/content/experimental/outcome.md index e35a2277e..2000d51bd 100644 --- a/doc/src/content/experimental/outcome.md +++ b/doc/src/content/experimental/outcome.md @@ -1,6 +1,6 @@ +++ title = "Tying it all together" -weight = 80 +weight = 75 +++ Firstly let's alias a more convenient form of `status_result`: diff --git a/doc/src/content/experimental/worked-example-long/_index.md b/doc/src/content/experimental/worked-example-long/_index.md new file mode 100644 index 000000000..aa18f0d4d --- /dev/null +++ b/doc/src/content/experimental/worked-example-long/_index.md @@ -0,0 +1,36 @@ ++++ +title = "Worked example: Custom domain (the long way)" +weight = 85 ++++ + +Here follows a longer worked example of use of Experimental Outcome. It presents +the same sample program I sent to the San Diego 2018 WG21 standards meeting +after I was asked by the committee to demonstrate how P1095 implements P0709 +in a working code example they could study and discuss. + +We will walk through this worked example, step by step, explaining how each +part works in detail. This will help you implement your own code based on +Experimental Outcome. + +Most users will not need this level of customisation, and for them the preceding +[quick and easy approach]({{% relref "../worked-example" %}}) will be much easier. + +You may find it useful to open now in a separate browser tab the reference API +documentation for proposed `` at https://ned14.github.io/status-code/ +(scroll half way down). The references in the comments to P1028 are to +[P1028 *SG14 status_code and standard error object for P0709 Zero-overhead +deterministic exceptions*](http://wg21.link/P1028), which is the WG21 proposal +paper for potential ``. + +### Goal of this section + +We are going to define a simple custom code domain which defines that +the status code's payload will consist of a POSIX error code, and the +`__FILE__` and `__LINE__` where the failure occurred. This custom status +code will have an implicit conversion to type erased `error` defined, which dynamically +allocates memory for the original status code, and outputs an `error` +which manages that dynamic allocation, indirecting all queries etc +to the erased custom status code type such that the `error` instance +quacks as if just like the original. This demonstrates that `error` could +just as equally convey a `std::exception_ptr`, for example, or indeed +manage the lifetime of any pointer. diff --git a/doc/src/content/experimental/worked-example/constructor.md b/doc/src/content/experimental/worked-example-long/constructor.md similarity index 100% rename from doc/src/content/experimental/worked-example/constructor.md rename to doc/src/content/experimental/worked-example-long/constructor.md diff --git a/doc/src/content/experimental/worked-example/implicit_conversion.md b/doc/src/content/experimental/worked-example-long/implicit_conversion.md similarity index 86% rename from doc/src/content/experimental/worked-example/implicit_conversion.md rename to doc/src/content/experimental/worked-example-long/implicit_conversion.md index 5c65c8830..1a516d42c 100644 --- a/doc/src/content/experimental/worked-example/implicit_conversion.md +++ b/doc/src/content/experimental/worked-example-long/implicit_conversion.md @@ -3,7 +3,7 @@ title = "Implicit conversion" weight = 70 +++ -Back in [The payload]({{< relref "/experimental/worked-example/value_type" >}}), we +Back in [The payload]({{< relref "value_type" >}}), we mentioned that there was no default implicit conversion of `file_io_error` (`status_code<_file_io_error_domain>`) to `error`, as `error` is too small to hold `_file_io_error_domain::value_type`. diff --git a/doc/src/content/experimental/worked-example/message.md b/doc/src/content/experimental/worked-example-long/message.md similarity index 100% rename from doc/src/content/experimental/worked-example/message.md rename to doc/src/content/experimental/worked-example-long/message.md diff --git a/doc/src/content/experimental/worked-example/preamble.md b/doc/src/content/experimental/worked-example-long/preamble.md similarity index 100% rename from doc/src/content/experimental/worked-example/preamble.md rename to doc/src/content/experimental/worked-example-long/preamble.md diff --git a/doc/src/content/experimental/worked-example/source.md b/doc/src/content/experimental/worked-example-long/source.md similarity index 81% rename from doc/src/content/experimental/worked-example/source.md rename to doc/src/content/experimental/worked-example-long/source.md index 20a56f3f6..815e8f943 100644 --- a/doc/src/content/experimental/worked-example/source.md +++ b/doc/src/content/experimental/worked-example-long/source.md @@ -3,7 +3,7 @@ title = "Constexpr domain source" weight = 60 +++ -Back in [The constructor]({{< relref "/experimental/worked-example/constructor" >}}), we +Back in [The constructor]({{< relref "constructor" >}}), we declared but did not implement a `.get()` function which returns a constexpr static instance of the domain. We implement this now: diff --git a/doc/src/content/experimental/worked-example/string_ref.md b/doc/src/content/experimental/worked-example-long/string_ref.md similarity index 100% rename from doc/src/content/experimental/worked-example/string_ref.md rename to doc/src/content/experimental/worked-example-long/string_ref.md diff --git a/doc/src/content/experimental/worked-example/value_type.md b/doc/src/content/experimental/worked-example-long/value_type.md similarity index 100% rename from doc/src/content/experimental/worked-example/value_type.md rename to doc/src/content/experimental/worked-example-long/value_type.md diff --git a/doc/src/content/experimental/worked-example/_index.md b/doc/src/content/experimental/worked-example/_index.md index ac7dd5e0b..aebbe54ec 100644 --- a/doc/src/content/experimental/worked-example/_index.md +++ b/doc/src/content/experimental/worked-example/_index.md @@ -1,33 +1,30 @@ +++ -title = "Worked example: Custom domain" +title = "Worked example: Custom domain (the short way)" weight = 80 +++ -Here follows a worked example of use of Experimental Outcome. It presents -the same sample program I sent to the San Diego 2018 WG21 standards meeting -after I was asked by the committee to demonstrate how P1095 implements P0709 -in a working code example they could study and discuss. +The section after this one will take the long way through defining a custom domain +which sometimes is necessary if you have particularly bespoke requirements. +If however you just want to wrap a custom `enum` type of yours into its +own custom code domain, the boilerplate can be automated away +by filling in a few simple fields like this: -We will walk through this worked example, step by step, explaining how each -part works in detail. This will help you implement your own code based on -Experimental Outcome. +{{% snippet "quick_status_code_from_enum.cpp" "preamble" %}} -You may find it useful to open now in a separate browser tab the reference API -documentation for proposed `` at https://ned14.github.io/status-code/ -(scroll half way down). The references in the comments to P1028 are to -[P1028 *SG14 status_code and standard error object for P0709 Zero-overhead -deterministic exceptions*](http://wg21.link/P1028), which is the WG21 proposal -paper for potential ``. +Here we supply the bare minimum requirements for a status code domain: -### Goal of this section - -We are going to define a simple custom code domain which defines that -the status code's payload will consist of a POSIX error code, and the -`__FILE__` and `__LINE__` where the failure occurred. This custom status -code will have an implicit conversion to type erased `error` defined, which dynamically -allocates memory for the original status code, and outputs an `error` -which manages that dynamic allocation, indirecting all queries etc -to the erased custom status code type such that the `error` instance -quacks as if just like the original. This demonstrates that `error` could -just as equally convey a `std::exception_ptr`, for example, or indeed -manage the lifetime of any pointer. +1. The name in text, so it can be printed. +2. The unique UUID to identify when multiple copies of the domain are the same. +PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h to generate +this, do not twiddle a few bits. +3. For each enum value, its printable text and a sequence of `errc::` enumerations +which you think it is semantically equivalent to i.e. its mapping onto `generic_code` +which is how status code defines the common mapping between status codes. If you later compare the +status code to one of those values (or to another status code which also provides +a mapping), if the generic codes are equivalent then the comparison will return true. +This means code like `if(sc == errc::no_such_file_or_directory) ...` would match +all custom error codes which mean 'something was not found'. +4. OPTIONAL: if you would like the custom status code type generated by this +to have additional member functions or additional payload, you can define a mixin +here to inject either data or functions or both. If you omit this, nothing gets +injected. \ No newline at end of file diff --git a/doc/src/content/experimental/worked-example/implicit-construction.md b/doc/src/content/experimental/worked-example/implicit-construction.md new file mode 100644 index 000000000..ce0899563 --- /dev/null +++ b/doc/src/content/experimental/worked-example/implicit-construction.md @@ -0,0 +1,13 @@ ++++ +title = "Implicit construction" +weight = 10 ++++ + +The preceding code had the compiler stamp out a custom status code domain +for a user supplied `enum`. You now get the following types: + +{{% snippet "quick_status_code_from_enum.cpp" "implicit_construction" %}} + +As you can see, this is less work than [plugging your custom enum +into `std::error_code`]({{% relref "/motivation/plug_error_code" %}}). +It also has C compatibility, and generates better codegen. diff --git a/doc/src/content/recipes/rust.md b/doc/src/content/recipes/rust.md new file mode 100644 index 000000000..50c55a144 --- /dev/null +++ b/doc/src/content/recipes/rust.md @@ -0,0 +1,139 @@ ++++ +title = "Rust FFI" +description = "How to teach Rust about `result`." +tags = [ "Rust" ] ++++ + +A nice side effect of [Outcome.Experimental's excellent C support]({{% relref "/experimental/c-api" %}}) +is that teaching Rust about Outcome's `result` becomes trivially +easy. C and C++ results propagate _losslessly_ into Rust Results, and the +full power of the Outcome C API is available to Rust code for semantic +equivalence comparison et al. + +Here's a quick snippet to get you started. This assumes that you have declared +your C result using `BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(outcome, intptr_t)` in order +to produce a C result named "outcome" compatible with an erased system code C++ result: + +```rust +// Rust representation of an Outcome.Experimental Result +pub type OutcomeCResult = Result; + +unsafe impl Send for cxx_status_code_system {} +unsafe impl Sync for cxx_status_code_system {} + +impl std::fmt::Display for cxx_status_code_system { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + String::from_utf8_lossy(unsafe { + CStr::from_ptr(outcome_status_code_message( + self as *const _ as *const ::std::os::raw::c_void, + )) + .to_bytes() + }) + ) + } +} + +impl std::fmt::Debug for cxx_status_code_system { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + String::from_utf8_lossy(unsafe { + CStr::from_ptr(outcome_status_code_message( + self as *const _ as *const ::std::os::raw::c_void, + )) + .to_bytes() + }) + ) + } +} + +// Tell Rust that this meets the `Error` trait +impl std::error::Error for cxx_status_code_system { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +// Tell Rust how to make an `io::Result` from a `OutcomeCResult` +impl From for std::io::Error { + fn from(val: cxx_status_code_system) -> Self { + // This is nasty, and we ought to figure out a better way of doing this + for n in 1..200 { + if unsafe { + outcome_status_code_equal_generic( + &val as *const cxx_status_code_system as *const ::std::os::raw::c_void, + n, + ) != 0 + } { + return Self::new(Self::from_raw_os_error(n).kind(), val); + } + } + Self::new(std::io::ErrorKind::Other, val) + } +} + +impl std::fmt::Debug for cxx_result_status_code_system_outcome { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if (self.flags & 1) == 1 { + let v: OutcomeCResult = Ok(self.value); + return v.fmt(f); + } else { + let v: OutcomeCResult = Err(self.error); + return v.fmt(f); + } + } +} + +pub fn to_result(res: cxx_result_status_code_system_outcome) -> OutcomeCResult { + if (res.flags & 1) == 1 { + return Ok(res.value); + } + Err(res.error) +} + +pub fn success(val: isize) -> outcome_c_result { + // You will need to export BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_SUCCESS(outcome, v) as + // FFI outcome_c_make_success() + unsafe { outcome_c_make_success(val) } +} + +pub fn failure_from_errno(val: ::std::os::raw::c_int) -> outcome_c_result { + // You will need to export BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FAILURE_SYSTEM(outcome, v) as + // FFI outcome_c_make_failure() + unsafe { outcome_c_make_failure(val) } +} +``` + +Let's say there is an FFI function like this: + +```rust +unsafe extern "C" { + pub fn file_read( + arg1: *mut db, + buffer: *mut u8, + bytes: usize, + ) -> outcome_c_result; +} +``` + +You can now do: + +```rust +// Make a Rust Result equivalent to the Outcome Result +let res = to_result(unsafe { file_read(db, buffer, 256) }); +// Asks Outcome for the message automatically +println!("Message: {}", res.err().unwrap()); +``` + +You can use the standard Rust `?` to TRY Rust Results, same as anywhere else in Rust. + +As we taught Rust how to lossy map `OutcomeCResult` into an `io::Result`, you +can also `?` from an Outcome C Result function into an i/o Result function. Note that +this is **lossy**, and you may not wish to allow that by removing the `From` trait +definition above. + +Easy as pie! diff --git a/doc/src/content/reference/policies/base/ub.md b/doc/src/content/reference/policies/base/ub.md index f6f461212..8fda60ca4 100644 --- a/doc/src/content/reference/policies/base/ub.md +++ b/doc/src/content/reference/policies/base/ub.md @@ -9,7 +9,7 @@ This is a special function which does compiler-specific stuff to tell the compil This may seem highly undesirable. However, it also means that the optimiser can optimise more strongly, and so long as you never actually do execute this branch, you do get higher quality code generation. -If the `NDEBUG` macro is not defined, an `assert(false)` is present. This will cause attempts to execute this function to fail in a very obvious way, but it also generates runtime code to trigger the obvious failure. +If the `NDEBUG` macro is not defined, an `BOOST_OUTCOME_ASSERT(false)` is present. This will cause attempts to execute this function to fail in a very obvious way, but it also generates runtime code to trigger the obvious failure. If the `NDEBUG` macro is defined, and the program is compiled with the undefined behaviour sanitiser, attempts to execute this function will trigger an undefined behaviour sanitiser action. diff --git a/doc/src/content/requirements/_index.md b/doc/src/content/requirements/_index.md index 3c9dc90a2..2ab7b2344 100644 --- a/doc/src/content/requirements/_index.md +++ b/doc/src/content/requirements/_index.md @@ -20,18 +20,27 @@ performance improvements. Any Concepts TS or Coroutines TS implemented by your compiler is automatically detected and used. -Partially working compilers (this was last updated January 2019): +Known compiler issues (this was last updated April 2023): - clang 3.5 - 3.9 can compile varying degrees of the test suite, the problem is lack of complete and unbuggy C++ 14 language support. + - Older point releases of GCCs 7 and 8 have internal compiler error bugs in their constexpr implementation which tend to be triggered by using Outcome in constexpr. If you don't use Outcome in constexpr, you won't see these problems. If you need your GCC to not ICE, upgrade to the very latest point release, the constexpr ICE has been since fixed. + - Early editions of Visual Studio 2017 have many corner case problems. -The latest point release, VS2017.9, only has a few known problems, -and should be relatively unsurprising for most use cases. +From VS2017.9 onwards there remain a number of usually untroublesome corner +case issues, but use should be relatively unsurprising for most use cases. +Be aware that only from Visual Studio 2022 onwards are almost all corner +case problems fixed. + +- Some point releases of GCC 10 with libstdc++ 10 can induce an infinite +template instantiation, which fails the build for some rare use cases. Earlier +or later GCCs or different point releases of the 10 series do not have +this issue. --- diff --git a/doc/src/snippets/boost-only/asio_integration.cpp b/doc/src/snippets/boost-only/asio_integration.cpp index 409d2095e..ffccf407e 100644 --- a/doc/src/snippets/boost-only/asio_integration.cpp +++ b/doc/src/snippets/boost-only/asio_integration.cpp @@ -1,5 +1,5 @@ /* Example of Outcome -(C) 2017-2023 Niall Douglas (3 commits) +(C) 2017-2025 Niall Douglas (3 commits) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/doc/src/snippets/boost-only/asio_integration_1_70.cpp b/doc/src/snippets/boost-only/asio_integration_1_70.cpp index 4d3e4c78d..ff51c8b33 100644 --- a/doc/src/snippets/boost-only/asio_integration_1_70.cpp +++ b/doc/src/snippets/boost-only/asio_integration_1_70.cpp @@ -1,5 +1,5 @@ /* Example of Outcome -(C) 2017-2023 Niall Douglas (3 commits) +(C) 2017-2025 Niall Douglas (3 commits) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/doc/src/snippets/boost-only/error_code_registration.cpp b/doc/src/snippets/boost-only/error_code_registration.cpp index 324ff99bd..6a512b831 100644 --- a/doc/src/snippets/boost-only/error_code_registration.cpp +++ b/doc/src/snippets/boost-only/error_code_registration.cpp @@ -1,5 +1,5 @@ /* Documentation snippet -(C) 2017-2023 Niall Douglas (3 commits), Luke Peterson (2 commits), Andrzej Krzemienski (2 commits) and Andrzej Krzemieński (1 commit) +(C) 2017-2025 Niall Douglas (3 commits), Luke Peterson (2 commits), Andrzej Krzemienski (2 commits) and Andrzej Krzemieński (1 commit) File Created: Mar 2017 diff --git a/doc/src/snippets/c_api.c b/doc/src/snippets/c_api.c index ba1f3c461..989b9b601 100644 --- a/doc/src/snippets/c_api.c +++ b/doc/src/snippets/c_api.c @@ -1,5 +1,5 @@ /* Example of Outcome used with C APIs -(C) 2017-2023 Niall Douglas (4 commits) +(C) 2017-2025 Niall Douglas (4 commits) Boost Software License - Version 1.0 - August 17th, 2003 @@ -43,7 +43,7 @@ DEALINGS IN THE SOFTWARE. // // The first parameter is some unique identifier for this type which will be used // whenever we reference this type in the future. -CXX_DECLARE_RESULT_SYSTEM(to_string_rettype, size_t); +CXX_DECLARE_RESULT_SYSTEM(to_string_rettype, size_t) // Tell C about our extern C++ function `to_string()` extern CXX_RESULT_SYSTEM(to_string_rettype) _Z9to_stringPcmi(char *buffer, size_t bufferlen, int v); @@ -98,4 +98,4 @@ extern CXX_RESULT_SYSTEM(to_string_rettype) _Z9to_stringPcmi(char *buffer, size_ ret.flags |= 1U; ret.value = len; return ret; -} \ No newline at end of file +} diff --git a/doc/src/snippets/c_api2.cpp b/doc/src/snippets/c_api2.cpp new file mode 100644 index 000000000..2ff8da3ab --- /dev/null +++ b/doc/src/snippets/c_api2.cpp @@ -0,0 +1,107 @@ +/* Example of Outcome used with C APIs +(C) 2024-2025 Niall Douglas (4 commits) + + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +// This header in Experimental Outcome is pure C, it provides a suite of C helper macros +#include "../../../include/boost/outcome/experimental/result.h" + +#include +#include +#include +#include + +//! [preamble] +// Declare to C a Result with a happy value of intptr_t +BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(result_int, intptr_t) + +// Save oneself typing out BOOST_OUTCOME_C_RESULT_SYSTEM(result_int) all the time +typedef BOOST_OUTCOME_C_RESULT_SYSTEM(result_int) result; + +// Our custom C enum +enum c_enum +{ + c_enum_not_found, + c_enum_bad_argument +}; + +// Make a custom status code domain for this C enum +BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM_FROM_ENUM(result_int, // The C Result type declared above + c_enum, // The C enum we wish to wrap + "{74ceb994-7622-3a21-07f0-b016aa705585}", // Unique UUID for this domain + // Mappings of C enum values to textual description and semantic equivalances to generic codes + {c_enum::c_enum_not_found, "item not found", {errc::no_such_file_or_directory}}, + {c_enum::c_enum_bad_argument, "invoked wrong", {errc::invalid_argument}}) + +// Make helper macros +#define SUCCESS(v) BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_SUCCESS(result_int, (v)) +#define FAILURE(v) BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FROM_ENUM(result_int, c_enum, (v)) +//! [preamble] + +//! [using] +result positive_only(int x) +{ + if(x < 0) + { + return FAILURE(c_enum_bad_argument); + } + return SUCCESS(x); +} + +bool test(int x) +{ + result r = positive_only(x); + if(BOOST_OUTCOME_C_RESULT_HAS_ERROR(r)) + { + if(outcome_status_code_equal_generic(&r.error, EINVAL)) + { + fprintf(stderr, "Positive numbers only!\n"); + return false; + } + } + return true; +} +//! [using] + +//! [try] +result test2(int x) +{ + BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(int v, // what to set to value if successful + fprintf(stderr, "Positive numbers only!\n"), // what cleanup to run if unsuccessful + positive_only(x)); + return SUCCESS(v + 1); +} +//! [try] + +int main(void) +{ + if(!test(0)) + abort(); + if(test(-1)) + abort(); + return 0; +} diff --git a/doc/src/snippets/constructors.cpp b/doc/src/snippets/constructors.cpp index 814ff39c3..272259582 100644 --- a/doc/src/snippets/constructors.cpp +++ b/doc/src/snippets/constructors.cpp @@ -1,5 +1,5 @@ /* Example of Outcome used with constructors -(C) 2017-2023 Niall Douglas (5 commits) +(C) 2017-2025 Niall Douglas (5 commits) Boost Software License - Version 1.0 - August 17th, 2003 @@ -83,7 +83,11 @@ class file_handle // Moves but not copies permitted file_handle(const file_handle &) = delete; - file_handle(file_handle &&o) noexcept : _fd(o._fd) { o._fd = -1; } + file_handle(file_handle &&o) noexcept + : _fd(o._fd) + { + o._fd = -1; + } file_handle &operator=(const file_handle &) = delete; file_handle &operator=(file_handle &&o) noexcept { @@ -136,7 +140,7 @@ inline outcome::result file_handle::file(file_handle::path_type pat default: return std::errc::invalid_argument; } - ret._fd = ::open(path.u8string().c_str(), flags); + ret._fd = ::open((const char *) path.u8string().c_str(), flags); if(-1 == ret._fd) { // Note that if we bail out here, ~file_handle() will correctly not call ::close() diff --git a/doc/src/snippets/cpp_api.cpp b/doc/src/snippets/cpp_api.cpp index 8b630f1b8..0cbb11b5d 100644 --- a/doc/src/snippets/cpp_api.cpp +++ b/doc/src/snippets/cpp_api.cpp @@ -1,5 +1,5 @@ /* Example of Outcome used with C APIs -(C) 2017-2023 Niall Douglas (2 commits) +(C) 2017-2025 Niall Douglas (2 commits) Boost Software License - Version 1.0 - August 17th, 2003 @@ -30,7 +30,7 @@ DEALINGS IN THE SOFTWARE. #include // for memcpy #include -#include "../../../include/boost/outcome/experimental/status-code/status-code/system_code_from_exception.hpp" +#include "../../../include/boost/outcome/experimental/status-code/system_code_from_exception.hpp" #include "../../../include/boost/outcome/experimental/status_result.hpp" //! [function] @@ -63,7 +63,7 @@ outcome_e::status_result to_string(char *buffer, size_t bufferlen, int v // win32_code). // // Note that using this function requires including - // + // // It is NOT included by Experimental Outcome by default. return outcome_e::system_code_from_exception(); } diff --git a/doc/src/snippets/error_code_enums1.cpp b/doc/src/snippets/error_code_enums1.cpp index 023b69fe2..8aa5ce1c0 100644 --- a/doc/src/snippets/error_code_enums1.cpp +++ b/doc/src/snippets/error_code_enums1.cpp @@ -1,5 +1,5 @@ /* Example of Outcome used with error code enums -(C) 2017-2023 Niall Douglas (3 commits) and Andrzej Krzemienski (1 commit) +(C) 2017-2025 Niall Douglas (3 commits) and Andrzej Krzemienski (1 commit) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/doc/src/snippets/error_code_enums2.cpp b/doc/src/snippets/error_code_enums2.cpp index 5ed46302e..463e1ccba 100644 --- a/doc/src/snippets/error_code_enums2.cpp +++ b/doc/src/snippets/error_code_enums2.cpp @@ -1,5 +1,5 @@ /* Example of Outcome used with error code enums -(C) 2017-2023 Niall Douglas (3 commits) +(C) 2017-2025 Niall Douglas (3 commits) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/doc/src/snippets/error_code_extended.cpp b/doc/src/snippets/error_code_extended.cpp index b1e88b184..710b5f7f1 100644 --- a/doc/src/snippets/error_code_extended.cpp +++ b/doc/src/snippets/error_code_extended.cpp @@ -1,5 +1,5 @@ /* Example of how to extend result's error code with extra information -(C) 2017-2023 Niall Douglas (7 commits) and Andrzej Krzemieński (1 commit) +(C) 2017-2025 Niall Douglas (7 commits) and Andrzej Krzemieński (1 commit) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/doc/src/snippets/error_code_registration.cpp b/doc/src/snippets/error_code_registration.cpp index 22ee49058..0a83eb511 100644 --- a/doc/src/snippets/error_code_registration.cpp +++ b/doc/src/snippets/error_code_registration.cpp @@ -1,5 +1,5 @@ /* Documentation snippet -(C) 2017-2023 Niall Douglas (3 commits), Luke Peterson (2 commits), Andrzej Krzemienski (2 commits) and Andrzej Krzemieński (1 commit) +(C) 2017-2025 Niall Douglas (3 commits), Luke Peterson (2 commits), Andrzej Krzemienski (2 commits) and Andrzej Krzemieński (1 commit) File Created: Mar 2017 diff --git a/doc/src/snippets/exception_ptr.cpp b/doc/src/snippets/exception_ptr.cpp index 48aed3bda..734c3c917 100644 --- a/doc/src/snippets/exception_ptr.cpp +++ b/doc/src/snippets/exception_ptr.cpp @@ -1,5 +1,5 @@ /* Example of Outcome used with exception ptrs -(C) 2017-2023 Niall Douglas (1 commit) +(C) 2017-2025 Niall Douglas (1 commit) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/doc/src/snippets/expected_implementation.cpp b/doc/src/snippets/expected_implementation.cpp index 683d71c3f..458ab77cc 100644 --- a/doc/src/snippets/expected_implementation.cpp +++ b/doc/src/snippets/expected_implementation.cpp @@ -1,5 +1,5 @@ /* Documentation snippet -(C) 2017-2023 Niall Douglas (7 commits) +(C) 2017-2025 Niall Douglas (7 commits) File Created: Mar 2017 diff --git a/doc/src/snippets/experimental_status_code.cpp b/doc/src/snippets/experimental_status_code.cpp index 105860f03..516bbb29c 100644 --- a/doc/src/snippets/experimental_status_code.cpp +++ b/doc/src/snippets/experimental_status_code.cpp @@ -1,5 +1,5 @@ /* Example use of std::error implicit conversion -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: Sept 2018 @@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE. #if !defined(__GNUC__) || __GNUC__ > 6 // GCC 6 chokes on this #include "../../../include/boost/outcome/experimental/status_result.hpp" +#include "../../../include/boost/outcome/experimental/status-code/nested_status_code.hpp" /* Original note to WG21: @@ -161,7 +162,7 @@ inline constexpr const _file_io_error_domain &_file_io_error_domain::get() // is guaranteed to do nothing. inline outcome_e::system_code make_status_code(file_io_error v) { - // `make_status_code_ptr()` dynamically allocates memory to store an + // `make_nested_status_code()` dynamically allocates memory to store an // instance of `file_io_error`, then returns a status code whose domain // specifies that its value type is a pointer to `file_io_error`. The // domain is a templated instance which indirects all observers of the @@ -171,8 +172,8 @@ inline outcome_e::system_code make_status_code(file_io_error v) // by definition fits into `intptr_t` and is trivially copyable. // Therefore `system_code` (which is also a type alias to // `status_code>`) is happy to implicitly construct - // from the status code returned by `make_status_code_ptr()`. - return make_status_code_ptr(std::move(v)); + // from the status code returned by `make_nested_status_code()`. + return make_nested_status_code(std::move(v)); } //! [implicit_conversion] diff --git a/doc/src/snippets/finale.cpp b/doc/src/snippets/finale.cpp index ab11bc249..92c729f07 100644 --- a/doc/src/snippets/finale.cpp +++ b/doc/src/snippets/finale.cpp @@ -1,5 +1,5 @@ /* Example of how to marshall Outcomes at namespace boundaries -(C) 2017-2023 Niall Douglas (11 commits) +(C) 2017-2025 Niall Douglas (11 commits) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/doc/src/snippets/foreign_try.cpp b/doc/src/snippets/foreign_try.cpp index e5cbef0ad..771807dda 100644 --- a/doc/src/snippets/foreign_try.cpp +++ b/doc/src/snippets/foreign_try.cpp @@ -1,5 +1,5 @@ /* Example of Outcome try used with foreign object -(C) 2019-2023 Niall Douglas (1 commit) +(C) 2019-2025 Niall Douglas (1 commit) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/doc/src/snippets/intro_c_example.cpp b/doc/src/snippets/intro_c_example.cpp new file mode 100644 index 000000000..60c858eb9 --- /dev/null +++ b/doc/src/snippets/intro_c_example.cpp @@ -0,0 +1,79 @@ +/* Example of Outcome +(C) 2024-2025 Niall Douglas + + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#include "../../../include/boost/outcome/experimental/result.h" + +#include + +void use_string(const char *foo) +{ + (void) foo; +} + +//! [signature] +BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(result_string, const char *) + +BOOST_OUTCOME_C_RESULT_SYSTEM(result_string) data_from_file(const char *path); +//! [signature] + +int main() +{ + //! [inspect] + BOOST_OUTCOME_C_RESULT_SYSTEM(result_string) rslt = data_from_file("config.cfg"); + if(BOOST_OUTCOME_C_RESULT_HAS_VALUE(rslt)) + use_string(rslt.value); // returns string + else + fprintf(stderr, "%s\n", outcome_status_code_message(&rslt.error)); + //! [inspect] +} + +//! [implementation] +BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(result_int, int) + +BOOST_OUTCOME_C_RESULT_SYSTEM(result_int) process(const char *content); + +BOOST_OUTCOME_C_RESULT_SYSTEM(result_int) int_from_file(const char *path) +{ + BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(const char *str, result_int, /* cleanup on fail */, data_from_file(path)); + // if control gets here data_from_file() has succeeded + return process(str); // decltype(str) == string +} +//! [implementation] + +BOOST_OUTCOME_C_RESULT_SYSTEM(result_int) process(const char *foo) +{ + (void) foo; + return outcome_make_result_system_result_int_success(1); +} + +BOOST_OUTCOME_C_RESULT_SYSTEM(result_string) data_from_file(const char *path) +{ + (void) path; + return outcome_make_result_system_result_string_success(""); +} diff --git a/doc/src/snippets/intro_example.cpp b/doc/src/snippets/intro_example.cpp index a69a4b179..9e3b23ec1 100644 --- a/doc/src/snippets/intro_example.cpp +++ b/doc/src/snippets/intro_example.cpp @@ -1,5 +1,5 @@ /* Example of Outcome -(C) 2017-2023 Niall Douglas (4 commits), Andrzej Krzemienski (3 commits), akrzemi1 (1 commit) and Andrzej Krzemieński (1 commit) +(C) 2017-2025 Niall Douglas (4 commits), Andrzej Krzemienski (3 commits), akrzemi1 (1 commit) and Andrzej Krzemieński (1 commit) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/doc/src/snippets/outcome_payload.cpp b/doc/src/snippets/outcome_payload.cpp index ed9faaf68..01b13a6c2 100644 --- a/doc/src/snippets/outcome_payload.cpp +++ b/doc/src/snippets/outcome_payload.cpp @@ -1,5 +1,5 @@ /* Documentation snippet -(C) 2017-2023 Niall Douglas (8 commits) +(C) 2017-2025 Niall Douglas (8 commits) File Created: Mar 2017 diff --git a/doc/src/snippets/policies.cpp b/doc/src/snippets/policies.cpp index 5b944a2f7..473351d6b 100644 --- a/doc/src/snippets/policies.cpp +++ b/doc/src/snippets/policies.cpp @@ -1,5 +1,5 @@ /* Documentation snippet -(C) 2017-2023 Niall Douglas (5 commits) and Andrzej Krzemienski (1 commit) +(C) 2017-2025 Niall Douglas (5 commits) and Andrzej Krzemienski (1 commit) File Created: Mar 2017 diff --git a/doc/src/snippets/quick_status_code_from_enum.cpp b/doc/src/snippets/quick_status_code_from_enum.cpp new file mode 100644 index 000000000..8adb0abe7 --- /dev/null +++ b/doc/src/snippets/quick_status_code_from_enum.cpp @@ -0,0 +1,113 @@ +/* Example use of std::error implicit conversion +(C) 2024-2025 Niall Douglas (5 commits) +File Created: Sept 2018 + + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#include "../../../include/boost/outcome/experimental/status-code/quick_status_code_from_enum.hpp" +#include "../../../include/boost/outcome/experimental/status_result.hpp" +#include "outcome/experimental/status-code/config.hpp" + + +//! [preamble] +// My custom enum type +enum class custom_failure +{ + not_found, + bad_argument +}; + +// Tell `status_code` to stamp out a custom code domain for this enum type +BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN +template <> struct quick_status_code_from_enum : quick_status_code_from_enum_defaults +{ + // Text name of the enum + static constexpr const auto domain_name = "My custom failure"; + // Unique UUID for the enum. PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h + static constexpr const auto domain_uuid = "{be201f65-3962-dd0e-1266-a72e63776a42}"; + // Map of each enum value to its text string, and list of semantically equivalent errc's + static const std::initializer_list &value_mappings() + { + static const std::initializer_list v = { + // Format is: { enum value, "string representation", { list of errc mappings ... } } + {custom_failure::not_found, "item not found", {errc::no_such_file_or_directory}}, // + {custom_failure::bad_argument, "invoked wrong", {errc::invalid_argument}}, // + }; + return v; + } + // Completely optional definition of mixin for the status code synthesised from `Enum`. It can be omitted. + template struct mixin : Base + { + using Base::Base; + constexpr int custom_method() const { return 42; } + }; +}; +BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END +//! [preamble] + +//! [implicit_construction] +// This is the status code generated for your custom enum type. It will implicitly construct from +// values of enum custom_failure. +using custom_failure_code = BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::quick_status_code_from_enum_code; + +namespace outcome_e = BOOST_OUTCOME_V2_NAMESPACE::experimental; + +// You don't usually need to use the status code type explicitly, because this "just works": +outcome_e::status_result positive_only(int x) +{ + if(x < 0) + { + // Outcome's result sees that status_code will implicitly construct from this enum, + // and it returns an errored result + return custom_failure::bad_argument; + } + return x; +} + +// Semantic comparisons work +bool test(int x) +{ + if(auto r = positive_only(x); !r) + { + if(r.error() == outcome_e::errc::invalid_argument) + { + std::cerr << "Positive numbers only!" << std::endl; + return false; + } + } + return true; +} +//! [implicit_construction] + +int main(void) +{ + if(!test(0)) + abort(); + if(test(-1)) + abort(); + return 0; +} diff --git a/doc/src/snippets/udts.cpp b/doc/src/snippets/udts.cpp index d7587f957..a4958ff49 100644 --- a/doc/src/snippets/udts.cpp +++ b/doc/src/snippets/udts.cpp @@ -1,5 +1,5 @@ /* Example of Outcome used with UDTs -(C) 2017-2023 Niall Douglas (3 commits) +(C) 2017-2025 Niall Douglas (3 commits) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/doc/src/snippets/using_outcome.cpp b/doc/src/snippets/using_outcome.cpp index d776cd3d0..962586bd9 100644 --- a/doc/src/snippets/using_outcome.cpp +++ b/doc/src/snippets/using_outcome.cpp @@ -1,5 +1,5 @@ /* Example of Outcome -(C) 2017-2023 Niall Douglas (3 commits), Krzemienski (3 commits) and Andrzej Krzemienski (3 commits) +(C) 2017-2025 Niall Douglas (3 commits), Krzemienski (3 commits) and Andrzej Krzemienski (3 commits) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/doc/src/snippets/using_result.cpp b/doc/src/snippets/using_result.cpp index f4a01aad1..5dcc98976 100644 --- a/doc/src/snippets/using_result.cpp +++ b/doc/src/snippets/using_result.cpp @@ -1,5 +1,5 @@ /* Documentation snippet -(C) 2017-2023 Niall Douglas (7 commits), Andrzej Krzemienski (4 commits) and Krzemienski (2 commits) +(C) 2017-2025 Niall Douglas (7 commits), Andrzej Krzemienski (4 commits) and Krzemienski (2 commits) File Created: Mar 2017 diff --git a/doc/src/snippets/void_terminate.cpp b/doc/src/snippets/void_terminate.cpp index 7104aa1d8..310192d85 100644 --- a/doc/src/snippets/void_terminate.cpp +++ b/doc/src/snippets/void_terminate.cpp @@ -1,5 +1,5 @@ /* Example of Outcome used with void -(C) 2017-2023 Niall Douglas (1 commit) +(C) 2017-2025 Niall Douglas (1 commit) Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/boost/outcome.hpp b/include/boost/outcome.hpp index 088bcc751..480723f47 100644 --- a/include/boost/outcome.hpp +++ b/include/boost/outcome.hpp @@ -1,5 +1,5 @@ /* Include the default amount of outcome -(C) 2018-2023 Niall Douglas (4 commits) +(C) 2018-2025 Niall Douglas (4 commits) File Created: Mar 2018 diff --git a/include/boost/outcome/bad_access.hpp b/include/boost/outcome/bad_access.hpp index a348f0449..af03a7ed2 100644 --- a/include/boost/outcome/bad_access.hpp +++ b/include/boost/outcome/bad_access.hpp @@ -1,5 +1,5 @@ /* Exception types throwable -(C) 2017-2023 Niall Douglas (9 commits) +(C) 2017-2025 Niall Douglas (9 commits) File Created: Oct 2017 diff --git a/include/boost/outcome/basic_outcome.hpp b/include/boost/outcome/basic_outcome.hpp index 12ba7dc88..0a2fe1029 100644 --- a/include/boost/outcome/basic_outcome.hpp +++ b/include/boost/outcome/basic_outcome.hpp @@ -1,5 +1,5 @@ /* A less simple result type -(C) 2017-2023 Niall Douglas (20 commits) +(C) 2017-2025 Niall Douglas (20 commits) File Created: June 2017 @@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE. #include "config.hpp" #include "basic_result.hpp" + #include "detail/basic_outcome_exception_observers.hpp" #include "detail/basic_outcome_failure_observers.hpp" @@ -42,6 +43,11 @@ DEALINGS IN THE SOFTWARE. #pragma clang diagnostic ignored "-Wdocumentation" // Standardese markup confuses clang #endif +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 6287) // redundant code +#endif + BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN template // @@ -66,14 +72,14 @@ namespace detail template static constexpr bool enable_value_converting_constructor = // implicit_constructors_enabled // - &&result::template enable_value_converting_constructor // + && result::template enable_value_converting_constructor // && !detail::is_implicitly_constructible; // deliberately less tolerant of ambiguity than result's edition // Predicate for the error converting constructor to be available. template static constexpr bool enable_error_converting_constructor = // implicit_constructors_enabled // - &&result::template enable_error_converting_constructor // + && result::template enable_error_converting_constructor // && !detail::is_implicitly_constructible; // deliberately less tolerant of ambiguity than result's edition // Predicate for the error condition converting constructor to be available. @@ -149,12 +155,18 @@ namespace detail std::conditional_t::value && trait::is_exception_ptr_available

    ::value, basic_outcome_failure_observers, Base>; - template constexpr inline const V &extract_exception_from_failure(const failure_type &v) { return v.exception(); } + template constexpr inline const V &extract_exception_from_failure(const failure_type &v) + { + return v.exception(); + } template constexpr inline V &&extract_exception_from_failure(failure_type &&v) { return static_cast &&>(v).exception(); } - template constexpr inline const U &extract_exception_from_failure(const failure_type &v) { return v.error(); } + template constexpr inline const U &extract_exception_from_failure(const failure_type &v) + { + return v.error(); + } template constexpr inline U &&extract_exception_from_failure(failure_type &&v) { return static_cast &&>(v).error(); @@ -405,7 +417,7 @@ class BOOST_OUTCOME_NODISCARD basic_outcome template static constexpr bool enable_inplace_value_error_exception_constructor = // constructors_enabled // - &&base::template enable_inplace_value_error_exception_constructor; + && base::template enable_inplace_value_error_exception_constructor; template using choose_inplace_value_error_exception_constructor = typename base::template choose_inplace_value_error_exception_constructor; }; @@ -427,7 +439,7 @@ SIGNATURE NOT RECOGNISED */ BOOST_OUTCOME_TEMPLATE(class Arg, class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED((!predicate::constructors_enabled && sizeof...(Args) >= 0))) - basic_outcome(Arg && /*unused*/, Args &&... /*unused*/) = delete; // NOLINT basic_outcome<> with any of the same type is NOT SUPPORTED, see docs! + basic_outcome(Arg && /*unused*/, Args &&.../*unused*/) = delete; // NOLINT basic_outcome<> with any of the same type is NOT SUPPORTED, see docs! /*! AWAITING HUGO JSON CONVERSION TOOL SIGNATURE NOT RECOGNISED @@ -494,7 +506,7 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TEMPLATE(class T, class U) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_error_exception_converting_constructor)) constexpr basic_outcome(T &&a, U &&b, error_exception_converting_constructor_tag /*unused*/ = error_exception_converting_constructor_tag()) noexcept( - detail::is_nothrow_constructible &&detail::is_nothrow_constructible) // NOLINT + detail::is_nothrow_constructible && detail::is_nothrow_constructible) // NOLINT : base{in_place_type, static_cast(a)} , _ptr(static_cast(b)) { @@ -521,9 +533,9 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_compatible_conversion)) constexpr explicit basic_outcome( const basic_outcome &o, - explicit_compatible_copy_conversion_tag /*unused*/ = - explicit_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible &&detail::is_nothrow_constructible - &&detail::is_nothrow_constructible) + explicit_compatible_copy_conversion_tag /*unused*/ = explicit_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible && + detail::is_nothrow_constructible && + detail::is_nothrow_constructible) : base{typename base::compatible_conversion_tag(), o} , _ptr(o._ptr) { @@ -536,9 +548,9 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_compatible_conversion)) constexpr explicit basic_outcome( basic_outcome &&o, - explicit_compatible_move_conversion_tag /*unused*/ = - explicit_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible &&detail::is_nothrow_constructible - &&detail::is_nothrow_constructible) + explicit_compatible_move_conversion_tag /*unused*/ = explicit_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible && + detail::is_nothrow_constructible && + detail::is_nothrow_constructible) : base{typename base::compatible_conversion_tag(), static_cast &&>(o)} , _ptr(static_cast::exception_type &&>(o._ptr)) { @@ -551,9 +563,9 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(detail::result_predicates::template enable_compatible_conversion)) constexpr explicit basic_outcome( const basic_result &o, - explicit_compatible_copy_conversion_tag /*unused*/ = - explicit_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible &&detail::is_nothrow_constructible - &&detail::is_nothrow_constructible) + explicit_compatible_copy_conversion_tag /*unused*/ = explicit_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible && + detail::is_nothrow_constructible && + detail::is_nothrow_constructible) : base{typename base::compatible_conversion_tag(), o} , _ptr() { @@ -566,9 +578,9 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(detail::result_predicates::template enable_compatible_conversion)) constexpr explicit basic_outcome( basic_result &&o, - explicit_compatible_move_conversion_tag /*unused*/ = - explicit_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible &&detail::is_nothrow_constructible - &&detail::is_nothrow_constructible) + explicit_compatible_move_conversion_tag /*unused*/ = explicit_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible && + detail::is_nothrow_constructible && + detail::is_nothrow_constructible) : base{typename base::compatible_conversion_tag(), static_cast &&>(o)} , _ptr() { @@ -581,8 +593,8 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(detail::result_predicates::template enable_make_error_code_compatible_conversion)) constexpr explicit basic_outcome(const basic_result &o, explicit_make_error_code_compatible_copy_conversion_tag /*unused*/ = - explicit_make_error_code_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible - &&noexcept(make_error_code(std::declval())) && + explicit_make_error_code_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible && + noexcept(make_error_code(std::declval())) && detail::is_nothrow_constructible) : base{typename base::make_error_code_compatible_conversion_tag(), o} , _ptr() @@ -596,8 +608,8 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(detail::result_predicates::template enable_make_error_code_compatible_conversion)) constexpr explicit basic_outcome(basic_result &&o, explicit_make_error_code_compatible_move_conversion_tag /*unused*/ = - explicit_make_error_code_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible - &&noexcept(make_error_code(std::declval())) && + explicit_make_error_code_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible && + noexcept(make_error_code(std::declval())) && detail::is_nothrow_constructible) : base{typename base::make_error_code_compatible_conversion_tag(), static_cast &&>(o)} , _ptr() @@ -611,7 +623,7 @@ SIGNATURE NOT RECOGNISED */ BOOST_OUTCOME_TEMPLATE(class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_value_constructor)) - constexpr explicit basic_outcome(in_place_type_t _, Args &&... args) noexcept(detail::is_nothrow_constructible) + constexpr explicit basic_outcome(in_place_type_t _, Args &&...args) noexcept(detail::is_nothrow_constructible) : base{_, static_cast(args)...} , _ptr() { @@ -623,7 +635,7 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TEMPLATE(class U, class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_value_constructor, Args...>)) constexpr explicit basic_outcome(in_place_type_t _, std::initializer_list il, - Args &&... args) noexcept(detail::is_nothrow_constructible, Args...>) + Args &&...args) noexcept(detail::is_nothrow_constructible, Args...>) : base{_, il, static_cast(args)...} , _ptr() { @@ -634,7 +646,7 @@ SIGNATURE NOT RECOGNISED */ BOOST_OUTCOME_TEMPLATE(class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_error_constructor)) - constexpr explicit basic_outcome(in_place_type_t _, Args &&... args) noexcept(detail::is_nothrow_constructible) + constexpr explicit basic_outcome(in_place_type_t _, Args &&...args) noexcept(detail::is_nothrow_constructible) : base{_, static_cast(args)...} , _ptr() { @@ -646,7 +658,7 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TEMPLATE(class U, class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_error_constructor, Args...>)) constexpr explicit basic_outcome(in_place_type_t _, std::initializer_list il, - Args &&... args) noexcept(detail::is_nothrow_constructible, Args...>) + Args &&...args) noexcept(detail::is_nothrow_constructible, Args...>) : base{_, il, static_cast(args)...} , _ptr() { @@ -658,7 +670,7 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TEMPLATE(class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_exception_constructor)) constexpr explicit basic_outcome(in_place_type_t /*unused*/, - Args &&... args) noexcept(detail::is_nothrow_constructible) + Args &&...args) noexcept(detail::is_nothrow_constructible) : base() , _ptr(static_cast(args)...) { @@ -671,7 +683,7 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TEMPLATE(class U, class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_exception_constructor, Args...>)) constexpr explicit basic_outcome(in_place_type_t /*unused*/, std::initializer_list il, - Args &&... args) noexcept(detail::is_nothrow_constructible, Args...>) + Args &&...args) noexcept(detail::is_nothrow_constructible, Args...>) : base() , _ptr(il, static_cast(args)...) { @@ -683,7 +695,7 @@ SIGNATURE NOT RECOGNISED */ BOOST_OUTCOME_TEMPLATE(class A1, class A2, class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_value_error_exception_constructor)) - constexpr basic_outcome(A1 &&a1, A2 &&a2, Args &&... args) noexcept( + constexpr basic_outcome(A1 &&a1, A2 &&a2, Args &&...args) noexcept( noexcept(typename predicate::template choose_inplace_value_error_exception_constructor(std::declval(), std::declval(), std::declval()...))) : basic_outcome(in_place_type>, static_cast(a1), @@ -770,7 +782,7 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TEMPLATE(class T, class U) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(!std::is_void::value && predicate::template enable_compatible_conversion)) constexpr basic_outcome(const failure_type &o, explicit_compatible_copy_conversion_tag /*unused*/ = explicit_compatible_copy_conversion_tag()) noexcept( - detail::is_nothrow_constructible &&detail::is_nothrow_constructible) // NOLINT + detail::is_nothrow_constructible && detail::is_nothrow_constructible) // NOLINT : base{in_place_type, detail::extract_error_from_failure(o)} , _ptr(detail::extract_exception_from_failure(o)) { @@ -833,7 +845,7 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TEMPLATE(class T, class U) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(!std::is_void::value && predicate::template enable_compatible_conversion)) constexpr basic_outcome(failure_type &&o, explicit_compatible_move_conversion_tag /*unused*/ = explicit_compatible_move_conversion_tag()) noexcept( - detail::is_nothrow_constructible &&detail::is_nothrow_constructible) // NOLINT + detail::is_nothrow_constructible && detail::is_nothrow_constructible) // NOLINT : base{in_place_type, detail::extract_error_from_failure(static_cast &&>(o))} , _ptr(detail::extract_exception_from_failure(static_cast &&>(o))) { @@ -861,10 +873,10 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TEXPR(std::declval>() == std::declval>()), // BOOST_OUTCOME_TEXPR(std::declval>() == std::declval>()), // BOOST_OUTCOME_TEXPR(std::declval>() == std::declval>())) - constexpr bool operator==(const basic_outcome &o) const noexcept( // - noexcept(std::declval>() == std::declval>()) // - &&noexcept(std::declval>() == std::declval>()) // - &&noexcept(std::declval>() == std::declval>())) + constexpr bool operator==(const basic_outcome &o) const noexcept( // + noexcept(std::declval>() == std::declval>()) // + && noexcept(std::declval>() == std::declval>()) // + && noexcept(std::declval>() == std::declval>())) { if(this->_state._status.have_value() && o._state._status.have_value()) { @@ -892,7 +904,7 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TEXPR(std::declval() == std::declval()), // BOOST_OUTCOME_TEXPR(std::declval() == std::declval())) constexpr bool operator==(const failure_type &o) const noexcept( // - noexcept(std::declval() == std::declval()) &&noexcept(std::declval() == std::declval())) + noexcept(std::declval() == std::declval()) && noexcept(std::declval() == std::declval())) { if(this->_state._status.have_error() && o._state._status.have_error() // && this->_state._status.have_exception() && o._state._status.have_exception()) @@ -916,10 +928,10 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TEXPR(std::declval>() != std::declval>()), // BOOST_OUTCOME_TEXPR(std::declval>() != std::declval>()), // BOOST_OUTCOME_TEXPR(std::declval>() != std::declval>())) - constexpr bool operator!=(const basic_outcome &o) const noexcept( // - noexcept(std::declval>() != std::declval>()) // - &&noexcept(std::declval>() != std::declval>()) // - &&noexcept(std::declval>() != std::declval>())) + constexpr bool operator!=(const basic_outcome &o) const noexcept( // + noexcept(std::declval>() != std::declval>()) // + && noexcept(std::declval>() != std::declval>()) // + && noexcept(std::declval>() != std::declval>())) { if(this->_state._status.have_value() && o._state._status.have_value()) { @@ -947,7 +959,7 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TEXPR(std::declval() != std::declval()), // BOOST_OUTCOME_TEXPR(std::declval() != std::declval())) constexpr bool operator!=(const failure_type &o) const noexcept( // - noexcept(std::declval() == std::declval()) &&noexcept(std::declval() == std::declval())) + noexcept(std::declval() == std::declval()) && noexcept(std::declval() == std::declval())) { if(this->_state._status.have_error() && o._state._status.have_error() // && this->_state._status.have_exception() && o._state._status.have_exception()) @@ -988,37 +1000,38 @@ SIGNATURE NOT RECOGNISED swap(this->_ptr, o._ptr); return; } - struct _ + struct some_type { basic_outcome &a, &b; bool exceptioned{false}; bool all_good{false}; - ~_() + ~some_type() { - if(!all_good) + if(!this->all_good) { // We lost one of the values - a._state._status.set_have_lost_consistency(true); - b._state._status.set_have_lost_consistency(true); + this->a._state._status.set_have_lost_consistency(true); + this->b._state._status.set_have_lost_consistency(true); return; } - if(exceptioned) + if(this->exceptioned) { // The value + error swap threw an exception. Try to swap back _ptr try { - strong_swap(all_good, a._ptr, b._ptr); + strong_swap(this->all_good, this->a._ptr, this->b._ptr); } catch(...) { // We lost one of the values - a._state._status.set_have_lost_consistency(true); - b._state._status.set_have_lost_consistency(true); + this->a._state._status.set_have_lost_consistency(true); + this->b._state._status.set_have_lost_consistency(true); // throw away second exception } // Prevent has_value() == has_error() or has_value() == has_exception() - auto check = [](basic_outcome *t) { + auto check = [](basic_outcome *t) + { if(t->has_value() && (t->has_error() || t->has_exception())) { t->_state._status.set_have_error(false).set_have_exception(false); @@ -1030,15 +1043,15 @@ SIGNATURE NOT RECOGNISED t->_state._status.set_have_error(true).set_have_lost_consistency(true); } }; - check(&a); - check(&b); + check(&this->a); + check(&this->b); } } - } _{*this, o}; - strong_swap(_.all_good, this->_ptr, o._ptr); - _.exceptioned = true; + } some_type_value{*this, o}; + strong_swap(some_type_value.all_good, this->_ptr, o._ptr); + some_type_value.exceptioned = true; this->_state.swap(o._state); - _.exceptioned = false; + some_type_value.exceptioned = false; #ifdef _MSC_VER #pragma warning(pop) #endif @@ -1137,6 +1150,10 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_V2_NAMESPACE_END +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif + #ifdef __clang__ #pragma clang diagnostic pop #endif diff --git a/include/boost/outcome/basic_result.hpp b/include/boost/outcome/basic_result.hpp index cb8b3c606..9eeb5e3b2 100644 --- a/include/boost/outcome/basic_result.hpp +++ b/include/boost/outcome/basic_result.hpp @@ -1,5 +1,5 @@ /* A very simple result type -(C) 2017-2023 Niall Douglas (14 commits) +(C) 2017-2025 Niall Douglas (14 commits) File Created: June 2017 @@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE. #include "config.hpp" #include "convert.hpp" #include "detail/basic_result_final.hpp" +#include "outcome_gdb.h" #include "policy/all_narrow.hpp" #include "policy/terminate.hpp" @@ -43,6 +44,11 @@ DEALINGS IN THE SOFTWARE. #pragma clang diagnostic ignored "-Wdocumentation" // Standardese markup confuses clang #endif +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable: 6287) // redundant code +#endif + BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN template // @@ -142,16 +148,31 @@ namespace detail && !std::is_same, disable_inplace_value_error_constructor>::value; }; - template constexpr inline const U &extract_value_from_success(const success_type &v) { return v.value(); } - template constexpr inline U &&extract_value_from_success(success_type &&v) { return static_cast &&>(v).value(); } - template constexpr inline T extract_value_from_success(const success_type & /*unused*/) { return T{}; } + template constexpr inline const U &extract_value_from_success(const success_type &v) + { + return v.value(); + } + template constexpr inline U &&extract_value_from_success(success_type &&v) + { + return static_cast &&>(v).value(); + } + template constexpr inline T extract_value_from_success(const success_type & /*unused*/) + { + return T{}; + } - template constexpr inline const U &extract_error_from_failure(const failure_type &v) { return v.error(); } + template constexpr inline const U &extract_error_from_failure(const failure_type &v) + { + return v.error(); + } template constexpr inline U &&extract_error_from_failure(failure_type &&v) { return static_cast &&>(v).error(); } - template constexpr inline T extract_error_from_failure(const failure_type & /*unused*/) { return T{}; } + template constexpr inline T extract_error_from_failure(const failure_type & /*unused*/) + { + return T{}; + } template struct is_basic_result { @@ -361,7 +382,7 @@ class BOOST_OUTCOME_NODISCARD basic_result : public detail::basic_result_final static constexpr bool enable_inplace_value_error_constructor = // constructors_enabled // - &&base::template enable_inplace_value_error_constructor; + && base::template enable_inplace_value_error_constructor; template using choose_inplace_value_error_constructor = typename base::template choose_inplace_value_error_constructor; }; @@ -393,7 +414,7 @@ SIGNATURE NOT RECOGNISED */ BOOST_OUTCOME_TEMPLATE(class Arg, class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(!predicate::constructors_enabled && (sizeof...(Args) >= 0))) - basic_result(Arg && /*unused*/, Args &&... /*unused*/) = delete; // NOLINT basic_result is NOT SUPPORTED, see docs! + basic_result(Arg && /*unused*/, Args &&.../*unused*/) = delete; // NOLINT basic_result is NOT SUPPORTED, see docs! /*! AWAITING HUGO JSON CONVERSION TOOL SIGNATURE NOT RECOGNISED @@ -457,8 +478,8 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_compatible_conversion)) constexpr explicit basic_result( const basic_result &o, - explicit_compatible_copy_conversion_tag /*unused*/ = - explicit_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible &&detail::is_nothrow_constructible) + explicit_compatible_copy_conversion_tag /*unused*/ = explicit_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible && + detail::is_nothrow_constructible) : base{typename base::compatible_conversion_tag(), o} { no_value_policy_type::on_result_copy_construction(this, o); @@ -470,8 +491,8 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_compatible_conversion)) constexpr explicit basic_result( basic_result &&o, - explicit_compatible_move_conversion_tag /*unused*/ = - explicit_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible &&detail::is_nothrow_constructible) + explicit_compatible_move_conversion_tag /*unused*/ = explicit_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible && + detail::is_nothrow_constructible) : base{typename base::compatible_conversion_tag(), static_cast &&>(o)} { no_value_policy_type::on_result_move_construction(this, static_cast &&>(o)); @@ -483,8 +504,8 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_error_code_compatible_conversion)) constexpr explicit basic_result(const basic_result &o, explicit_make_error_code_compatible_copy_conversion_tag /*unused*/ = - explicit_make_error_code_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible - &&noexcept(make_error_code(std::declval()))) + explicit_make_error_code_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible && + noexcept(make_error_code(std::declval()))) : base{typename base::make_error_code_compatible_conversion_tag(), o} { no_value_policy_type::on_result_copy_construction(this, o); @@ -496,8 +517,8 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_error_code_compatible_conversion)) constexpr explicit basic_result(basic_result &&o, explicit_make_error_code_compatible_move_conversion_tag /*unused*/ = - explicit_make_error_code_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible - &&noexcept(make_error_code(std::declval()))) + explicit_make_error_code_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible && + noexcept(make_error_code(std::declval()))) : base{typename base::make_error_code_compatible_conversion_tag(), static_cast &&>(o)} { no_value_policy_type::on_result_move_construction(this, static_cast &&>(o)); @@ -509,8 +530,8 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_exception_ptr_compatible_conversion)) constexpr explicit basic_result(const basic_result &o, explicit_make_exception_ptr_compatible_copy_conversion_tag /*unused*/ = - explicit_make_exception_ptr_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible - &&noexcept(make_exception_ptr(std::declval()))) + explicit_make_exception_ptr_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible && + noexcept(make_exception_ptr(std::declval()))) : base{typename base::make_exception_ptr_compatible_conversion_tag(), o} { no_value_policy_type::on_result_copy_construction(this, o); @@ -522,8 +543,8 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_exception_ptr_compatible_conversion)) constexpr explicit basic_result(basic_result &&o, explicit_make_exception_ptr_compatible_move_conversion_tag /*unused*/ = - explicit_make_exception_ptr_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible - &&noexcept(make_exception_ptr(std::declval()))) + explicit_make_exception_ptr_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible && + noexcept(make_exception_ptr(std::declval()))) : base{typename base::make_exception_ptr_compatible_conversion_tag(), static_cast &&>(o)} { no_value_policy_type::on_result_move_construction(this, static_cast &&>(o)); @@ -534,7 +555,7 @@ SIGNATURE NOT RECOGNISED */ BOOST_OUTCOME_TEMPLATE(class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_value_constructor)) - constexpr explicit basic_result(in_place_type_t _, Args &&... args) noexcept(detail::is_nothrow_constructible) + constexpr explicit basic_result(in_place_type_t _, Args &&...args) noexcept(detail::is_nothrow_constructible) : base{_, static_cast(args)...} { no_value_policy_type::on_result_in_place_construction(this, in_place_type, static_cast(args)...); @@ -545,7 +566,7 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TEMPLATE(class U, class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_value_constructor, Args...>)) constexpr explicit basic_result(in_place_type_t _, std::initializer_list il, - Args &&... args) noexcept(detail::is_nothrow_constructible, Args...>) + Args &&...args) noexcept(detail::is_nothrow_constructible, Args...>) : base{_, il, static_cast(args)...} { no_value_policy_type::on_result_in_place_construction(this, in_place_type, il, static_cast(args)...); @@ -555,7 +576,7 @@ SIGNATURE NOT RECOGNISED */ BOOST_OUTCOME_TEMPLATE(class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_error_constructor)) - constexpr explicit basic_result(in_place_type_t _, Args &&... args) noexcept(detail::is_nothrow_constructible) + constexpr explicit basic_result(in_place_type_t _, Args &&...args) noexcept(detail::is_nothrow_constructible) : base{_, static_cast(args)...} { no_value_policy_type::on_result_in_place_construction(this, in_place_type, static_cast(args)...); @@ -566,7 +587,7 @@ SIGNATURE NOT RECOGNISED BOOST_OUTCOME_TEMPLATE(class U, class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_error_constructor, Args...>)) constexpr explicit basic_result(in_place_type_t _, std::initializer_list il, - Args &&... args) noexcept(detail::is_nothrow_constructible, Args...>) + Args &&...args) noexcept(detail::is_nothrow_constructible, Args...>) : base{_, il, static_cast(args)...} { no_value_policy_type::on_result_in_place_construction(this, in_place_type, il, static_cast(args)...); @@ -576,7 +597,7 @@ SIGNATURE NOT RECOGNISED */ BOOST_OUTCOME_TEMPLATE(class A1, class A2, class... Args) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_value_error_constructor)) - constexpr basic_result(A1 &&a1, A2 &&a2, Args &&... args) noexcept(noexcept( + constexpr basic_result(A1 &&a1, A2 &&a2, Args &&...args) noexcept(noexcept( typename predicate::template choose_inplace_value_error_constructor(std::declval(), std::declval(), std::declval()...))) : basic_result(in_place_type>, static_cast(a1), static_cast(a2), static_cast(args)...) @@ -745,6 +766,10 @@ static_assert(std::is_standard_layout (7 commits) +(C) 2017-2025 Niall Douglas (7 commits) File Created: June 2017 diff --git a/include/boost/outcome/boost_result.hpp b/include/boost/outcome/boost_result.hpp index 94d914ad1..fa2eda069 100644 --- a/include/boost/outcome/boost_result.hpp +++ b/include/boost/outcome/boost_result.hpp @@ -1,5 +1,5 @@ /* A very simple result type -(C) 2017-2023 Niall Douglas (10 commits) +(C) 2017-2025 Niall Douglas (10 commits) File Created: June 2017 diff --git a/include/boost/outcome/config.hpp b/include/boost/outcome/config.hpp index 3655d9b89..785978a30 100644 --- a/include/boost/outcome/config.hpp +++ b/include/boost/outcome/config.hpp @@ -1,5 +1,5 @@ /* Configure Boost.Outcome with Boost -(C) 2015-2023 Niall Douglas (7 commits) +(C) 2015-2025 Niall Douglas (7 commits) File Created: August 2015 @@ -69,15 +69,16 @@ DEALINGS IN THE SOFTWARE. #define BOOST_OUTCOME_NODISCARD __attribute__((warn_unused_result)) #elif defined(_MSC_VER) // _Must_inspect_result_ expands into this -#define BOOST_OUTCOME_NODISCARD \ - __declspec("SAL_name" \ - "(" \ - "\"_Must_inspect_result_\"" \ - "," \ - "\"\"" \ - "," \ - "\"2\"" \ - ")") __declspec("SAL_begin") __declspec("SAL_post") __declspec("SAL_mustInspect") __declspec("SAL_post") __declspec("SAL_checkReturn") __declspec("SAL_end") +#define BOOST_OUTCOME_NODISCARD \ + __declspec( \ + "SAL_name" \ + "(" \ + "\"_Must_inspect_result_\"" \ + "," \ + "\"\"" \ + "," \ + "\"2\"" \ + ")") __declspec("SAL_begin") __declspec("SAL_post") __declspec("SAL_mustInspect") __declspec("SAL_post") __declspec("SAL_checkReturn") __declspec("SAL_end") #endif #endif #ifndef BOOST_OUTCOME_NODISCARD @@ -130,28 +131,28 @@ namespace boost } } /*! The namespace of this Boost.Outcome v2. -*/ + */ #define BOOST_OUTCOME_V2_NAMESPACE boost::outcome_v2 /*! Expands into the appropriate namespace markup to enter the Boost.Outcome v2 namespace. -*/ -#define BOOST_OUTCOME_V2_NAMESPACE_BEGIN \ - namespace boost \ - { \ - namespace outcome_v2 \ + */ +#define BOOST_OUTCOME_V2_NAMESPACE_BEGIN \ + namespace boost \ + { \ + namespace outcome_v2 \ { /*! Expands into the appropriate namespace markup to enter the C++ module exported Boost.Outcome v2 namespace. */ -#define BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN \ - namespace boost \ - { \ - namespace outcome_v2 \ +#define BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN \ + namespace boost \ + { \ + namespace outcome_v2 \ { /*! \brief Expands into the appropriate namespace markup to exit the Boost.Outcome v2 namespace. \ingroup config */ -#define BOOST_OUTCOME_V2_NAMESPACE_END \ - } \ +#define BOOST_OUTCOME_V2_NAMESPACE_END \ + } \ } #include // for uint32_t etc @@ -198,6 +199,18 @@ template constexpr in_place_type_t in_place_type{}; BOOST_OUTCOME_V2_NAMESPACE_END #endif +#if defined(BOOST_OUTCOME_USE_STD_ADDRESSOF) && !BOOST_OUTCOME_USE_STD_ADDRESSOF +#define BOOST_OUTCOME_ADDRESS_OF(...) (&__VA_ARGS__) +#else +#include // for std::addressof +#define BOOST_OUTCOME_ADDRESS_OF(...) std::addressof(__VA_ARGS__) +#endif + +#ifndef BOOST_OUTCOME_ASSERT +#include +#define BOOST_OUTCOME_ASSERT(...) BOOST_ASSERT(__VA_ARGS__) +#endif + #ifndef BOOST_OUTCOME_TRIVIAL_ABI #if defined(STANDARDESE_IS_IN_THE_HOUSE) || __clang_major__ >= 7 //! Defined to be `[[clang::trivial_abi]]` when on a new enough clang compiler. Usually automatic, can be overriden. diff --git a/include/boost/outcome/convert.hpp b/include/boost/outcome/convert.hpp index 5a118a984..78a1ba6bb 100644 --- a/include/boost/outcome/convert.hpp +++ b/include/boost/outcome/convert.hpp @@ -1,5 +1,5 @@ /* Says how to convert value, error and exception types -(C) 2017-2023 Niall Douglas (12 commits) +(C) 2017-2025 Niall Douglas (12 commits) File Created: Nov 2017 @@ -38,43 +38,55 @@ BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN namespace concepts { #if defined(__cpp_concepts) -#if (defined(_MSC_VER) || defined(__clang__) || (defined(__GNUC__) && __cpp_concepts >= 201707) || BOOST_OUTCOME_FORCE_STD_BOOST_OUTCOME_C_CONCEPTS) && !BOOST_OUTCOME_FORCE_LEGACY_GCC_BOOST_OUTCOME_C_CONCEPTS +#if(defined(_MSC_VER) || defined(__clang__) || (defined(__GNUC__) && __cpp_concepts >= 201707) || BOOST_OUTCOME_FORCE_STD_BOOST_OUTCOME_C_CONCEPTS) && \ +!BOOST_OUTCOME_FORCE_LEGACY_GCC_BOOST_OUTCOME_C_CONCEPTS #define BOOST_OUTCOME_GCC6_CONCEPT_BOOL #else +#ifndef BOOST_OUTCOME_SUPPRESS_LEGACY_CONCEPTS_WARNING +#warning "WARNING: Legacy GCC concepts are known to fail to compile in a number of important situations!" +#endif #define BOOST_OUTCOME_GCC6_CONCEPT_BOOL bool #endif namespace detail { - template concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL SameHelper = std::is_same::value; - template concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL same_as = detail::SameHelper &&detail::SameHelper; - template concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL convertible = std::is_convertible::value; - template concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL base_of = std::is_base_of::value; + template + concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL SameHelper = std::is_same::value; + template + concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL same_as = detail::SameHelper && detail::SameHelper; + template + concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL convertible = std::is_convertible::value; + template + concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL base_of = std::is_base_of::value; } // namespace detail /* The `value_or_none` concept. \requires That `U::value_type` exists and that `std::declval().has_value()` returns a `bool` and `std::declval().value()` exists. */ - template concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL value_or_none = requires(U a) - { + template + concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL value_or_none = requires(U a) { { a.has_value() - } - ->detail::same_as; - {a.value()}; + } -> detail::same_as; + { + a.value() + }; }; /* The `value_or_error` concept. \requires That `U::value_type` and `U::error_type` exist; that `std::declval().has_value()` returns a `bool`, `std::declval().value()` and `std::declval().error()` exists. */ - template concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL value_or_error = requires(U a) - { + template + concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL value_or_error = requires(U a) { { a.has_value() - } - ->detail::same_as; - {a.value()}; - {a.error()}; + } -> detail::same_as; + { + a.value() + }; + { + a.error() + }; }; #else @@ -115,8 +127,10 @@ namespace convert { #if BOOST_OUTCOME_ENABLE_LEGACY_SUPPORT_FOR < 220 #if defined(__cpp_concepts) - template concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL ValueOrNone = concepts::value_or_none; - template concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL ValueOrError = concepts::value_or_error; + template + concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL ValueOrNone = concepts::value_or_none; + template + concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL ValueOrError = concepts::value_or_error; #else template static constexpr bool ValueOrNone = concepts::value_or_none; template static constexpr bool ValueOrError = concepts::value_or_error; @@ -148,8 +162,8 @@ type definition value_or_error. Potential doc page: NOT FOUND static constexpr bool enable_outcome_inputs = false; BOOST_OUTCOME_TEMPLATE(class X) BOOST_OUTCOME_TREQUIRES( - BOOST_OUTCOME_TPRED(std::is_same>::value // - &&concepts::value_or_error // + BOOST_OUTCOME_TPRED(std::is_same>::value // + &&concepts::value_or_error // && (std::is_void::value_type>::value || BOOST_OUTCOME_V2_NAMESPACE::detail::is_explicitly_constructible::value_type>) // &&(std::is_void::error_type>::value || diff --git a/include/boost/outcome/coroutine_support.hpp b/include/boost/outcome/coroutine_support.hpp index 1a1d01d1c..0f37d75c1 100644 --- a/include/boost/outcome/coroutine_support.hpp +++ b/include/boost/outcome/coroutine_support.hpp @@ -1,5 +1,5 @@ /* Tells C++ coroutines about Outcome's result -(C) 2019-2023 Niall Douglas (12 commits) +(C) 2019-2025 Niall Douglas (12 commits) File Created: Oct 2019 diff --git a/include/boost/outcome/detail/basic_outcome_exception_observers.hpp b/include/boost/outcome/detail/basic_outcome_exception_observers.hpp index e962ce313..ed3dd8793 100644 --- a/include/boost/outcome/detail/basic_outcome_exception_observers.hpp +++ b/include/boost/outcome/detail/basic_outcome_exception_observers.hpp @@ -1,5 +1,5 @@ /* Exception observers for outcome type -(C) 2017-2023 Niall Douglas (3 commits) +(C) 2017-2025 Niall Douglas (3 commits) File Created: Oct 2017 @@ -44,9 +44,9 @@ namespace detail using Base::Base; constexpr inline exception_type &assume_exception() & noexcept; - constexpr inline const exception_type &assume_exception() const &noexcept; + constexpr inline const exception_type &assume_exception() const & noexcept; constexpr inline exception_type &&assume_exception() && noexcept; - constexpr inline const exception_type &&assume_exception() const &&noexcept; + constexpr inline const exception_type &&assume_exception() const && noexcept; constexpr inline exception_type &exception() &; constexpr inline const exception_type &exception() const &; @@ -59,8 +59,16 @@ namespace detail { public: using Base::Base; - constexpr void assume_exception() const noexcept { NoValuePolicy::narrow_exception_check(this); } - constexpr void exception() const { NoValuePolicy::wide_exception_check(this); } + + constexpr void assume_exception() & noexcept { NoValuePolicy::narrow_exception_check(*this); } + constexpr void assume_exception() const & noexcept { NoValuePolicy::narrow_exception_check(*this); } + constexpr void assume_exception() && noexcept { NoValuePolicy::narrow_exception_check(std::move(*this)); } + constexpr void assume_exception() const && noexcept { NoValuePolicy::narrow_exception_check(std::move(*this)); } + + constexpr void exception() & { NoValuePolicy::wide_exception_check(*this); } + constexpr void exception() const & { NoValuePolicy::wide_exception_check(*this); } + constexpr void exception() && { NoValuePolicy::wide_exception_check(std::move(*this)); } + constexpr void exception() const && { NoValuePolicy::wide_exception_check(std::move(*this)); } }; } // namespace detail diff --git a/include/boost/outcome/detail/basic_outcome_exception_observers_impl.hpp b/include/boost/outcome/detail/basic_outcome_exception_observers_impl.hpp index 63549528e..5009ec30f 100644 --- a/include/boost/outcome/detail/basic_outcome_exception_observers_impl.hpp +++ b/include/boost/outcome/detail/basic_outcome_exception_observers_impl.hpp @@ -1,5 +1,5 @@ /* Exception observers for outcome type -(C) 2017-2023 Niall Douglas (6 commits) +(C) 2017-2025 Niall Douglas (6 commits) File Created: Oct 2017 diff --git a/include/boost/outcome/detail/basic_outcome_failure_observers.hpp b/include/boost/outcome/detail/basic_outcome_failure_observers.hpp index 5be9922b1..da233ada1 100644 --- a/include/boost/outcome/detail/basic_outcome_failure_observers.hpp +++ b/include/boost/outcome/detail/basic_outcome_failure_observers.hpp @@ -1,5 +1,5 @@ /* Failure observers for outcome type -(C) 2017-2023 Niall Douglas (7 commits) +(C) 2017-2025 Niall Douglas (7 commits) File Created: Oct 2017 diff --git a/include/boost/outcome/detail/basic_result_error_observers.hpp b/include/boost/outcome/detail/basic_result_error_observers.hpp index 4c6469878..ab40ddbbd 100644 --- a/include/boost/outcome/detail/basic_result_error_observers.hpp +++ b/include/boost/outcome/detail/basic_result_error_observers.hpp @@ -1,5 +1,5 @@ /* Error observers for a very simple basic_result type -(C) 2017-2023 Niall Douglas (2 commits) +(C) 2017-2025 Niall Douglas (2 commits) File Created: Oct 2017 @@ -48,7 +48,7 @@ namespace detail NoValuePolicy::narrow_error_check(static_cast(*this)); return this->_state._error; } - constexpr const error_type &assume_error() const &noexcept + constexpr const error_type &assume_error() const & noexcept { NoValuePolicy::narrow_error_check(static_cast(*this)); return this->_state._error; @@ -58,7 +58,7 @@ namespace detail NoValuePolicy::narrow_error_check(static_cast(*this)); return static_cast(this->_state._error); } - constexpr const error_type &&assume_error() const &&noexcept + constexpr const error_type &&assume_error() const && noexcept { NoValuePolicy::narrow_error_check(static_cast(*this)); return static_cast(this->_state._error); @@ -89,8 +89,16 @@ namespace detail { public: using Base::Base; - constexpr void assume_error() const noexcept { NoValuePolicy::narrow_error_check(*this); } - constexpr void error() const { NoValuePolicy::wide_error_check(*this); } + + constexpr void assume_error() & noexcept { NoValuePolicy::narrow_error_check(static_cast(*this)); } + constexpr void assume_error() const & noexcept { NoValuePolicy::narrow_error_check(static_cast(*this)); } + constexpr void assume_error() && noexcept { NoValuePolicy::narrow_error_check(static_cast(*this)); } + constexpr void assume_error() const && noexcept { NoValuePolicy::narrow_error_check(static_cast(*this)); } + + constexpr void error() & { NoValuePolicy::wide_error_check(static_cast(*this)); } + constexpr void error() const & { NoValuePolicy::wide_error_check(static_cast(*this)); } + constexpr void error() && { NoValuePolicy::wide_error_check(static_cast(*this)); } + constexpr void error() const && { NoValuePolicy::wide_error_check(static_cast(*this)); } }; } // namespace detail BOOST_OUTCOME_V2_NAMESPACE_END diff --git a/include/boost/outcome/detail/basic_result_final.hpp b/include/boost/outcome/detail/basic_result_final.hpp index 05a037556..90f11b0db 100644 --- a/include/boost/outcome/detail/basic_result_final.hpp +++ b/include/boost/outcome/detail/basic_result_final.hpp @@ -1,5 +1,5 @@ /* Finaliser for a very simple result type -(C) 2017-2023 Niall Douglas (5 commits) +(C) 2017-2025 Niall Douglas (5 commits) File Created: Oct 2017 diff --git a/include/boost/outcome/detail/basic_result_storage.hpp b/include/boost/outcome/detail/basic_result_storage.hpp index 9d7eec2f7..8334f390b 100644 --- a/include/boost/outcome/detail/basic_result_storage.hpp +++ b/include/boost/outcome/detail/basic_result_storage.hpp @@ -1,5 +1,5 @@ /* Storage for a very simple basic_result type -(C) 2017-2023 Niall Douglas (6 commits) +(C) 2017-2025 Niall Douglas (6 commits) File Created: Oct 2017 diff --git a/include/boost/outcome/detail/basic_result_value_observers.hpp b/include/boost/outcome/detail/basic_result_value_observers.hpp index f657c4368..5c5c0d527 100644 --- a/include/boost/outcome/detail/basic_result_value_observers.hpp +++ b/include/boost/outcome/detail/basic_result_value_observers.hpp @@ -1,5 +1,5 @@ /* Value observers for a very simple basic_result type -(C) 2017-2023 Niall Douglas (2 commits) +(C) 2017-2025 Niall Douglas (2 commits) File Created: Oct 2017 @@ -48,7 +48,7 @@ namespace detail NoValuePolicy::narrow_value_check(static_cast(*this)); return this->_state._value; // NOLINT } - constexpr const value_type &assume_value() const &noexcept + constexpr const value_type &assume_value() const & noexcept { NoValuePolicy::narrow_value_check(static_cast(*this)); return this->_state._value; // NOLINT @@ -58,7 +58,7 @@ namespace detail NoValuePolicy::narrow_value_check(static_cast(*this)); return static_cast(this->_state._value); // NOLINT } - constexpr const value_type &&assume_value() const &&noexcept + constexpr const value_type &&assume_value() const && noexcept { NoValuePolicy::narrow_value_check(static_cast(*this)); return static_cast(this->_state._value); // NOLINT @@ -90,8 +90,15 @@ namespace detail public: using Base::Base; - constexpr void assume_value() const noexcept { NoValuePolicy::narrow_value_check(*this); } - constexpr void value() const { NoValuePolicy::wide_value_check(*this); } + constexpr void assume_value() & noexcept { NoValuePolicy::narrow_value_check(static_cast(*this)); } + constexpr void assume_value() const & noexcept { NoValuePolicy::narrow_value_check(static_cast(*this)); } + constexpr void assume_value() && noexcept { NoValuePolicy::narrow_value_check(static_cast(*this)); } + constexpr void assume_value() const && noexcept { NoValuePolicy::narrow_value_check(static_cast(*this)); } + + constexpr void value() & { NoValuePolicy::wide_value_check(static_cast(*this)); } + constexpr void value() const & { NoValuePolicy::wide_value_check(static_cast(*this)); } + constexpr void value() && { NoValuePolicy::wide_value_check(static_cast(*this)); } + constexpr void value() const && { NoValuePolicy::wide_value_check(static_cast(*this)); } }; } // namespace detail diff --git a/include/boost/outcome/detail/coroutine_support.ipp b/include/boost/outcome/detail/coroutine_support.ipp index 0d421505b..efd553f29 100644 --- a/include/boost/outcome/detail/coroutine_support.ipp +++ b/include/boost/outcome/detail/coroutine_support.ipp @@ -1,5 +1,5 @@ /* Tells C++ coroutines about Outcome's result -(C) 2019-2023 Niall Douglas (12 commits) +(C) 2019-2025 Niall Douglas (12 commits) File Created: Oct 2019 @@ -36,10 +36,19 @@ DEALINGS IN THE SOFTWARE. #define BOOST_OUTCOME_DETAIL_COROUTINE_SUPPORT_HPP #include -#include #include -#if __cpp_impl_coroutine || (defined(_MSC_VER) && __cpp_coroutines) || (defined(__clang__) && __cpp_coroutines) +#ifndef BOOST_OUTCOME_COROUTINE_HEADER_TYPE +#if __has_include() +#define BOOST_OUTCOME_COROUTINE_HEADER_TYPE 1 +#elif __has_include() +#define BOOST_OUTCOME_COROUTINE_HEADER_TYPE 2 +#else +#define BOOST_OUTCOME_COROUTINE_HEADER_TYPE 0 +#endif +#endif + +#if BOOST_OUTCOME_COROUTINE_HEADER_TYPE && (__cpp_impl_coroutine || (defined(_MSC_VER) && __cpp_coroutines)) #ifndef BOOST_OUTCOME_HAVE_NOOP_COROUTINE #if defined(__has_builtin) #if __has_builtin(__builtin_coro_noop) || (!defined(__clang__) && __GNUC__ >= 10) @@ -54,7 +63,7 @@ DEALINGS IN THE SOFTWARE. #define BOOST_OUTCOME_HAVE_NOOP_COROUTINE 0 #endif #endif -#if __has_include() +#if BOOST_OUTCOME_COROUTINE_HEADER_TYPE == 1 #include BOOST_OUTCOME_V2_NAMESPACE_BEGIN namespace awaitables @@ -69,7 +78,7 @@ namespace awaitables } // namespace awaitables BOOST_OUTCOME_V2_NAMESPACE_END #define BOOST_OUTCOME_FOUND_COROUTINE_HEADER 1 -#elif __has_include() +#elif BOOST_OUTCOME_COROUTINE_HEADER_TYPE == 2 #include BOOST_OUTCOME_V2_NAMESPACE_BEGIN namespace awaitables @@ -87,6 +96,12 @@ BOOST_OUTCOME_V2_NAMESPACE_END #endif #endif +#ifndef BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER +// #include +// #define BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(...) std::cout << __VA_ARGS__ << std::endl; +#define BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(...) +#endif + BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN namespace awaitables { @@ -102,10 +117,22 @@ namespace awaitables { using type = T; }; - template constexpr inline type_found extract_error_type(int /*unused*/) { return {}; } - template constexpr inline type_found extract_error_type(...) { return {}; } - template constexpr inline type_found extract_exception_type(int /*unused*/) { return {}; } - template constexpr inline type_found extract_exception_type(...) { return {}; } + template constexpr inline type_found extract_error_type(int /*unused*/) + { + return {}; + } + template constexpr inline type_found extract_error_type(...) + { + return {}; + } + template constexpr inline type_found extract_exception_type(int /*unused*/) + { + return {}; + } + template constexpr inline type_found extract_exception_type(...) + { + return {}; + } BOOST_OUTCOME_TEMPLATE(class T, class U) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(BOOST_OUTCOME_V2_NAMESPACE::detail::is_constructible)) @@ -114,11 +141,20 @@ namespace awaitables new(result) U(static_cast(e)); return true; } - template inline bool try_set_error(T && /*unused*/, ...) { return false; } + template inline bool try_set_error(T && /*unused*/, ...) + { + return false; + } BOOST_OUTCOME_TEMPLATE(class T, class U) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(BOOST_OUTCOME_V2_NAMESPACE::detail::is_constructible)) - inline void set_or_rethrow(T &e, U *result) { new(result) U(e); } - template inline void set_or_rethrow(T &e, ...) { rethrow_exception(e); } + inline void set_or_rethrow(T &e, U *result) + { + new(result) U(e); + } + template inline void set_or_rethrow(T &e, ...) + { + rethrow_exception(e); + } template class fake_atomic { T _v; @@ -130,6 +166,15 @@ namespace awaitables } T load(std::memory_order /*unused*/) { return _v; } void store(T v, std::memory_order /*unused*/) { _v = v; } + bool compare_exchange_strong(T &expected, T v, std::memory_order /*unused*/, std::memory_order /*unused*/) + { + if(_v == expected) + { + _v = v; + return true; + } + return false; + } }; #ifdef BOOST_OUTCOME_FOUND_COROUTINE_HEADER @@ -142,16 +187,20 @@ namespace awaitables BOOST_OUTCOME_V2_NAMESPACE::detail::empty_type _default{}; container_type result; }; - result_set_type result_set{false}; + result_set_type result_set{false}, pending_first_resumption{is_initially_suspended}; coroutine_handle<> continuation; - outcome_promise_type() noexcept {} + static constexpr bool is_initially_suspended = suspend_initial; + static constexpr bool is_using_atomics = use_atomic; + + outcome_promise_type() noexcept { BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise constructed"); } outcome_promise_type(const outcome_promise_type &) = delete; outcome_promise_type(outcome_promise_type &&) = delete; outcome_promise_type &operator=(const outcome_promise_type &) = delete; outcome_promise_type &operator=(outcome_promise_type &&) = delete; ~outcome_promise_type() { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise destructs"); if(result_set.load(std::memory_order_acquire)) { result.~container_type(); // could throw @@ -159,11 +208,13 @@ namespace awaitables } auto get_return_object() { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise returns awaitable"); return Awaitable{*this}; // could throw bad_alloc } void return_value(container_type &&value) { - assert(!result_set.load(std::memory_order_acquire)); + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise returns value"); + BOOST_OUTCOME_ASSERT(!result_set.load(std::memory_order_acquire)); if(result_set.load(std::memory_order_acquire)) { result.~container_type(); // could throw @@ -173,7 +224,8 @@ namespace awaitables } void return_value(const container_type &value) { - assert(!result_set.load(std::memory_order_acquire)); + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise returns value"); + BOOST_OUTCOME_ASSERT(!result_set.load(std::memory_order_acquire)); if(result_set.load(std::memory_order_acquire)) { result.~container_type(); // could throw @@ -183,7 +235,8 @@ namespace awaitables } void unhandled_exception() { - assert(!result_set.load(std::memory_order_acquire)); + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise unhandled exception"); + BOOST_OUTCOME_ASSERT(!result_set.load(std::memory_order_acquire)); if(result_set.load(std::memory_order_acquire)) { result.~container_type(); @@ -203,9 +256,10 @@ namespace awaitables } auto initial_suspend() noexcept { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise initial suspend = " << suspend_initial); struct awaiter { - bool await_ready() noexcept { return !suspend_initial; } + constexpr bool await_ready() noexcept { return !suspend_initial; } void await_resume() noexcept {} void await_suspend(coroutine_handle<> /*unused*/) noexcept {} }; @@ -215,11 +269,21 @@ namespace awaitables { struct awaiter { - bool await_ready() noexcept { return false; } + // If we don't force a final suspend, promise will get deleted before awaitable + // TODO: Implement detachable awaitables + constexpr bool await_ready() noexcept { return false; } void await_resume() noexcept {} #if BOOST_OUTCOME_HAVE_NOOP_COROUTINE coroutine_handle<> await_suspend(coroutine_handle self) noexcept { + if(self.promise().continuation) + { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will resume coroutine " << self.promise().continuation.address()); + } + else + { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will exit"); + } return self.promise().continuation ? self.promise().continuation : noop_coroutine(); } #else @@ -227,8 +291,13 @@ namespace awaitables { if(self.promise().continuation) { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will resume coroutine " << self.promise().continuation.address()); return self.promise().continuation.resume(); } + else + { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will exit"); + } } #endif }; @@ -239,10 +308,13 @@ namespace awaitables { using container_type = void; using result_set_type = std::conditional_t, fake_atomic>; - result_set_type result_set{false}; + result_set_type result_set{false}, pending_first_resumption{is_initially_suspended}; coroutine_handle<> continuation; - outcome_promise_type() {} + static constexpr bool is_initially_suspended = suspend_initial; + static constexpr bool is_using_atomics = use_atomic; + + outcome_promise_type() { BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise constructed"); } outcome_promise_type(const outcome_promise_type &) = delete; outcome_promise_type(outcome_promise_type &&) = delete; outcome_promise_type &operator=(const outcome_promise_type &) = delete; @@ -250,23 +322,27 @@ namespace awaitables ~outcome_promise_type() = default; auto get_return_object() { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise returns awaitable"); return Awaitable{*this}; // could throw bad_alloc } void return_void() noexcept { - assert(!result_set.load(std::memory_order_acquire)); + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise returns void"); + BOOST_OUTCOME_ASSERT(!result_set.load(std::memory_order_acquire)); result_set.store(true, std::memory_order_release); } void unhandled_exception() { - assert(!result_set.load(std::memory_order_acquire)); + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise unhandled exception"); + BOOST_OUTCOME_ASSERT(!result_set.load(std::memory_order_acquire)); std::rethrow_exception(std::current_exception()); // throws } auto initial_suspend() noexcept { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise initial suspend = " << suspend_initial); struct awaiter { - bool await_ready() noexcept { return !suspend_initial; } + constexpr bool await_ready() noexcept { return !suspend_initial; } void await_resume() noexcept {} void await_suspend(coroutine_handle<> /*unused*/) noexcept {} }; @@ -276,11 +352,21 @@ namespace awaitables { struct awaiter { - bool await_ready() noexcept { return false; } + // If we don't force a final suspend, promise will get deleted before awaitable + // TODO: Implement detachable awaitables + constexpr bool await_ready() noexcept { return false; } void await_resume() noexcept {} #if BOOST_OUTCOME_HAVE_NOOP_COROUTINE coroutine_handle<> await_suspend(coroutine_handle self) noexcept { + if(self.promise().continuation) + { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will resume coroutine " << self.promise().continuation.address()); + } + else + { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will exit"); + } return self.promise().continuation ? self.promise().continuation : noop_coroutine(); } #else @@ -288,8 +374,13 @@ namespace awaitables { if(self.promise().continuation) { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will resume coroutine " << self.promise().continuation.address()); return self.promise().continuation.resume(); } + else + { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will exit"); + } } #endif }; @@ -324,6 +415,7 @@ namespace awaitables awaitable &operator=(const awaitable &) = delete; ~awaitable() { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " awaitable destructs"); if(_h) { _h.destroy(); @@ -332,12 +424,19 @@ namespace awaitables explicit awaitable(promise_type &p) // could throw : _h(coroutine_handle::from_promise(p)) { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " awaitable constructs for coroutine " << _h.address() + << " shall resume on first suspend = " << promise_type::is_initially_suspended); } bool valid() const noexcept { return _h != nullptr; } - bool await_ready() noexcept { return _h.promise().result_set.load(std::memory_order_acquire); } + bool await_ready() noexcept + { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " await_ready = " << _h.promise().result_set.load(std::memory_order_acquire)); + return _h.promise().result_set.load(std::memory_order_acquire); + } container_type await_resume() { - assert(_h.promise().result_set.load(std::memory_order_acquire)); + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " await_resume"); + BOOST_OUTCOME_ASSERT(_h.promise().result_set.load(std::memory_order_acquire)); if(!_h.promise().result_set.load(std::memory_order_acquire)) { std::terminate(); @@ -347,14 +446,31 @@ namespace awaitables #if BOOST_OUTCOME_HAVE_NOOP_COROUTINE coroutine_handle<> await_suspend(coroutine_handle<> cont) noexcept { - _h.promise().continuation = cont; - return _h; + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " await_suspend suspends coroutine " << cont.address()); + auto &p = _h.promise(); + p.continuation = cont; + bool expected = true; + if(p.pending_first_resumption.compare_exchange_strong(expected, false, std::memory_order_acq_rel, std::memory_order_relaxed)) + { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() + << " await_suspend does one time first resumption of initially suspended coroutine " << _h.address()); + return _h; + } + return noop_coroutine(); } #else void await_suspend(coroutine_handle<> cont) { - _h.promise().continuation = cont; - _h.resume(); + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " await_suspend suspends coroutine " << cont.address()); + auto &p = _h.promise(); + p.continuation = cont; + bool expected = true; + if(p.pending_first_resumption.compare_exchange_strong(expected, false, std::memory_order_acq_rel, std::memory_order_relaxed)) + { + BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() + << " await_suspend does one time first resumption of initially suspended coroutine " << _h.address()); + _h.resume(); + } } #endif }; @@ -396,7 +512,7 @@ namespace awaitables } void return_void() noexcept { - assert(result_set.load(std::memory_order_acquire) >= 0); + BOOST_OUTCOME_ASSERT(result_set.load(std::memory_order_acquire) >= 0); if(result_set.load(std::memory_order_acquire) == 1) { result.~container_type(); // could throw @@ -405,7 +521,7 @@ namespace awaitables } suspend_always yield_value(container_type &&value) { - assert(result_set.load(std::memory_order_acquire) >= 0); + BOOST_OUTCOME_ASSERT(result_set.load(std::memory_order_acquire) >= 0); if(result_set.load(std::memory_order_acquire) == 1) { result.~container_type(); // could throw @@ -416,7 +532,7 @@ namespace awaitables } suspend_always yield_value(const container_type &value) { - assert(result_set.load(std::memory_order_acquire) >= 0); + BOOST_OUTCOME_ASSERT(result_set.load(std::memory_order_acquire) >= 0); if(result_set.load(std::memory_order_acquire) == 1) { result.~container_type(); // could throw @@ -427,7 +543,7 @@ namespace awaitables } void unhandled_exception() { - assert(result_set.load(std::memory_order_acquire) >= 0); + BOOST_OUTCOME_ASSERT(result_set.load(std::memory_order_acquire) >= 0); if(result_set.load(std::memory_order_acquire) == 1) { result.~container_type(); @@ -520,7 +636,7 @@ namespace awaitables { _h(); } - assert(p.result_set.load(std::memory_order_acquire) >= 0); + BOOST_OUTCOME_ASSERT(p.result_set.load(std::memory_order_acquire) >= 0); if(p.result_set.load(std::memory_order_acquire) < 0) { std::terminate(); diff --git a/include/boost/outcome/detail/revision.hpp b/include/boost/outcome/detail/revision.hpp index a88298c39..55c35a801 100644 --- a/include/boost/outcome/detail/revision.hpp +++ b/include/boost/outcome/detail/revision.hpp @@ -1,5 +1,5 @@ /* UPDATED BY SCRIPT -(C) 2017-2023 Niall Douglas (225 commits) +(C) 2017-2025 Niall Douglas (225 commits) Boost Software License - Version 1.0 - August 17th, 2003 @@ -28,6 +28,6 @@ DEALINGS IN THE SOFTWARE. */ // Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time -#define BOOST_OUTCOME_PREVIOUS_COMMIT_REF 46ef3a877785a05b1423848b14549e53f66926ba -#define BOOST_OUTCOME_PREVIOUS_COMMIT_DATE "2022-09-13 17:09:18 +00:00" -#define BOOST_OUTCOME_PREVIOUS_COMMIT_UNIQUE 46ef3a87 +#define BOOST_OUTCOME_PREVIOUS_COMMIT_REF 744da6b7536f2850df972ab01504e3c4d9530149 +#define BOOST_OUTCOME_PREVIOUS_COMMIT_DATE "2025-05-21 12:10:22 +00:00" +#define BOOST_OUTCOME_PREVIOUS_COMMIT_UNIQUE 744da6b7 diff --git a/include/boost/outcome/detail/trait_std_error_code.hpp b/include/boost/outcome/detail/trait_std_error_code.hpp index 9e5825776..1e2373056 100644 --- a/include/boost/outcome/detail/trait_std_error_code.hpp +++ b/include/boost/outcome/detail/trait_std_error_code.hpp @@ -1,5 +1,5 @@ /* Traits for Outcome -(C) 2018-2023 Niall Douglas (6 commits) +(C) 2018-2025 Niall Douglas (6 commits) File Created: March 2018 diff --git a/include/boost/outcome/detail/trait_std_exception.hpp b/include/boost/outcome/detail/trait_std_exception.hpp index 8c083e13a..31c2ea676 100644 --- a/include/boost/outcome/detail/trait_std_exception.hpp +++ b/include/boost/outcome/detail/trait_std_exception.hpp @@ -1,5 +1,5 @@ /* Traits for Outcome -(C) 2018-2023 Niall Douglas (3 commits) +(C) 2018-2025 Niall Douglas (3 commits) File Created: March 2018 diff --git a/include/boost/outcome/detail/try.h b/include/boost/outcome/detail/try.h new file mode 100644 index 000000000..7beed50a5 --- /dev/null +++ b/include/boost/outcome/detail/try.h @@ -0,0 +1,104 @@ +/* Try operation macros +(C) 2017-2025 Niall Douglas (20 commits) +File Created: July 2017 + + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef BOOST_OUTCOME_TRY_H +#define BOOST_OUTCOME_TRY_H + +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wparentheses" +#endif + +#define BOOST_OUTCOME_TRY_GLUE2(x, y) x##y +#define BOOST_OUTCOME_TRY_GLUE(x, y) BOOST_OUTCOME_TRY_GLUE2(x, y) +#define BOOST_OUTCOME_TRY_UNIQUE_NAME BOOST_OUTCOME_TRY_GLUE(_outcome_try_unique_name_temporary, __COUNTER__) + +#define BOOST_OUTCOME_TRY_RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, count, ...) count +#define BOOST_OUTCOME_TRY_EXPAND_ARGS(args) BOOST_OUTCOME_TRY_RETURN_ARG_COUNT args +#define BOOST_OUTCOME_TRY_COUNT_ARGS_MAX8(...) BOOST_OUTCOME_TRY_EXPAND_ARGS((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define BOOST_OUTCOME_TRY_OVERLOAD_MACRO2(name, count) name##count +#define BOOST_OUTCOME_TRY_OVERLOAD_MACRO1(name, count) BOOST_OUTCOME_TRY_OVERLOAD_MACRO2(name, count) +#define BOOST_OUTCOME_TRY_OVERLOAD_MACRO(name, count) BOOST_OUTCOME_TRY_OVERLOAD_MACRO1(name, count) +#define BOOST_OUTCOME_TRY_OVERLOAD_GLUE(x, y) x y +#define BOOST_OUTCOME_TRY_CALL_OVERLOAD(name, ...) \ + BOOST_OUTCOME_TRY_OVERLOAD_GLUE(BOOST_OUTCOME_TRY_OVERLOAD_MACRO(name, BOOST_OUTCOME_TRY_COUNT_ARGS_MAX8(__VA_ARGS__)), (__VA_ARGS__)) + +#define _OUTCOME_TRY_RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, count, ...) count +#define _OUTCOME_TRY_EXPAND_ARGS(args) _OUTCOME_TRY_RETURN_ARG_COUNT args +#define _OUTCOME_TRY_COUNT_ARGS_MAX8(...) _OUTCOME_TRY_EXPAND_ARGS((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define _OUTCOME_TRY_OVERLOAD_MACRO2(name, count) name##count +#define _OUTCOME_TRY_OVERLOAD_MACRO1(name, count) _OUTCOME_TRY_OVERLOAD_MACRO2(name, count) +#define _OUTCOME_TRY_OVERLOAD_MACRO(name, count) _OUTCOME_TRY_OVERLOAD_MACRO1(name, count) +#define _OUTCOME_TRY_OVERLOAD_GLUE(x, y) x y +#define _OUTCOME_TRY_CALL_OVERLOAD(name, ...) \ + _OUTCOME_TRY_OVERLOAD_GLUE(_OUTCOME_TRY_OVERLOAD_MACRO(name, _OUTCOME_TRY_COUNT_ARGS_MAX8(__VA_ARGS__)), (__VA_ARGS__)) + +#if !defined(BOOST_OUTCOME_TRY_LIKELY_IF) && defined(__has_cpp_attribute) +#if __has_cpp_attribute(likely) +#define BOOST_OUTCOME_TRY_LIKELY_IF(...) if(__VA_ARGS__) [[likely]] +#endif +#endif +#ifndef BOOST_OUTCOME_TRY_LIKELY_IF +#if defined(__clang__) || defined(__GNUC__) +#define BOOST_OUTCOME_TRY_LIKELY_IF(...) if(__builtin_expect(!!(__VA_ARGS__), 1)) +#else +#define BOOST_OUTCOME_TRY_LIKELY_IF(...) if(__VA_ARGS__) +#endif +#endif + +#ifdef __cplusplus +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_AUTO(...) auto +#else +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_AUTO(...) __typeof__(__VA_ARGS__) +#endif + +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK(...) __VA_ARGS__ +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE3(unique, ...) BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_AUTO(__VA_ARGS__) unique = (__VA_ARGS__) +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE2(x) x +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE(unique, x, ...) BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE2(BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE3(unique, __VA_ARGS__)) +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED3(unique, x, y, ...) x unique = (__VA_ARGS__) +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED2(x) x +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED(unique, ...) \ + BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED2(BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED3(unique, __VA_ARGS__)) +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE1(...) BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE2(...) BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED +#define BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE(unique, spec, ...) \ + _OUTCOME_TRY_CALL_OVERLOAD(BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE, BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK spec) \ + (unique, BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK spec, __VA_ARGS__) + +#define BOOST_OUTCOME_TRY2_VAR_SECOND2(x, var) var +#define BOOST_OUTCOME_TRY2_VAR_SECOND3(x, y, ...) x y +#define BOOST_OUTCOME_TRY2_VAR(spec) _OUTCOME_TRY_CALL_OVERLOAD(BOOST_OUTCOME_TRY2_VAR_SECOND, BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK spec, spec) + +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif + +#endif diff --git a/include/boost/outcome/detail/value_storage.hpp b/include/boost/outcome/detail/value_storage.hpp index 34b616c60..80c31eae6 100644 --- a/include/boost/outcome/detail/value_storage.hpp +++ b/include/boost/outcome/detail/value_storage.hpp @@ -1,5 +1,5 @@ /* Essentially an internal optional implementation :) -(C) 2017-2023 Niall Douglas (24 commits) +(C) 2017-2025 Niall Douglas (24 commits) File Created: June 2017 @@ -33,8 +33,6 @@ DEALINGS IN THE SOFTWARE. #include "../config.hpp" -#include - BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN namespace detail @@ -56,7 +54,7 @@ namespace detail // But fall back on default construction and move assign if necessary template struct move_assign_to_empty { - move_assign_to_empty(T *dest, T *o) noexcept(std::is_nothrow_default_constructible::value &&std::is_nothrow_move_assignable::value) + move_assign_to_empty(T *dest, T *o) noexcept(std::is_nothrow_default_constructible::value && std::is_nothrow_move_assignable::value) { new(dest) T; *dest = static_cast(*o); @@ -65,19 +63,15 @@ namespace detail // Void does nothing template <> struct move_assign_to_empty { - move_assign_to_empty(void *, void *) noexcept - { /* nothing to assign */ - } + move_assign_to_empty(void *, void *) noexcept { /* nothing to assign */ } }; template <> struct move_assign_to_empty { - move_assign_to_empty(const void *, const void *) noexcept - { /* nothing to assign */ - } + move_assign_to_empty(const void *, const void *) noexcept { /* nothing to assign */ } }; // Helpers for copy assigning to empty storage template ::value, - bool isDefaultConstructibleAndCopyAssignable = std::is_default_constructible::value &&std::is_copy_assignable::value> + bool isDefaultConstructibleAndCopyAssignable = std::is_default_constructible::value && std::is_copy_assignable::value> struct copy_assign_to_empty; // Prefer to use copy construction template struct copy_assign_to_empty @@ -91,7 +85,7 @@ namespace detail // But fall back on default construction and copy assign if necessary template struct copy_assign_to_empty { - copy_assign_to_empty(T *dest, const T *o) noexcept(std::is_nothrow_default_constructible::value &&std::is_nothrow_copy_assignable::value) + copy_assign_to_empty(T *dest, const T *o) noexcept(std::is_nothrow_default_constructible::value && std::is_nothrow_copy_assignable::value) { new(dest) T; *dest = *o; @@ -100,15 +94,11 @@ namespace detail // Void does nothing template <> struct copy_assign_to_empty { - copy_assign_to_empty(void *, void *) noexcept - { /* nothing to assign */ - } + copy_assign_to_empty(void *, void *) noexcept { /* nothing to assign */ } }; template <> struct copy_assign_to_empty { - copy_assign_to_empty(const void *, const void *) noexcept - { /* nothing to assign */ - } + copy_assign_to_empty(const void *, const void *) noexcept { /* nothing to assign */ } }; template struct strong_swap_impl @@ -239,11 +229,12 @@ namespace detail #ifdef _MSC_VER __declspec(noreturn) #elif defined(__GNUC__) || defined(__clang__) - __attribute__((noreturn)) + __attribute__((noreturn)) #endif - void make_ub(T && /*unused*/) + void + make_ub(T && /*unused*/) { - assert(false); // NOLINT + BOOST_OUTCOME_ASSERT(false); // NOLINT #if defined(__GNUC__) || defined(__clang__) __builtin_unreachable(); #elif defined(_MSC_VER) @@ -263,10 +254,10 @@ namespace detail to change the value to one of the enum's values. This is stupid to look at in source code, but it make clang's optimiser do the right thing, so it's worth it. */ -#define BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS 0 enum class status : uint16_t { // WARNING: These bits are not tracked by abi-dumper, but changing them will break ABI! + // bits 0-5 in use. none = 0, have_value = (1U << 0U), @@ -276,6 +267,7 @@ namespace detail // failed to complete a strong swap have_lost_consistency = (1U << 3U), + have_value_lost_consistency = (1U << 0U) | (1U << 3U), have_error_lost_consistency = (1U << 1U) | (1U << 3U), have_exception_lost_consistency = (2U << 1U) | (1U << 3U), @@ -283,6 +275,7 @@ namespace detail // can errno be set from this error? have_error_is_errno = (1U << 4U), + have_error_error_is_errno = (1U << 1U) | (1U << 4U), have_error_exception_error_is_errno = (3U << 1U) | (1U << 4U), @@ -290,7 +283,24 @@ namespace detail have_error_exception_lost_consistency_error_is_errno = (3U << 1U) | (1U << 3U) | (1U << 4U), // value has been moved from - have_moved_from = (1U << 5U) + have_moved_from = (1U << 5U), + + have_value_moved_from = (1U << 0U) | (1U << 5U), + have_error_moved_from = (1U << 1U) | (1U << 5U), + have_exception_moved_from = (2U << 1U) | (1U << 5U), + have_error_exception_moved_from = (3U << 1U) | (1U << 5U), + + have_value_lost_consistency_moved_from = (1U << 0U) | (1U << 3U) | (1U << 5U), + have_error_lost_consistency_moved_from = (1U << 1U) | (1U << 3U) | (1U << 5U), + have_exception_lost_consistency_moved_from = (2U << 1U) | (1U << 3U) | (1U << 5U), + have_error_exception_lost_consistency_moved_from = (3U << 1U) | (1U << 3U) | (1U << 5U), + + have_error_is_errno_moved_from = (1U << 4U) | (1U << 5U), + have_error_error_is_errno_moved_from = (1U << 1U) | (1U << 4U) | (1U << 5U), + have_error_exception_error_is_errno_moved_from = (3U << 1U) | (1U << 4U) | (1U << 5U), + + have_error_lost_consistency_error_is_errno_moved_from = (1U << 1U) | (1U << 3U) | (1U << 4U) | (1U << 5U), + have_error_exception_lost_consistency_error_is_errno_moved_from = (3U << 1U) | (1U << 3U) | (1U << 4U) | (1U << 5U), }; struct status_bitfield_type { @@ -313,523 +323,53 @@ namespace detail constexpr status_bitfield_type &operator=(status_bitfield_type &&) = default; //~status_bitfield_type() = default; // Do NOT uncomment this, it breaks older clangs! - constexpr bool have_value() const noexcept - { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS - return (status_value == status::have_value) // - || (status_value == status::have_value_lost_consistency) // - ; -#else - return (static_cast(status_value) & static_cast(status::have_value)) != 0; -#endif - } - constexpr bool have_error() const noexcept - { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS - return (status_value == status::have_error) // - || (status_value == status::have_error_exception) // - || (status_value == status::have_error_lost_consistency) // - || (status_value == status::have_error_exception_lost_consistency) // - || (status_value == status::have_error_error_is_errno) // - || (status_value == status::have_error_exception_error_is_errno) // - || (status_value == status::have_error_lost_consistency_error_is_errno) // - || (status_value == status::have_error_exception_lost_consistency_error_is_errno) // - ; -#else - return (static_cast(status_value) & static_cast(status::have_error)) != 0; -#endif - } - constexpr bool have_exception() const noexcept - { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS - return (status_value == status::have_exception) // - || (status_value == status::have_error_exception) // - || (status_value == status::have_exception_lost_consistency) // - || (status_value == status::have_error_exception_lost_consistency) // - || (status_value == status::have_error_exception_error_is_errno) // - || (status_value == status::have_error_exception_lost_consistency_error_is_errno) // - ; -#else - return (static_cast(status_value) & static_cast(status::have_exception)) != 0; -#endif - } + constexpr bool have_value() const noexcept { return (static_cast(status_value) & static_cast(status::have_value)) != 0; } + constexpr bool have_error() const noexcept { return (static_cast(status_value) & static_cast(status::have_error)) != 0; } + constexpr bool have_exception() const noexcept { return (static_cast(status_value) & static_cast(status::have_exception)) != 0; } constexpr bool have_lost_consistency() const noexcept { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS - return (status_value == status::have_value_lost_consistency) // - || (status_value == status::have_error_lost_consistency) // - || (status_value == status::have_exception_lost_consistency) // - || (status_value == status::have_error_lost_consistency_error_is_errno) // - || (status_value == status::have_error_exception_lost_consistency_error_is_errno) // - ; -#else return (static_cast(status_value) & static_cast(status::have_lost_consistency)) != 0; -#endif } constexpr bool have_error_is_errno() const noexcept { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS - return (status_value == status::have_error_error_is_errno) // - || (status_value == status::have_error_exception_error_is_errno) // - || (status_value == status::have_error_lost_consistency_error_is_errno) // - || (status_value == status::have_error_exception_lost_consistency_error_is_errno) // - ; -#else return (static_cast(status_value) & static_cast(status::have_error_is_errno)) != 0; -#endif - } - constexpr bool have_moved_from() const noexcept - { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS -#error Fixme -#else - return (static_cast(status_value) & static_cast(status::have_moved_from)) != 0; -#endif } + constexpr bool have_moved_from() const noexcept { return (static_cast(status_value) & static_cast(status::have_moved_from)) != 0; } constexpr status_bitfield_type &set_have_value(bool v) noexcept { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS - switch(status_value) - { - case status::none: - if(v) - { - status_value = status::have_value; - } - break; - case status::have_value: - if(!v) - { - status_value = status::none; - } - break; - case status::have_error: - if(v) - { - make_ub(*this); - } - break; - case status::have_exception: - if(v) - { - make_ub(*this); - } - break; - case status::have_error_exception: - if(v) - { - make_ub(*this); - } - break; - case status::have_value_lost_consistency: - if(!v) - { - status_value = status::none; - } - break; - case status::have_error_lost_consistency: - if(v) - { - make_ub(*this); - } - break; - case status::have_exception_lost_consistency: - if(v) - { - make_ub(*this); - } - break; - case status::have_error_exception_lost_consistency: - if(v) - { - make_ub(*this); - } - break; - case status::have_error_error_is_errno: - if(v) - { - make_ub(*this); - } - break; - case status::have_error_exception_error_is_errno: - if(v) - { - make_ub(*this); - } - break; - case status::have_error_lost_consistency_error_is_errno: - if(v) - { - make_ub(*this); - } - break; - case status::have_error_exception_lost_consistency_error_is_errno: - if(v) - { - make_ub(*this); - } - break; - } -#else status_value = static_cast(v ? (static_cast(status_value) | static_cast(status::have_value)) : (static_cast(status_value) & ~static_cast(status::have_value))); -#endif return *this; } constexpr status_bitfield_type &set_have_error(bool v) noexcept { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS - switch(status_value) - { - case status::none: - if(v) - { - status_value = status::have_error; - } - break; - case status::have_value: - if(v) - { - make_ub(*this); - } - break; - case status::have_error: - if(!v) - { - status_value = status::none; - } - break; - case status::have_exception: - if(v) - { - status_value = status::have_error_exception; - } - break; - case status::have_error_exception: - if(!v) - { - status_value = status::have_exception; - } - break; - case status::have_value_lost_consistency: - if(v) - { - make_ub(*this); - } - break; - case status::have_error_lost_consistency: - if(!v) - { - status_value = status::none; - } - break; - case status::have_exception_lost_consistency: - if(v) - { - status_value = status::have_error_exception_lost_consistency; - } - break; - case status::have_error_exception_lost_consistency: - if(!v) - { - status_value = status::have_exception_lost_consistency; - } - break; - case status::have_error_error_is_errno: - if(!v) - { - status_value = status::none; - } - break; - case status::have_error_exception_error_is_errno: - if(!v) - { - status_value = status::have_exception; - } - break; - case status::have_error_lost_consistency_error_is_errno: - if(!v) - { - status_value = status::none; - } - break; - case status::have_error_exception_lost_consistency_error_is_errno: - if(!v) - { - status_value = status::have_exception_lost_consistency; - } - break; - } -#else status_value = static_cast(v ? (static_cast(status_value) | static_cast(status::have_error)) : (static_cast(status_value) & ~static_cast(status::have_error))); -#endif return *this; } constexpr status_bitfield_type &set_have_exception(bool v) noexcept { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS - switch(status_value) - { - case status::none: - if(v) - { - status_value = status::have_exception; - } - break; - case status::have_value: - if(v) - { - make_ub(*this); - } - break; - case status::have_error: - if(v) - { - status_value = status::have_error_exception; - } - break; - case status::have_exception: - if(!v) - { - status_value = status::none; - } - break; - case status::have_error_exception: - if(!v) - { - status_value = status::have_error; - } - break; - case status::have_value_lost_consistency: - if(v) - { - make_ub(*this); - } - break; - case status::have_error_lost_consistency: - if(v) - { - status_value = status::have_error_exception_lost_consistency; - } - break; - case status::have_exception_lost_consistency: - if(!v) - { - status_value = status::none; - } - break; - case status::have_error_exception_lost_consistency: - if(!v) - { - status_value = status::have_error_lost_consistency; - } - break; - case status::have_error_error_is_errno: - if(v) - { - status_value = status::have_error_exception_error_is_errno; - } - break; - case status::have_error_exception_error_is_errno: - if(!v) - { - status_value = status::have_error_error_is_errno; - } - break; - case status::have_error_lost_consistency_error_is_errno: - if(v) - { - status_value = status::have_error_exception_lost_consistency_error_is_errno; - } - break; - case status::have_error_exception_lost_consistency_error_is_errno: - if(!v) - { - status_value = status::have_error_lost_consistency_error_is_errno; - } - break; - } -#else status_value = static_cast(v ? (static_cast(status_value) | static_cast(status::have_exception)) : (static_cast(status_value) & ~static_cast(status::have_exception))); -#endif return *this; } constexpr status_bitfield_type &set_have_error_is_errno(bool v) noexcept { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS - switch(status_value) - { - case status::none: - make_ub(*this); - break; - case status::have_value: - make_ub(*this); - break; - case status::have_error: - if(v) - { - status_value = status::have_error_error_is_errno; - } - break; - case status::have_exception: - make_ub(*this); - break; - case status::have_error_exception: - if(v) - { - status_value = status::have_error_exception_error_is_errno; - } - break; - case status::have_value_lost_consistency: - make_ub(*this); - break; - case status::have_error_lost_consistency: - if(v) - { - status_value = status::have_error_lost_consistency_error_is_errno; - } - break; - case status::have_exception_lost_consistency: - make_ub(*this); - break; - case status::have_error_exception_lost_consistency: - if(v) - { - status_value = status::have_error_exception_lost_consistency_error_is_errno; - } - break; - case status::have_error_error_is_errno: - if(!v) - { - status_value = status::have_error; - } - break; - case status::have_error_exception_error_is_errno: - if(!v) - { - status_value = status::have_error_exception; - } - break; - case status::have_error_lost_consistency_error_is_errno: - if(!v) - { - status_value = status::have_error_lost_consistency; - } - break; - case status::have_error_exception_lost_consistency_error_is_errno: - if(!v) - { - status_value = status::have_error_exception_lost_consistency; - } - break; - } -#else status_value = static_cast(v ? (static_cast(status_value) | static_cast(status::have_error_is_errno)) : (static_cast(status_value) & ~static_cast(status::have_error_is_errno))); -#endif return *this; } constexpr status_bitfield_type &set_have_lost_consistency(bool v) noexcept { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS - switch(status_value) - { - case status::none: - if(v) - { - make_ub(*this); - } - break; - case status::have_value: - if(v) - { - status_value = status::have_value_lost_consistency; - } - break; - case status::have_error: - if(v) - { - status_value = status::have_error_lost_consistency; - } - break; - case status::have_exception: - if(v) - { - status_value = status::have_exception_lost_consistency; - } - break; - case status::have_error_exception: - if(v) - { - status_value = status::have_error_exception_lost_consistency; - } - break; - case status::have_value_lost_consistency: - if(!v) - { - status_value = status::have_value; - } - break; - case status::have_error_lost_consistency: - if(!v) - { - status_value = status::have_error; - } - break; - case status::have_exception_lost_consistency: - if(!v) - { - status_value = status::have_exception; - } - break; - case status::have_error_exception_lost_consistency: - if(!v) - { - status_value = status::have_error_exception; - } - break; - case status::have_error_error_is_errno: - if(v) - { - status_value = status::have_error_lost_consistency_error_is_errno; - } - break; - case status::have_error_exception_error_is_errno: - if(v) - { - status_value = status::have_error_exception_lost_consistency_error_is_errno; - } - break; - case status::have_error_lost_consistency_error_is_errno: - if(!v) - { - status_value = status::have_error_exception_error_is_errno; - } - break; - case status::have_error_exception_lost_consistency_error_is_errno: - if(!v) - { - status_value = status::have_error_exception_error_is_errno; - } - break; - } -#else status_value = static_cast(v ? (static_cast(status_value) | static_cast(status::have_lost_consistency)) : (static_cast(status_value) & ~static_cast(status::have_lost_consistency))); -#endif return *this; } constexpr status_bitfield_type &set_have_moved_from(bool v) noexcept { -#if BOOST_OUTCOME_USE_CONSTEXPR_ENUM_STATUS -#error Fixme -#else status_value = static_cast(v ? (static_cast(status_value) | static_cast(status::have_moved_from)) : (static_cast(status_value) & ~static_cast(status::have_moved_from))); -#endif return *this; } }; @@ -933,8 +473,9 @@ namespace detail && detail::is_constructible && detail::is_constructible; BOOST_OUTCOME_TEMPLATE(class U, class V) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_nonvoid_converting_constructor)) - constexpr explicit value_storage_trivial(const value_storage_trivial &o, nonvoid_converting_constructor_tag /*unused*/ = {}) noexcept( - detail::is_nothrow_constructible<_value_type_, U> &&detail::is_nothrow_constructible<_error_type_, V>) + constexpr explicit value_storage_trivial(const value_storage_trivial &o, + nonvoid_converting_constructor_tag /*unused*/ = {}) noexcept(detail::is_nothrow_constructible<_value_type_, U> && + detail::is_nothrow_constructible<_error_type_, V>) : value_storage_trivial(o._status.have_value() ? value_storage_trivial(in_place_type, o._value) : (o._status.have_error() ? value_storage_trivial(in_place_type, o._error) : value_storage_trivial())) // NOLINT @@ -943,8 +484,9 @@ namespace detail } BOOST_OUTCOME_TEMPLATE(class U, class V) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_nonvoid_converting_constructor)) - constexpr explicit value_storage_trivial(value_storage_trivial &&o, nonvoid_converting_constructor_tag /*unused*/ = {}) noexcept( - detail::is_nothrow_constructible<_value_type_, U> &&detail::is_nothrow_constructible<_error_type_, V>) + constexpr explicit value_storage_trivial(value_storage_trivial &&o, + nonvoid_converting_constructor_tag /*unused*/ = {}) noexcept(detail::is_nothrow_constructible<_value_type_, U> && + detail::is_nothrow_constructible<_error_type_, V>) : value_storage_trivial( o._status.have_value() ? value_storage_trivial(in_place_type, static_cast(o._value)) : @@ -957,11 +499,12 @@ namespace detail { }; template - static constexpr bool enable_void_value_converting_constructor = std::is_default_constructible::value &&detail::is_constructible; + static constexpr bool enable_void_value_converting_constructor = + std::is_default_constructible::value && detail::is_constructible; BOOST_OUTCOME_TEMPLATE(class V) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_void_value_converting_constructor)) constexpr explicit value_storage_trivial(const value_storage_trivial &o, void_value_converting_constructor_tag /*unused*/ = {}) noexcept( - std::is_nothrow_default_constructible<_value_type_>::value &&detail::is_nothrow_constructible<_error_type_, V>) + std::is_nothrow_default_constructible<_value_type_>::value && detail::is_nothrow_constructible<_error_type_, V>) : value_storage_trivial(o._status.have_value() ? value_storage_trivial(in_place_type) : (o._status.have_error() ? value_storage_trivial(in_place_type, o._error) : value_storage_trivial())) // NOLINT @@ -971,7 +514,7 @@ namespace detail BOOST_OUTCOME_TEMPLATE(class V) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_void_value_converting_constructor)) constexpr explicit value_storage_trivial(value_storage_trivial &&o, void_value_converting_constructor_tag /*unused*/ = {}) noexcept( - std::is_nothrow_default_constructible<_value_type_>::value &&detail::is_nothrow_constructible<_error_type_, V>) + std::is_nothrow_default_constructible<_value_type_>::value && detail::is_nothrow_constructible<_error_type_, V>) : value_storage_trivial( o._status.have_value() ? value_storage_trivial(in_place_type) : @@ -984,11 +527,12 @@ namespace detail { }; template - static constexpr bool enable_void_error_converting_constructor = std::is_default_constructible::value &&detail::is_constructible; + static constexpr bool enable_void_error_converting_constructor = + std::is_default_constructible::value && detail::is_constructible; BOOST_OUTCOME_TEMPLATE(class U) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_void_error_converting_constructor)) constexpr explicit value_storage_trivial(const value_storage_trivial &o, void_error_converting_constructor_tag /*unused*/ = {}) noexcept( - detail::is_nothrow_constructible<_value_type_, U> &&std::is_nothrow_default_constructible<_error_type_>::value) + detail::is_nothrow_constructible<_value_type_, U> && std::is_nothrow_default_constructible<_error_type_>::value) : value_storage_trivial(o._status.have_value() ? value_storage_trivial(in_place_type, o._value) : (o._status.have_error() ? value_storage_trivial(in_place_type) : value_storage_trivial())) // NOLINT @@ -998,7 +542,7 @@ namespace detail BOOST_OUTCOME_TEMPLATE(class U) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_void_error_converting_constructor)) constexpr explicit value_storage_trivial(value_storage_trivial &&o, void_error_converting_constructor_tag /*unused*/ = {}) noexcept( - detail::is_nothrow_constructible<_value_type_, U> &&std::is_nothrow_default_constructible<_error_type_>::value) + detail::is_nothrow_constructible<_value_type_, U> && std::is_nothrow_default_constructible<_error_type_>::value) : value_storage_trivial(o._status.have_value() ? value_storage_trivial(in_place_type, static_cast(o._value)) : (o._status.have_error() ? value_storage_trivial(in_place_type) : value_storage_trivial())) // NOLINT @@ -1056,16 +600,16 @@ namespace detail #if __cplusplus >= 202000L || _HAS_CXX20 constexpr #endif - value_storage_nontrivial(value_storage_nontrivial &&o) noexcept( - std::is_nothrow_move_constructible<_value_type_>::value &&std::is_nothrow_move_constructible<_error_type_>::value) // NOLINT + value_storage_nontrivial(value_storage_nontrivial &&o) noexcept(std::is_nothrow_move_constructible<_value_type_>::value && + std::is_nothrow_move_constructible<_error_type_>::value) // NOLINT { if(o._status.have_value()) { - new(&_value) _value_type_(static_cast<_value_type_ &&>(o._value)); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_value)) _value_type_(static_cast<_value_type_ &&>(o._value)); // NOLINT } else if(o._status.have_error()) { - new(&_error) _error_type_(static_cast<_error_type_ &&>(o._error)); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_error)) _error_type_(static_cast<_error_type_ &&>(o._error)); // NOLINT } _status = o._status; o._status.set_have_moved_from(true); @@ -1073,16 +617,16 @@ namespace detail #if __cplusplus >= 202000L || _HAS_CXX20 constexpr #endif - value_storage_nontrivial(const value_storage_nontrivial &o) noexcept( - std::is_nothrow_copy_constructible<_value_type_>::value &&std::is_nothrow_copy_constructible<_error_type_>::value) + value_storage_nontrivial(const value_storage_nontrivial &o) noexcept(std::is_nothrow_copy_constructible<_value_type_>::value && + std::is_nothrow_copy_constructible<_error_type_>::value) { if(o._status.have_value()) { - new(&_value) _value_type_(o._value); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_value)) _value_type_(o._value); // NOLINT } else if(o._status.have_error()) { - new(&_error) _error_type_(o._error); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_error)) _error_type_(o._error); // NOLINT } _status = o._status; } @@ -1136,7 +680,7 @@ namespace detail BOOST_OUTCOME_TEMPLATE(class U, class V) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_nonvoid_converting_constructor)) constexpr explicit value_storage_nontrivial(const value_storage_trivial &o, nonvoid_converting_constructor_tag /*unused*/ = {}) noexcept( - detail::is_nothrow_constructible<_value_type_, U> &&detail::is_nothrow_constructible<_error_type_, V>) + detail::is_nothrow_constructible<_value_type_, U> && detail::is_nothrow_constructible<_error_type_, V>) : value_storage_nontrivial(o._status.have_value() ? value_storage_nontrivial(in_place_type, o._value) : (o._status.have_error() ? value_storage_nontrivial(in_place_type, o._error) : value_storage_nontrivial())) @@ -1146,7 +690,7 @@ namespace detail BOOST_OUTCOME_TEMPLATE(class U, class V) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_nonvoid_converting_constructor)) constexpr explicit value_storage_nontrivial(value_storage_trivial &&o, nonvoid_converting_constructor_tag /*unused*/ = {}) noexcept( - detail::is_nothrow_constructible<_value_type_, U> &&detail::is_nothrow_constructible<_error_type_, V>) + detail::is_nothrow_constructible<_value_type_, U> && detail::is_nothrow_constructible<_error_type_, V>) : value_storage_nontrivial( o._status.have_value() ? value_storage_nontrivial(in_place_type, static_cast(o._value)) : @@ -1157,7 +701,7 @@ namespace detail BOOST_OUTCOME_TEMPLATE(class U, class V) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_nonvoid_converting_constructor)) constexpr explicit value_storage_nontrivial(const value_storage_nontrivial &o, nonvoid_converting_constructor_tag /*unused*/ = {}) noexcept( - detail::is_nothrow_constructible<_value_type_, U> &&detail::is_nothrow_constructible<_error_type_, V>) + detail::is_nothrow_constructible<_value_type_, U> && detail::is_nothrow_constructible<_error_type_, V>) : value_storage_nontrivial(o._status.have_value() ? value_storage_nontrivial(in_place_type, o._value) : (o._status.have_error() ? value_storage_nontrivial(in_place_type, o._error) : value_storage_nontrivial())) @@ -1167,7 +711,7 @@ namespace detail BOOST_OUTCOME_TEMPLATE(class U, class V) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_nonvoid_converting_constructor)) constexpr explicit value_storage_nontrivial(value_storage_nontrivial &&o, nonvoid_converting_constructor_tag /*unused*/ = {}) noexcept( - detail::is_nothrow_constructible<_value_type_, U> &&detail::is_nothrow_constructible<_error_type_, V>) + detail::is_nothrow_constructible<_value_type_, U> && detail::is_nothrow_constructible<_error_type_, V>) : value_storage_nontrivial( o._status.have_value() ? value_storage_nontrivial(in_place_type, static_cast(o._value)) : @@ -1180,34 +724,35 @@ namespace detail { }; template - static constexpr bool enable_void_value_converting_constructor = std::is_default_constructible::value &&detail::is_constructible; + static constexpr bool enable_void_value_converting_constructor = + std::is_default_constructible::value && detail::is_constructible; BOOST_OUTCOME_TEMPLATE(class V) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_void_value_converting_constructor)) constexpr explicit value_storage_nontrivial(const value_storage_trivial &o, void_value_converting_constructor_tag /*unused*/ = {}) noexcept( - std::is_nothrow_default_constructible<_value_type_>::value &&detail::is_nothrow_constructible<_error_type_, V>) + std::is_nothrow_default_constructible<_value_type_>::value && detail::is_nothrow_constructible<_error_type_, V>) { if(o._status.have_value()) { - new(&_value) _value_type_(); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_value)) _value_type_(); // NOLINT } else if(o._status.have_error()) { - new(&_error) _error_type_(o._error); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_error)) _error_type_(o._error); // NOLINT } _status = o._status; } BOOST_OUTCOME_TEMPLATE(class V) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_void_value_converting_constructor)) constexpr explicit value_storage_nontrivial(value_storage_trivial &&o, void_value_converting_constructor_tag /*unused*/ = {}) noexcept( - std::is_nothrow_default_constructible<_value_type_>::value &&detail::is_nothrow_constructible<_error_type_, V>) + std::is_nothrow_default_constructible<_value_type_>::value && detail::is_nothrow_constructible<_error_type_, V>) { if(o._status.have_value()) { - new(&_value) _value_type_(); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_value)) _value_type_(); // NOLINT } else if(o._status.have_error()) { - new(&_error) _error_type_(static_cast<_error_type_ &&>(o._error)); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_error)) _error_type_(static_cast<_error_type_ &&>(o._error)); // NOLINT } _status = o._status; o._status.set_have_moved_from(true); @@ -1217,34 +762,35 @@ namespace detail { }; template - static constexpr bool enable_void_error_converting_constructor = std::is_default_constructible::value &&detail::is_constructible; + static constexpr bool enable_void_error_converting_constructor = + std::is_default_constructible::value && detail::is_constructible; BOOST_OUTCOME_TEMPLATE(class U) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_void_error_converting_constructor)) constexpr explicit value_storage_nontrivial(const value_storage_trivial &o, void_error_converting_constructor_tag /*unused*/ = {}) noexcept( - detail::is_nothrow_constructible<_value_type_, U> &&std::is_nothrow_default_constructible<_error_type_>::value) + detail::is_nothrow_constructible<_value_type_, U> && std::is_nothrow_default_constructible<_error_type_>::value) { if(o._status.have_value()) { - new(&_value) _value_type_(o._value); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_value)) _value_type_(o._value); // NOLINT } else if(o._status.have_error()) { - new(&_error) _error_type_(); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_error)) _error_type_(); // NOLINT } _status = o._status; } BOOST_OUTCOME_TEMPLATE(class U) BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_void_error_converting_constructor)) constexpr explicit value_storage_nontrivial(value_storage_trivial &&o, void_error_converting_constructor_tag /*unused*/ = {}) noexcept( - detail::is_nothrow_constructible<_value_type_, U> &&std::is_nothrow_default_constructible<_error_type_>::value) + detail::is_nothrow_constructible<_value_type_, U> && std::is_nothrow_default_constructible<_error_type_>::value) { if(o._status.have_value()) { - new(&_value) _value_type_(static_cast<_value_type_ &&>(o._value)); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_value)) _value_type_(static_cast<_value_type_ &&>(o._value)); // NOLINT } else if(o._status.have_error()) { - new(&_error) _error_type_(); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_error)) _error_type_(); // NOLINT } _status = o._status; o._status.set_have_moved_from(true); @@ -1253,7 +799,7 @@ namespace detail #if __cplusplus >= 202000L || _HAS_CXX20 constexpr #endif - ~value_storage_nontrivial() noexcept(std::is_nothrow_destructible<_value_type_>::value &&std::is_nothrow_destructible<_error_type_>::value) + ~value_storage_nontrivial() noexcept(std::is_nothrow_destructible<_value_type_>::value && std::is_nothrow_destructible<_error_type_>::value) { if(this->_status.have_value()) { @@ -1276,7 +822,7 @@ namespace detail constexpr #endif void - swap(value_storage_nontrivial &o) noexcept(detail::is_nothrow_swappable<_value_type_>::value &&detail::is_nothrow_swappable<_error_type_>::value) + swap(value_storage_nontrivial &o) noexcept(detail::is_nothrow_swappable<_value_type_>::value && detail::is_nothrow_swappable<_error_type_>::value) { using std::swap; // empty/empty @@ -1288,42 +834,42 @@ namespace detail // value/value if(_status.have_value() && o._status.have_value()) { - struct _ + struct some_type { status_bitfield_type &a, &b; bool all_good{false}; - ~_() + ~some_type() { - if(!all_good) + if(!this->all_good) { // We lost one of the values - a.set_have_lost_consistency(true); - b.set_have_lost_consistency(true); + this->a.set_have_lost_consistency(true); + this->b.set_have_lost_consistency(true); } } - } _{_status, o._status}; - strong_swap(_.all_good, _value, o._value); + } some_type_value{_status, o._status}; + strong_swap(some_type_value.all_good, _value, o._value); swap(_status, o._status); return; } // error/error if(_status.have_error() && o._status.have_error()) { - struct _ + struct some_type { status_bitfield_type &a, &b; bool all_good{false}; - ~_() + ~some_type() { - if(!all_good) + if(!this->all_good) { // We lost one of the values - a.set_have_lost_consistency(true); - b.set_have_lost_consistency(true); + this->a.set_have_lost_consistency(true); + this->b.set_have_lost_consistency(true); } } - } _{_status, o._status}; - strong_swap(_.all_good, _error, o._error); + } some_type_value{_status, o._status}; + strong_swap(some_type_value.all_good, _error, o._error); swap(_status, o._status); return; } @@ -1331,7 +877,7 @@ namespace detail if(_status.have_value() && !o._status.have_error()) { // Move construct me into other - new(&o._value) _value_type_(static_cast<_value_type_ &&>(_value)); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(o._value)) _value_type_(static_cast<_value_type_ &&>(_value)); // NOLINT if(!trait::is_move_bitcopying::value) { this->_value.~value_type(); // NOLINT @@ -1342,7 +888,7 @@ namespace detail if(o._status.have_value() && !_status.have_error()) { // Move construct other into me - new(&_value) _value_type_(static_cast<_value_type_ &&>(o._value)); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_value)) _value_type_(static_cast<_value_type_ &&>(o._value)); // NOLINT if(!trait::is_move_bitcopying::value) { o._value.~value_type(); // NOLINT @@ -1353,7 +899,7 @@ namespace detail if(_status.have_error() && !o._status.have_value()) { // Move construct me into other - new(&o._error) _error_type_(static_cast<_error_type_ &&>(_error)); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(o._error)) _error_type_(static_cast<_error_type_ &&>(_error)); // NOLINT if(!trait::is_move_bitcopying::value) { this->_error.~error_type(); // NOLINT @@ -1364,7 +910,7 @@ namespace detail if(o._status.have_error() && !_status.have_value()) { // Move construct other into me - new(&_error) _error_type_(static_cast<_error_type_ &&>(o._error)); // NOLINT + new(BOOST_OUTCOME_ADDRESS_OF(_error)) _error_type_(static_cast<_error_type_ &&>(o._error)); // NOLINT if(!trait::is_move_bitcopying::value) { o._error.~error_type(); // NOLINT @@ -1373,36 +919,36 @@ namespace detail return; } // It can now only be value/error, or error/value - struct _ + struct some_type { status_bitfield_type &a, &b; _value_type_ *value, *o_value; _error_type_ *error, *o_error; bool all_good{true}; - ~_() + ~some_type() { - if(!all_good) + if(!this->all_good) { // We lost one of the values - a.set_have_lost_consistency(true); - b.set_have_lost_consistency(true); + this->a.set_have_lost_consistency(true); + this->b.set_have_lost_consistency(true); } } - } _{_status, o._status, &_value, &o._value, &_error, &o._error}; + } some_type_value{_status, o._status, BOOST_OUTCOME_ADDRESS_OF(_value), BOOST_OUTCOME_ADDRESS_OF(o._value), BOOST_OUTCOME_ADDRESS_OF(_error), BOOST_OUTCOME_ADDRESS_OF(o._error)}; if(_status.have_value() && o._status.have_error()) { - strong_placement(_.all_good, _.o_value, _.value, [&_] { // - strong_placement(_.all_good, _.error, _.o_error, [&_] { // - swap(_.a, _.b); // + strong_placement(some_type_value.all_good, some_type_value.o_value, some_type_value.value, [&some_type_value] { // + strong_placement(some_type_value.all_good, some_type_value.error, some_type_value.o_error, [&some_type_value] { // + swap(some_type_value.a, some_type_value.b); // }); }); return; } if(_status.have_error() && o._status.have_value()) { - strong_placement(_.all_good, _.o_error, _.error, [&_] { // - strong_placement(_.all_good, _.value, _.o_value, [&_] { // - swap(_.a, _.b); // + strong_placement(some_type_value.all_good, some_type_value.o_error, some_type_value.error, [&some_type_value] { // + strong_placement(some_type_value.all_good, some_type_value.value, some_type_value.o_value, [&some_type_value] { // + swap(some_type_value.a, some_type_value.b); // }); }); return; @@ -1473,11 +1019,12 @@ namespace detail constexpr #endif value_storage_nontrivial_move_assignment & - operator=(value_storage_nontrivial_move_assignment &&o) noexcept( - std::is_nothrow_move_assignable::value &&std::is_nothrow_move_assignable::value &&noexcept(move_assign_to_empty( - static_cast(nullptr), - static_cast(nullptr))) &&noexcept(move_assign_to_empty(static_cast(nullptr), - static_cast(nullptr)))) // NOLINT + operator=(value_storage_nontrivial_move_assignment &&o) noexcept(std::is_nothrow_move_assignable::value && + std::is_nothrow_move_assignable::value && + noexcept(move_assign_to_empty(static_cast(nullptr), + static_cast(nullptr))) && + noexcept(move_assign_to_empty(static_cast(nullptr), + static_cast(nullptr)))) // NOLINT { using _value_type_ = typename Base::_value_type_; using _error_type_ = typename Base::_error_type_; @@ -1513,7 +1060,7 @@ namespace detail } if(!this->_status.have_value() && !this->_status.have_error() && o._status.have_value()) { - move_assign_to_empty<_value_type_>(&this->_value, &o._value); + move_assign_to_empty<_value_type_>(BOOST_OUTCOME_ADDRESS_OF(this->_value), BOOST_OUTCOME_ADDRESS_OF(o._value)); this->_status = o._status; o._status.set_have_moved_from(true); return *this; @@ -1530,7 +1077,7 @@ namespace detail } if(!this->_status.have_value() && !this->_status.have_error() && o._status.have_error()) { - move_assign_to_empty<_error_type_>(&this->_error, &o._error); + move_assign_to_empty<_error_type_>(BOOST_OUTCOME_ADDRESS_OF(this->_error), BOOST_OUTCOME_ADDRESS_OF(o._error)); this->_status = o._status; o._status.set_have_moved_from(true); return *this; @@ -1541,7 +1088,7 @@ namespace detail { this->_value.~_value_type_(); // NOLINT } - move_assign_to_empty<_error_type_>(&this->_error, &o._error); + move_assign_to_empty<_error_type_>(BOOST_OUTCOME_ADDRESS_OF(this->_error), BOOST_OUTCOME_ADDRESS_OF(o._error)); this->_status = o._status; o._status.set_have_moved_from(true); return *this; @@ -1552,7 +1099,7 @@ namespace detail { this->_error.~_error_type_(); // NOLINT } - move_assign_to_empty<_value_type_>(&this->_value, &o._value); + move_assign_to_empty<_value_type_>(BOOST_OUTCOME_ADDRESS_OF(this->_value), BOOST_OUTCOME_ADDRESS_OF(o._value)); this->_status = o._status; o._status.set_have_moved_from(true); return *this; @@ -1576,9 +1123,9 @@ namespace detail #endif value_storage_nontrivial_copy_assignment & operator=(const value_storage_nontrivial_copy_assignment &o) noexcept( - std::is_nothrow_copy_assignable::value &&std::is_nothrow_copy_assignable::value &&noexcept(copy_assign_to_empty( - static_cast(nullptr), static_cast(nullptr))) &&noexcept(copy_assign_to_empty(static_cast(nullptr), - static_cast(nullptr)))) + std::is_nothrow_copy_assignable::value && std::is_nothrow_copy_assignable::value && + noexcept(copy_assign_to_empty(static_cast(nullptr), static_cast(nullptr))) && + noexcept(copy_assign_to_empty(static_cast(nullptr), static_cast(nullptr)))) { using _value_type_ = typename Base::_value_type_; using _error_type_ = typename Base::_error_type_; @@ -1610,7 +1157,7 @@ namespace detail } if(!this->_status.have_value() && !this->_status.have_error() && o._status.have_value()) { - copy_assign_to_empty<_value_type_>(&this->_value, &o._value); + copy_assign_to_empty<_value_type_>(BOOST_OUTCOME_ADDRESS_OF(this->_value), BOOST_OUTCOME_ADDRESS_OF(o._value)); this->_status = o._status; return *this; } @@ -1625,7 +1172,7 @@ namespace detail } if(!this->_status.have_value() && !this->_status.have_error() && o._status.have_error()) { - copy_assign_to_empty<_error_type_>(&this->_error, &o._error); + copy_assign_to_empty<_error_type_>(BOOST_OUTCOME_ADDRESS_OF(this->_error), BOOST_OUTCOME_ADDRESS_OF(o._error)); this->_status = o._status; return *this; } @@ -1635,7 +1182,7 @@ namespace detail { this->_value.~_value_type_(); // NOLINT } - copy_assign_to_empty<_error_type_>(&this->_error, &o._error); + copy_assign_to_empty<_error_type_>(BOOST_OUTCOME_ADDRESS_OF(this->_error), BOOST_OUTCOME_ADDRESS_OF(o._error)); this->_status = o._status; return *this; } @@ -1645,7 +1192,7 @@ namespace detail { this->_error.~_error_type_(); // NOLINT } - copy_assign_to_empty<_value_type_>(&this->_value, &o._value); + copy_assign_to_empty<_value_type_>(BOOST_OUTCOME_ADDRESS_OF(this->_value), BOOST_OUTCOME_ADDRESS_OF(o._value)); this->_status = o._status; return *this; } @@ -1711,8 +1258,8 @@ namespace detail #ifndef NDEBUG // Check is trivial in all ways except default constructibility // static_assert(std::is_trivial>::value, "value_storage_select_impl is not trivial!"); - // static_assert(std::is_trivially_default_constructible>::value, "value_storage_select_impl is not trivially - // default constructible!"); + // static_assert(std::is_trivially_default_constructible>::value, "value_storage_select_impl is not + // trivially default constructible!"); static_assert(std::is_trivially_copyable>::value, "value_storage_select_impl is not trivially copyable!"); static_assert(std::is_trivially_assignable, value_storage_select_impl>::value, "value_storage_select_impl is not trivially assignable!"); diff --git a/include/boost/outcome/detail/version.hpp b/include/boost/outcome/detail/version.hpp index c428ac565..7f5de74f1 100644 --- a/include/boost/outcome/detail/version.hpp +++ b/include/boost/outcome/detail/version.hpp @@ -1,5 +1,5 @@ /* Sets Outcome version -(C) 2017-2023 Niall Douglas (4 commits) +(C) 2017-2025 Niall Douglas (4 commits) Boost Software License - Version 1.0 - August 17th, 2003 @@ -28,11 +28,11 @@ DEALINGS IN THE SOFTWARE. */ /*! AWAITING HUGO JSON CONVERSION TOOL */ -#define BOOST_OUTCOME_VERSION_MAJOR 2 +#define BOOST_OUTCOME_VERSION_MAJOR 2 /*! AWAITING HUGO JSON CONVERSION TOOL */ -#define BOOST_OUTCOME_VERSION_MINOR 2 +#define BOOST_OUTCOME_VERSION_MINOR 2 /*! AWAITING HUGO JSON CONVERSION TOOL */ -#define BOOST_OUTCOME_VERSION_PATCH 3 +#define BOOST_OUTCOME_VERSION_PATCH 9 /*! AWAITING HUGO JSON CONVERSION TOOL */ #define BOOST_OUTCOME_VERSION_REVISION 0 // Revision version for cmake and DLL version stamping diff --git a/include/boost/outcome/experimental/coroutine_support.hpp b/include/boost/outcome/experimental/coroutine_support.hpp index cec3cee90..41b4b126d 100644 --- a/include/boost/outcome/experimental/coroutine_support.hpp +++ b/include/boost/outcome/experimental/coroutine_support.hpp @@ -1,5 +1,5 @@ /* Tells C++ coroutines about Outcome's result -(C) 2019-2023 Niall Douglas (12 commits) +(C) 2019-2025 Niall Douglas (12 commits) File Created: Oct 2019 @@ -51,8 +51,8 @@ DEALINGS IN THE SOFTWARE. BOOST_OUTCOME_V2_NAMESPACE_END #ifndef BOOST_NO_EXCEPTIONS -#if !BOOST_OUTCOME_USE_SYSTEM_STATUS_CODE && __has_include("status-code/status-code/system_code_from_exception.hpp") -#include "status-code/status-code/system_code_from_exception.hpp" +#if !BOOST_OUTCOME_USE_SYSTEM_STATUS_CODE && __has_include("status-code/system_code_from_exception.hpp") +#include "status-code/system_code_from_exception.hpp" #else #include #endif diff --git a/include/boost/outcome/experimental/result.h b/include/boost/outcome/experimental/result.h index b5b5f6705..c46fbd6ec 100644 --- a/include/boost/outcome/experimental/result.h +++ b/include/boost/outcome/experimental/result.h @@ -1,5 +1,5 @@ /* C interface for result -(C) 2017-2023 Niall Douglas (6 commits) +(C) 2017-2025 Niall Douglas (6 commits) File Created: Aug 2017 @@ -31,8 +31,66 @@ DEALINGS IN THE SOFTWARE. #ifndef BOOST_OUTCOME_EXPERIMENTAL_RESULT_H #define BOOST_OUTCOME_EXPERIMENTAL_RESULT_H +#include +#include // for size_t #include // for intptr_t +#include "../detail/try.h" + +#ifndef BOOST_OUTCOME_C_WEAK +#ifdef _MSC_VER +#define BOOST_OUTCOME_C_WEAK inline +#else +#define BOOST_OUTCOME_C_WEAK __attribute__((weak)) +#endif +#endif + +#ifndef BOOST_OUTCOME_C_MSVC_FORCE_EMIT +#ifdef _MSC_VER +#ifdef __cplusplus +#define BOOST_OUTCOME_C_MSVC_FORCE_EMIT(x) extern "C" __declspec(selectany) void *x##_emit = x; +#else +#define BOOST_OUTCOME_C_MSVC_FORCE_EMIT(x) extern __declspec(selectany) void *x##_emit = x; +#endif +#else +#define BOOST_OUTCOME_C_MSVC_FORCE_EMIT(x) +#endif +#endif + + +#ifndef BOOST_OUTCOME_C_NODISCARD +#if __STDC_VERSION__ >= 202000L || __cplusplus >= 201700L +#define BOOST_OUTCOME_C_NODISCARD [[nodiscard]] +#ifdef __cplusplus +#define BOOST_OUTCOME_C_NODISCARD_EXTERN_C extern "C" [[nodiscard]] +#else +#define BOOST_OUTCOME_C_NODISCARD_EXTERN_C [[nodiscard]] extern +#endif +#elif defined(__GNUC__) || defined(__clang__) +#define BOOST_OUTCOME_C_NODISCARD __attribute__((warn_unused_result)) +#ifdef __cplusplus +#define BOOST_OUTCOME_C_NODISCARD_EXTERN_C extern "C" __attribute__((warn_unused_result)) +#else +#define BOOST_OUTCOME_C_NODISCARD_EXTERN_C extern __attribute__((warn_unused_result)) +#endif +#else +#define BOOST_OUTCOME_C_NODISCARD +#define BOOST_OUTCOME_C_NODISCARD_EXTERN_C extern +#endif +#endif + +#ifndef BOOST_OUTCOME_C_INLINE +#if __STDC_VERSION__ >= 199900L || __cplusplus > 0 +#define BOOST_OUTCOME_C_INLINE inline +#elif defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) +#define BOOST_OUTCOME_C_INLINE __inline +#else +#define BOOST_OUTCOME_C_INLINE +#endif +#endif + +#include "../outcome_gdb.h" + #ifdef __cplusplus extern "C" { @@ -58,7 +116,6 @@ extern "C" #define BOOST_OUTCOME_C_RESULT_ERROR_IS_ERRNO(r) (((r).flags & (1U << 4U)) == (1U << 4U)) - /***************************** support ******************************/ #define BOOST_OUTCOME_C_DECLARE_STATUS_CODE(ident, value_type) \ @@ -70,16 +127,95 @@ extern "C" #define BOOST_OUTCOME_C_STATUS_CODE(ident) struct cxx_status_code_##ident + extern BOOST_OUTCOME_C_WEAK void outcome_make_result_status_code_failure_posix(void *out, size_t bytes, size_t offset, int errcode); + extern BOOST_OUTCOME_C_WEAK void outcome_make_result_status_code_failure_system(void *out, size_t bytes, size_t offset, intptr_t errcode); + extern BOOST_OUTCOME_C_WEAK int outcome_status_code_equal(const void *a, const void *b); + extern BOOST_OUTCOME_C_WEAK int outcome_status_code_equal_generic(const void *a, int errcode); + extern BOOST_OUTCOME_C_WEAK const char *outcome_status_code_message(const void *a); + + +#ifdef __cplusplus +#define BOOST_OUTCOME_C_DECLARE_RESULT_STATUS_CODE_CXX(ident, R, S) \ + static_assert(std::is_trivially_copyable::value || BOOST_OUTCOME_V2_NAMESPACE::trait::is_move_bitcopying::value, \ + "R must be trivially copyable or move bitcopying to be used in a C Result"); \ + static_assert(std::is_trivially_copyable::value || BOOST_OUTCOME_V2_NAMESPACE::trait::is_move_bitcopying::value, \ + "S must be trivially copyable or move bitcopying to be used in a C Result"); \ + inline BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result to_result(const cxx_result_status_code_##ident &v) \ + { \ + union type_punner_t \ + { \ + cxx_result_status_code_##ident c; \ + BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result cpp; \ + \ + type_punner_t() \ + : c{} \ + { \ + } \ + ~type_punner_t() {} \ + } pun; \ + \ + pun.c = v; \ + return std::move(pun.cpp); \ + } \ + BOOST_OUTCOME_C_NODISCARD inline cxx_result_status_code_##ident to_##ident(BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result v) \ + { \ + union type_punner_t \ + { \ + BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result cpp; \ + cxx_result_status_code_##ident c; \ + \ + type_punner_t(BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result v) \ + : cpp(std::move(v)) \ + { \ + } \ + ~type_punner_t() {} \ + } pun{std::move(v)}; \ + \ + return pun.c; \ + } +#else +#define BOOST_OUTCOME_C_DECLARE_RESULT_STATUS_CODE_CXX(ident, R, S) +#endif + #define BOOST_OUTCOME_C_DECLARE_RESULT_STATUS_CODE(ident, R, S) \ struct cxx_result_status_code_##ident \ { \ R value; \ unsigned flags; \ S error; \ - } + }; \ + BOOST_OUTCOME_C_NODISCARD static BOOST_OUTCOME_C_INLINE struct cxx_result_status_code_##ident outcome_make_result_##ident##_success(R value) \ + { /* We special case this so it inlines efficiently */ \ + struct cxx_result_status_code_##ident ret; \ + ret.value = value; \ + ret.flags = 1 /* have_value */; \ + return ret; \ + } \ + BOOST_OUTCOME_C_NODISCARD_EXTERN_C BOOST_OUTCOME_C_WEAK struct cxx_result_status_code_##ident outcome_make_result_##ident##_failure_posix(int errcode) \ + { \ + struct cxx_result_status_code_##ident ret; \ + assert(outcome_make_result_status_code_failure_posix!=NULL); /* If this fails, you need to compile this file at least once in C++. */ \ + outcome_make_result_status_code_failure_posix((void *) &ret, sizeof(ret), offsetof(struct cxx_result_status_code_##ident, flags), errcode); \ + return ret; \ + } \ + BOOST_OUTCOME_C_MSVC_FORCE_EMIT(outcome_make_result_##ident##_failure_posix) \ + BOOST_OUTCOME_C_NODISCARD_EXTERN_C BOOST_OUTCOME_C_WEAK struct cxx_result_status_code_##ident outcome_make_result_##ident##_failure_system(intptr_t errcode) \ + { \ + struct cxx_result_status_code_##ident ret; \ + assert(outcome_make_result_status_code_failure_system!=NULL); /* If this fails, you need to compile this file at least once in C++. */ \ + outcome_make_result_status_code_failure_system((void *) &ret, sizeof(ret), offsetof(struct cxx_result_status_code_##ident, flags), errcode); \ + return ret; \ + } \ + BOOST_OUTCOME_C_MSVC_FORCE_EMIT(outcome_make_result_##ident##_failure_system) \ + BOOST_OUTCOME_C_DECLARE_RESULT_STATUS_CODE_CXX(ident, R, S) #define BOOST_OUTCOME_C_RESULT_STATUS_CODE(ident) struct cxx_result_status_code_##ident +#define BOOST_OUTCOME_C_TO_RESULT_STATUS_CODE(ident, ...) to_##ident(__VA_ARGS__) +#define BOOST_OUTCOME_C_MAKE_RESULT_STATUS_CODE_SUCCESS(ident, ...) outcome_make_result_##ident##_success(__VA_ARGS__) +#define BOOST_OUTCOME_C_MAKE_RESULT_STATUS_CODE_FAILURE_POSIX(ident, ...) outcome_make_result_##ident##_failure_posix(__VA_ARGS__) +#define BOOST_OUTCOME_C_MAKE_RESULT_STATUS_CODE_FAILURE_SYSTEM(ident, ...) outcome_make_result_##ident##_failure_system(__VA_ARGS__) + struct cxx_status_code_posix { @@ -97,8 +233,226 @@ extern "C" #define BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(ident, R) BOOST_OUTCOME_C_DECLARE_RESULT_STATUS_CODE(system_##ident, R, struct cxx_status_code_system) #define BOOST_OUTCOME_C_RESULT_SYSTEM(ident) BOOST_OUTCOME_C_RESULT_STATUS_CODE(system_##ident) -#ifdef __cplusplus +#define BOOST_OUTCOME_C_TO_RESULT_SYSTEM_CODE(ident, ...) to_system_##ident(__VA_ARGS__) +#define BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_SUCCESS(ident, ...) BOOST_OUTCOME_C_MAKE_RESULT_STATUS_CODE_SUCCESS(system_##ident, __VA_ARGS__) +#define BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FAILURE_POSIX(ident, ...) BOOST_OUTCOME_C_MAKE_RESULT_STATUS_CODE_FAILURE_POSIX(system_##ident, __VA_ARGS__) +#define BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FAILURE_SYSTEM(ident, ...) BOOST_OUTCOME_C_MAKE_RESULT_STATUS_CODE_FAILURE_SYSTEM(system_##ident, __VA_ARGS__) + +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_IMPLV(unique, retstmt, cleanup, spec, ...) \ + BOOST_OUTCOME_TRYV2_UNIQUE_STORAGE(unique, spec, __VA_ARGS__); \ + BOOST_OUTCOME_TRY_LIKELY_IF(BOOST_OUTCOME_C_RESULT_HAS_VALUE(unique)); \ + else \ + { \ + retstmt; \ + } +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_IMPLA(unique, retstmt, cleanup, var, ...) \ + BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_IMPLV(unique, retstmt, cleanup, var, __VA_ARGS__) \ + BOOST_OUTCOME_TRY2_VAR(var) = unique.value + +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_IMPL_RETURN(unique, ident) \ + BOOST_OUTCOME_C_RESULT_SYSTEM(ident) unique##_f; \ + unique##_f.flags = (unique).flags; \ + unique##_f.error = (unique).error; \ + return unique##_f + +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(unique, spec, ident, cleanup, ...) \ + BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_IMPLA(unique, BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_IMPL_RETURN(unique, ident), cleanup, spec, __VA_ARGS__) +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP(unique, spec, cleanup, ...) BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_IMPLA(unique, return unique, cleanup, spec, __VA_ARGS__) +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_WITH_CLEANUP(unique, cleanup, ...) BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_IMPLV(unique, return unique, cleanup, deduce, __VA_ARGS__) +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_SAME_RETURN_TYPE(unique, ...) BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_IMPLV(unique, return unique, , deduce, __VA_ARGS__) + +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_INVOKE_TRY8(a, b, c, d, e, f, g, h) \ + BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(BOOST_OUTCOME_TRY_UNIQUE_NAME, a, b, c, d, e, f, g, h) +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_INVOKE_TRY7(a, b, c, d, e, f, g) \ + BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(BOOST_OUTCOME_TRY_UNIQUE_NAME, a, b, c, d, e, f, g) +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_INVOKE_TRY6(a, b, c, d, e, f) \ + BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(BOOST_OUTCOME_TRY_UNIQUE_NAME, a, b, c, d, e, f) +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_INVOKE_TRY5(a, b, c, d, e) BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(BOOST_OUTCOME_TRY_UNIQUE_NAME, a, b, c, d, e) +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_INVOKE_TRY4(a, b, c, d) BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(BOOST_OUTCOME_TRY_UNIQUE_NAME, a, b, c, d) +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_INVOKE_TRY3(a, b, c) BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP(BOOST_OUTCOME_TRY_UNIQUE_NAME, a, b, c) +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_INVOKE_TRY2(a, b) BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_WITH_CLEANUP(BOOST_OUTCOME_TRY_UNIQUE_NAME, a, b) +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_INVOKE_TRY1(expr) BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_SAME_RETURN_TYPE(BOOST_OUTCOME_TRY_UNIQUE_NAME, expr) + +#define BOOST_OUTCOME_C_RESULT_SYSTEM_TRY(...) BOOST_OUTCOME_TRY_CALL_OVERLOAD(BOOST_OUTCOME_C_RESULT_SYSTEM_TRY_INVOKE_TRY, __VA_ARGS__) + +#define BOOST_OUTCOME_C_MAKE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, ...) outcome_make_result_##ident##_failure_system_enum_##enum_name(__VA_ARGS__) +#ifndef __cplusplus +// Declares the function in C, needs to occur at least once in a C++ source file to get implemented +#define BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, uuid, ...) \ + BOOST_OUTCOME_C_NODISCARD_EXTERN_C struct cxx_result_status_code_system_##ident outcome_make_result_##ident##_failure_system_enum_##enum_name(enum enum_name v); +#else +} + +#include "../config.hpp" +#include "status-code/config.hpp" +#include "status-code/system_code.hpp" +#include "status_result.hpp" + + +#include "status-code/posix_code.hpp" +#ifdef _WIN32 +#include "status-code/win32_code.hpp" +#endif + +#include +#include + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + +// You need to include this C header in at least one C++ source file to have these C helper functions be implemented +extern "C" BOOST_OUTCOME_C_WEAK void outcome_make_result_status_code_failure_posix(void *out, size_t bytes, size_t offset, int errcode) +{ + using value_type = BOOST_OUTCOME_V2_NAMESPACE::experimental::posix_code::value_type; + union type_punner_t + { + BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result cpp; + struct cxx_status_code + { + intptr_t value; + unsigned flags; + cxx_status_code_system error; + } c; + + explicit type_punner_t(BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result res) + : cpp(std::move(res)) + { + } + ~type_punner_t() {} + } pun{BOOST_OUTCOME_V2_NAMESPACE::experimental::posix_code(errcode)}; + static_assert(sizeof(pun.cpp) == sizeof(pun.c), ""); + static constexpr size_t punoffset = offsetof(type_punner_t::cxx_status_code, flags); + assert(bytes - offset >= sizeof(pun.cpp) - punoffset); + const size_t tocopy = std::min(bytes - offset, sizeof(pun.cpp) - punoffset); + memcpy(out, (void *) &pun.c, sizeof(value_type)); + memcpy((void *) ((char *) out + offset), (const void *) ((const char *) &pun.c + punoffset), tocopy); +} +BOOST_OUTCOME_C_MSVC_FORCE_EMIT(outcome_make_result_status_code_failure_posix) + +extern "C" BOOST_OUTCOME_C_WEAK void outcome_make_result_status_code_failure_system(void *out, size_t bytes, size_t offset, intptr_t errcode) +{ + using value_type = BOOST_OUTCOME_V2_NAMESPACE::experimental::system_code::value_type; + union type_punner_t + { + BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result cpp; + struct cxx_status_code + { + intptr_t value; + unsigned flags; + cxx_status_code_system error; + } c; + + explicit type_punner_t(BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result res) + : cpp(std::move(res)) + { + } + ~type_punner_t() {} + } pun{ +#ifdef _WIN32 + BOOST_OUTCOME_V2_NAMESPACE::experimental::win32_code((BOOST_OUTCOME_V2_NAMESPACE::experimental::win32::DWORD) errcode) +#else + BOOST_OUTCOME_V2_NAMESPACE::experimental::posix_code((int) errcode) +#endif + }; + static_assert(sizeof(pun.cpp) == sizeof(pun.c), ""); + static constexpr size_t punoffset = offsetof(type_punner_t::cxx_status_code, flags); + assert(bytes - offset >= sizeof(pun.cpp) - punoffset); + const size_t tocopy = std::min(bytes - offset, sizeof(pun.cpp) - punoffset); + memcpy(out, (void *) &pun.c, sizeof(value_type)); + memcpy((void *) ((char *) out + offset), (const void *) ((const char *) &pun.c + punoffset), tocopy); +} +BOOST_OUTCOME_C_MSVC_FORCE_EMIT(outcome_make_result_status_code_failure_system) + +extern "C" BOOST_OUTCOME_C_WEAK int outcome_status_code_equal(const void *_a, const void *_b) +{ + const auto *a = (const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::system_code *) _a; + const auto *b = (const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::system_code *) _b; + return *a == *b; } +BOOST_OUTCOME_C_MSVC_FORCE_EMIT(outcome_status_code_equal) + +extern "C" BOOST_OUTCOME_C_WEAK int outcome_status_code_equal_generic(const void *_a, int errcode) +{ + const auto *a = (const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::system_code *) _a; + return *a == (BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::errc) errcode; +} +BOOST_OUTCOME_C_MSVC_FORCE_EMIT(outcome_status_code_equal_generic) + +extern "C" BOOST_OUTCOME_C_WEAK const char *outcome_status_code_message(const void *_a) +{ + static thread_local BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::system_code::string_ref msg((const char *) nullptr, 0); + const auto *a = (const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::system_code *) _a; + msg = a->message(); + return msg.c_str(); +} +BOOST_OUTCOME_C_MSVC_FORCE_EMIT(outcome_status_code_message) + +BOOST_OUTCOME_V2_NAMESPACE_BEGIN +namespace experimental +{ + namespace detail + { + template inline RetType outcome_make_result_failure_system_enum(EnumType v) + { + using value_type = BOOST_OUTCOME_V2_NAMESPACE::experimental::system_code::value_type; + union type_punner_t + { + BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result cpp; + struct cxx_status_code + { + intptr_t value; + unsigned flags; + cxx_status_code_system error; + } c; + + explicit type_punner_t(BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result res) + : cpp(std::move(res)) + { + } + ~type_punner_t() {} + } pun{BOOST_OUTCOME_V2_NAMESPACE::experimental::quick_status_code_from_enum_code(v)}; + static constexpr size_t bytes = sizeof(RetType); + static constexpr size_t offset = offsetof(RetType, flags); + static constexpr size_t punoffset = offsetof(typename type_punner_t::cxx_status_code, flags); + assert(bytes - offset >= sizeof(pun.cpp) - punoffset); + const size_t tocopy = std::min(bytes - offset, sizeof(pun.cpp) - punoffset); + RetType ret; + memcpy(&ret, (void *) &pun.c, sizeof(value_type)); + memcpy((void *) ((char *) &ret + offset), (const void *) ((const char *) &pun.c + punoffset), tocopy); + return ret; + } + } // namespace detail +} // namespace experimental +BOOST_OUTCOME_V2_NAMESPACE_END + +// Unique UUID for the enum PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h +// .. is sequence of {enum_name::value, "text description", {errc::equivalent, ...}}, +#define BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, uuid, ...) \ + BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN \ + template <> struct quick_status_code_from_enum : quick_status_code_from_enum_defaults \ + { \ + static constexpr const auto domain_name = #enum_name; \ + static constexpr const auto domain_uuid = uuid; \ + static const std::initializer_list &value_mappings() \ + { \ + static const std::initializer_list v = {__VA_ARGS__}; \ + return v; \ + } \ + }; \ + BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END \ + extern "C" BOOST_OUTCOME_C_NODISCARD BOOST_OUTCOME_C_WEAK struct cxx_result_status_code_system_##ident outcome_make_result_##ident##_failure_system_enum_##enum_name( \ + enum enum_name v) \ + { \ + return BOOST_OUTCOME_V2_NAMESPACE::experimental::detail::outcome_make_result_failure_system_enum(v); \ + } \ + BOOST_OUTCOME_C_MSVC_FORCE_EMIT(outcome_make_result_##ident##_failure_system_enum_##enum_name) + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + #endif #endif diff --git a/include/boost/outcome/experimental/status-code/status-code/boost_error_code.hpp b/include/boost/outcome/experimental/status-code/boost_error_code.hpp similarity index 96% rename from include/boost/outcome/experimental/status-code/status-code/boost_error_code.hpp rename to include/boost/outcome/experimental/status-code/boost_error_code.hpp index 8569893cb..c13d324d3 100644 --- a/include/boost/outcome/experimental/status-code/status-code/boost_error_code.hpp +++ b/include/boost/outcome/experimental/status-code/boost_error_code.hpp @@ -66,7 +66,6 @@ namespace mixins class _boost_error_code_domain final : public status_code_domain { template friend class status_code; - template friend class detail::indirecting_domain; using _base = status_code_domain; using _error_code_type = boost::system::error_code; using _error_category_type = boost::system::error_category; @@ -124,10 +123,7 @@ class _boost_error_code_domain final : public status_code_domain static inline const _boost_error_code_domain *get(_error_code_type ec); - virtual string_ref name() const noexcept override - { - return string_ref(_name.c_str(), _name.size()); - } // NOLINT + virtual string_ref name() const noexcept override { return string_ref(_name.c_str(), _name.size()); } // NOLINT virtual payload_info_t payload_info() const noexcept override { @@ -196,7 +192,7 @@ namespace detail } if(ret == nullptr && count < max_items) { - ret = new(&items[count++].domain) _boost_error_code_domain(category); + ret = new(BOOST_OUTCOME_SYSTEM_ERROR2_ADDRESS_OF(items[count++].domain)) _boost_error_code_domain(category); } unlock(); return ret; @@ -323,7 +319,7 @@ namespace boost { namespace system { - inline BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code> make_status_code(error_code c) noexcept + inline BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::erased_status_code make_status_code(error_code c) noexcept { if(c.category() == detail::interop_category()) { diff --git a/include/boost/outcome/experimental/status-code/status-code/com_code.hpp b/include/boost/outcome/experimental/status-code/com_code.hpp similarity index 90% rename from include/boost/outcome/experimental/status-code/status-code/com_code.hpp rename to include/boost/outcome/experimental/status-code/com_code.hpp index 73ca2674c..8f1458fe4 100644 --- a/include/boost/outcome/experimental/status-code/status-code/com_code.hpp +++ b/include/boost/outcome/experimental/status-code/com_code.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: Feb 2018 @@ -39,6 +39,8 @@ DEALINGS IN THE SOFTWARE. #include "win32_code.hpp" #ifndef BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE +#include // needed by mingw for comdef.h to work + #include #endif @@ -46,10 +48,10 @@ BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN class _com_code_domain; /*! (Windows only) A COM error code. Note semantic equivalence testing is only implemented for `FACILITY_WIN32` -and `FACILITY_NT_BIT`. As you can see at [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 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 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 +and `FACILITY_NT_BIT`. As you can see at +[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 +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 +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 to add semantic equivalence testing for whichever extra COM codes that your application specifically needs. */ using com_code = status_code<_com_code_domain>; @@ -61,7 +63,6 @@ using com_error = status_error<_com_code_domain>; class _com_code_domain : public status_code_domain { template friend class status_code; - template friend class detail::indirecting_domain; using _base = status_code_domain; //! Construct from a `HRESULT` error code @@ -146,7 +147,11 @@ class _com_code_domain : public status_code_domain virtual string_ref name() const noexcept override { return string_ref("COM domain"); } // NOLINT - virtual payload_info_t payload_info() const noexcept override { 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 *)}; } + virtual payload_info_t payload_info() const noexcept override + { + 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 *)}; + } protected: virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT diff --git a/include/boost/outcome/experimental/status-code/status-code/config.hpp b/include/boost/outcome/experimental/status-code/config.hpp similarity index 80% rename from include/boost/outcome/experimental/status-code/status-code/config.hpp rename to include/boost/outcome/experimental/status-code/config.hpp index 849252d51..f78805043 100644 --- a/include/boost/outcome/experimental/status-code/status-code/config.hpp +++ b/include/boost/outcome/experimental/status-code/config.hpp @@ -64,6 +64,13 @@ Distributed under the Boost Software License, Version 1.0. #endif #endif +#if BOOST_OUTCOME_SYSTEM_ERROR2_USE_STD_ADDRESSOF +#include // for std::addressof +#define BOOST_OUTCOME_SYSTEM_ERROR2_ADDRESS_OF(...) std::addressof(__VA_ARGS__) +#else +#define BOOST_OUTCOME_SYSTEM_ERROR2_ADDRESS_OF(...) (&__VA_ARGS__) +#endif + #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 #if defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) || __cplusplus >= 201400 || _MSC_VER >= 1910 /* VS2017 */ //! Defined to be `constexpr` when on C++ 14 or better compilers. Usually automatic, can be overriden. @@ -124,15 +131,16 @@ Distributed under the Boost Software License, Version 1.0. #define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD __attribute__((warn_unused_result)) #elif defined(_MSC_VER) // _Must_inspect_result_ expands into this -#define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD \ - __declspec("SAL_name" \ - "(" \ - "\"_Must_inspect_result_\"" \ - "," \ - "\"\"" \ - "," \ - "\"2\"" \ - ")") __declspec("SAL_begin") __declspec("SAL_post") __declspec("SAL_mustInspect") __declspec("SAL_post") __declspec("SAL_checkReturn") __declspec("SAL_end") +#define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD \ + __declspec( \ + "SAL_name" \ + "(" \ + "\"_Must_inspect_result_\"" \ + "," \ + "\"\"" \ + "," \ + "\"2\"" \ + ")") __declspec("SAL_begin") __declspec("SAL_post") __declspec("SAL_mustInspect") __declspec("SAL_post") __declspec("SAL_checkReturn") __declspec("SAL_end") #endif #endif #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD @@ -174,7 +182,7 @@ Distributed under the Boost Software License, Version 1.0. #define BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(...) requires BOOST_OUTCOME_SYSTEM_ERROR2_CALL_OVERLOAD(BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND, __VA_ARGS__) #define BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(...) template <__VA_ARGS__> -#define BOOST_OUTCOME_SYSTEM_ERROR2_TEXPR(...) \ +#define BOOST_OUTCOME_SYSTEM_ERROR2_TEXPR(...) \ requires { (__VA_ARGS__); } #define BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(...) (__VA_ARGS__) #if !defined(_MSC_VER) || _MSC_FULL_VER >= 192400000 // VS 2019 16.3 is broken here @@ -200,8 +208,8 @@ Distributed under the Boost Software License, Version 1.0. //! The system_error2 namespace name. #define BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE system_error2 //! Begins the system_error2 namespace. -#define BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN \ - namespace system_error2 \ +#define BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN \ + namespace system_error2 \ { //! Ends the system_error2 namespace. #define BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END } @@ -268,9 +276,12 @@ namespace detail template using is_static_castable = std::integral_constant::value && is_integral_or_enum::value>; - template using is_union_castable = std::integral_constant::value && !std::is_array::value && !std::is_array::value>; + template + using is_union_castable = std::integral_constant::value && !std::is_array::value && !std::is_array::value>; - template using is_bit_castable = std::integral_constant::value && traits::is_move_bitcopying::value>; + template + using is_bit_castable = + std::integral_constant::value && traits::is_move_bitcopying::value>; template union bit_cast_union { @@ -296,7 +307,8 @@ namespace detail && !is_union_castable::value // && (!std::is_trivially_copyable_v // || !std::is_trivially_copyable_v) ) // - To bit_cast(const From &from) noexcept + To bit_cast(const From &from) + noexcept { bit_cast_union ret; memmove(&ret.source, &from, sizeof(ret.source)); @@ -341,7 +353,8 @@ namespace detail types it may insert the value into another object with extra padding bytes to satisfy bit_cast's preconditions that both types have the same size. */ - template using is_erasure_castable = std::integral_constant::value && traits::is_move_bitcopying::value>; + template + using is_erasure_castable = std::integral_constant::value && traits::is_move_bitcopying::value>; template ::value> struct identity_or_underlying_type { @@ -353,7 +366,9 @@ namespace detail }; template - using erasure_integer_type = typename std::conditional::type>::value, typename std::make_signed::type>::type, typename std::make_unsigned::type>::type>::type; + using erasure_integer_type = typename std::conditional::type>::value, + typename std::make_signed::type>::type, + typename std::make_unsigned::type>::type>::type; template struct padded_erasure_object { @@ -369,19 +384,35 @@ namespace detail }; BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From) - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value && (sizeof(To) == sizeof(From)))) constexpr To erasure_cast(const From &from) noexcept { return bit_cast(from); } + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value && (sizeof(To) == sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept { return bit_cast(from); } +#if defined(_WIN32) || defined(__APPLE__) || __LITTLE_ENDIAN__ || (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) + // We can avoid the type pun on little endian architectures which can aid optimisation BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, long = 5) - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value &&is_static_castable::value && (sizeof(To) < sizeof(From)))) constexpr To erasure_cast(const From &from) noexcept { return static_cast(bit_cast>(from)); } + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value &&is_static_castable::value && (sizeof(To) < sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept { return static_cast(bit_cast>(from)); } BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, int = 5) - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value &&is_static_castable::value && (sizeof(To) > sizeof(From)))) constexpr To erasure_cast(const From &from) noexcept { return bit_cast(static_cast>(from)); } + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value &&is_static_castable::value && (sizeof(To) > sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept { return bit_cast(static_cast>(from)); } + + BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, short = 5) + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value && !is_static_castable::value && (sizeof(To) < sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept { return bit_cast>(from).value; } + BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, char = 5) + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value && !is_static_castable::value && (sizeof(To) > sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept { return bit_cast(padded_erasure_object{from}); } +#else BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, short = 5) - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value && !is_static_castable::value && (sizeof(To) < sizeof(From)))) constexpr To erasure_cast(const From &from) noexcept { return bit_cast>(from).value; } + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value && (sizeof(To) < sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept { return bit_cast>(from).value; } BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, char = 5) - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value && !is_static_castable::value && (sizeof(To) > sizeof(From)))) constexpr To erasure_cast(const From &from) noexcept { return bit_cast(padded_erasure_object{from}); } + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable::value && (sizeof(To) > sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept { return bit_cast(padded_erasure_object{from}); } +#endif } // namespace detail BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END diff --git a/include/boost/outcome/experimental/status-code/status-code/detail/nt_code_to_generic_code.ipp b/include/boost/outcome/experimental/status-code/detail/nt_code_to_generic_code.ipp similarity index 100% rename from include/boost/outcome/experimental/status-code/status-code/detail/nt_code_to_generic_code.ipp rename to include/boost/outcome/experimental/status-code/detail/nt_code_to_generic_code.ipp diff --git a/include/boost/outcome/experimental/status-code/status-code/detail/nt_code_to_win32_code.ipp b/include/boost/outcome/experimental/status-code/detail/nt_code_to_win32_code.ipp similarity index 100% rename from include/boost/outcome/experimental/status-code/status-code/detail/nt_code_to_win32_code.ipp rename to include/boost/outcome/experimental/status-code/detail/nt_code_to_win32_code.ipp diff --git a/include/boost/outcome/experimental/status-code/status-code/detail/win32_code_to_generic_code.ipp b/include/boost/outcome/experimental/status-code/detail/win32_code_to_generic_code.ipp similarity index 100% rename from include/boost/outcome/experimental/status-code/status-code/detail/win32_code_to_generic_code.ipp rename to include/boost/outcome/experimental/status-code/detail/win32_code_to_generic_code.ipp diff --git a/include/boost/outcome/experimental/status-code/status-code/error.hpp b/include/boost/outcome/experimental/status-code/error.hpp similarity index 82% rename from include/boost/outcome/experimental/status-code/status-code/error.hpp rename to include/boost/outcome/experimental/status-code/error.hpp index ca1443984..ac4f5fd50 100644 --- a/include/boost/outcome/experimental/status-code/status-code/error.hpp +++ b/include/boost/outcome/experimental/status-code/error.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: Feb 2018 @@ -36,24 +36,24 @@ DEALINGS IN THE SOFTWARE. BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN -/*! An errored `system_code` which is always a failure. The closest equivalent to -`std::error_code`, except it cannot be null and cannot be modified. +/*! An errored `system_code` which must be a failure upon copy or move or +non-default construction. The closest equivalent to `std::error_code`, except +it cannot be modified. This refines `system_code` into an `error` object meeting the requirements of [P0709 Zero-overhead deterministic exceptions](https://wg21.link/P0709). Differences from `system_code`: -- Always a failure (this is checked at construction, and if not the case, -the program is terminated as this is a logic error) -- No default construction. -- No empty state possible. +- Almost always a failure (this is checked at copy or move and non-default +construction, and if not the case, the program is terminated as this is a logic +error) - Is immutable. As with `system_code`, it remains guaranteed to be two CPU registers in size, and move bitcopying. */ -using error = errored_status_code>; +using error = erased_errored_status_code; #ifndef NDEBUG static_assert(sizeof(error) == 2 * sizeof(void *), "error is not exactly two pointers in size!"); diff --git a/include/boost/outcome/experimental/status-code/status-code/errored_status_code.hpp b/include/boost/outcome/experimental/status-code/errored_status_code.hpp similarity index 94% rename from include/boost/outcome/experimental/status-code/status-code/errored_status_code.hpp rename to include/boost/outcome/experimental/status-code/errored_status_code.hpp index 79ed286a8..1e0481da4 100644 --- a/include/boost/outcome/experimental/status-code/status-code/errored_status_code.hpp +++ b/include/boost/outcome/experimental/status-code/errored_status_code.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: Jun 2018 @@ -32,7 +32,6 @@ DEALINGS IN THE SOFTWARE. #define BOOST_OUTCOME_SYSTEM_ERROR2_ERRORED_STATUS_CODE_HPP #include "quick_status_code_from_enum.hpp" -#include "status_code_ptr.hpp" BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN @@ -149,8 +148,8 @@ template class errored_status_code : public status_code>::value)) - explicit errored_status_code(const status_code> &v) noexcept(std::is_nothrow_copy_constructible::value) + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe>::value)) + explicit errored_status_code(const status_code> &v) noexcept(std::is_nothrow_copy_constructible::value) : errored_status_code(detail::erasure_cast(v.value())) // NOLINT { assert(v.domain() == this->domain()); // NOLINT @@ -160,7 +159,7 @@ template class errored_status_code : public status_code_value; } + constexpr const value_type &value() const & noexcept { return this->_value; } }; namespace traits @@ -171,9 +170,9 @@ namespace traits }; } // namespace traits -template class errored_status_code> : public status_code> +template class errored_status_code> : public status_code> { - using _base = status_code>; + using _base = status_code>; using _base::success; void _check() @@ -218,7 +217,7 @@ template class errored_status_code> : publ //! Implicit copy construction from any other status code if its value type is trivially copyable, it would fit into our storage, and it is not an erased //! status code. BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) // - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value), + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value), BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(!detail::is_erased_status_code::type>>::value)) errored_status_code(const status_code &v) noexcept : _base(v) // NOLINT @@ -228,7 +227,7 @@ template class errored_status_code> : publ //! Implicit copy construction from any other status code if its value type is trivially copyable and it would fit into our storage, and it is not an erased //! status code. BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) // - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value), + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value), BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(!detail::is_erased_status_code::type>>::value)) errored_status_code(const errored_status_code &v) noexcept : _base(static_cast &>(v)) // NOLINT @@ -237,7 +236,7 @@ template class errored_status_code> : publ } //! Implicit move construction from any other status code if its value type is trivially copyable or move bitcopying and it would fit into our storage BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) // - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value)) + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value)) errored_status_code(status_code &&v) noexcept : _base(static_cast &&>(v)) // NOLINT { @@ -245,7 +244,7 @@ template class errored_status_code> : publ } //! Implicit move construction from any other status code if its value type is trivially copyable or move bitcopying and it would fit into our storage BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) // - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value)) + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value)) errored_status_code(errored_status_code &&v) noexcept : _base(static_cast &&>(v)) // NOLINT { @@ -277,8 +276,8 @@ template class errored_status_code> : publ #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) //! Explicit copy construction from an unknown status code. Note that this will be empty if its value type is not trivially copyable or would not fit into our //! storage or the source domain's `_do_erased_copy()` refused the copy. - explicit errored_status_code(const status_code &v) // NOLINT - : _base(v) + explicit errored_status_code(in_place_t _, const status_code &v) // NOLINT + : _base(_, v) { _check(); } @@ -289,10 +288,15 @@ template class errored_status_code> : publ //! Return the erased `value_type` by value. constexpr value_type value() const noexcept { return this->_value; } }; +/*! An erased type specialisation of `errored_status_code`. +Available only if `ErasedType` satisfies `traits::is_move_bitcopying::value`. +*/ +template using erased_errored_status_code = errored_status_code>; + namespace traits { - template struct is_move_bitcopying>> + template struct is_move_bitcopying>> { static constexpr bool value = true; }; diff --git a/include/boost/outcome/experimental/status-code/status-code/generic_code.hpp b/include/boost/outcome/experimental/status-code/generic_code.hpp similarity index 88% rename from include/boost/outcome/experimental/status-code/status-code/generic_code.hpp rename to include/boost/outcome/experimental/status-code/generic_code.hpp index 30c8ad9aa..4d188e533 100644 --- a/include/boost/outcome/experimental/status-code/status-code/generic_code.hpp +++ b/include/boost/outcome/experimental/status-code/generic_code.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018 - 2020 Niall Douglas (5 commits) +(C) 2018 - 2024 Niall Douglas (5 commits) File Created: Feb 2018 @@ -29,6 +29,11 @@ Distributed under the Boost Software License, Version 1.0. #include // for error constants +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 6326) // constant comparison +#endif + BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN //! The generic error coding (POSIX) @@ -293,7 +298,6 @@ namespace detail class _generic_code_domain : public status_code_domain { template friend class status_code; - template friend class detail::indirecting_domain; using _base = status_code_domain; public: @@ -318,7 +322,11 @@ class _generic_code_domain : public status_code_domain virtual _base::string_ref name() const noexcept override { return string_ref("generic domain"); } // NOLINT - virtual payload_info_t payload_info() const noexcept override { 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 *)}; } + virtual payload_info_t payload_info() const noexcept override + { + 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 *)}; + } protected: virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT @@ -402,43 +410,49 @@ template inline BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 bool status_co return (!_domain && !o._domain); } //! True if the status code's are semantically equal via `equivalent()`. -template BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const status_code &a, const status_code &b) noexcept +template +BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const status_code &a, const status_code &b) noexcept { return a.equivalent(b); } //! True if the status code's are not semantically equal via `equivalent()`. -template BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const status_code &a, const status_code &b) noexcept +template +BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const status_code &a, const status_code &b) noexcept { return !a.equivalent(b); } //! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`. -BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, // - class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result::type) // Safe ADL lookup of make_status_code(), returns void if not found -BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, // + class MakeStatusCodeResult = + typename detail::safe_get_make_status_code_result::type) // Safe ADL lookup of make_status_code(), returns void if not found +BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const status_code &a, const T &b) { return a.equivalent(make_status_code(b)); } //! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`. -BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, // - class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result::type) // Safe ADL lookup of make_status_code(), returns void if not found -BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, // + class MakeStatusCodeResult = + typename detail::safe_get_make_status_code_result::type) // Safe ADL lookup of make_status_code(), returns void if not found +BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const T &a, const status_code &b) { return b.equivalent(make_status_code(a)); } //! True if the status code's are not semantically equal via `equivalent()` to `make_status_code(T)`. -BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, // - class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result::type) // Safe ADL lookup of make_status_code(), returns void if not found -BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, // + class MakeStatusCodeResult = + typename detail::safe_get_make_status_code_result::type) // Safe ADL lookup of make_status_code(), returns void if not found +BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const status_code &a, const T &b) { return !a.equivalent(make_status_code(b)); } //! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`. -BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, // - class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result::type) // Safe ADL lookup of make_status_code(), returns void if not found -BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, // + class MakeStatusCodeResult = + typename detail::safe_get_make_status_code_result::type) // Safe ADL lookup of make_status_code(), returns void if not found +BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const T &a, const status_code &b) { return !b.equivalent(make_status_code(a)); @@ -479,4 +493,8 @@ BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const T &a, const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif + #endif diff --git a/include/boost/outcome/experimental/status-code/status-code/getaddrinfo_code.hpp b/include/boost/outcome/experimental/status-code/getaddrinfo_code.hpp similarity index 94% rename from include/boost/outcome/experimental/status-code/status-code/getaddrinfo_code.hpp rename to include/boost/outcome/experimental/status-code/getaddrinfo_code.hpp index 2ed8d6f2f..66f1bf823 100644 --- a/include/boost/outcome/experimental/status-code/status-code/getaddrinfo_code.hpp +++ b/include/boost/outcome/experimental/status-code/getaddrinfo_code.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2020-2023 Niall Douglas (5 commits) +(C) 2020-2025 Niall Douglas (5 commits) File Created: Jan 2020 @@ -54,7 +54,6 @@ using getaddrinfo_error = status_error<_getaddrinfo_code_domain>; class _getaddrinfo_code_domain : public status_code_domain { template friend class status_code; - template friend class detail::indirecting_domain; using _base = status_code_domain; public: @@ -78,7 +77,11 @@ class _getaddrinfo_code_domain : public status_code_domain virtual string_ref name() const noexcept override { return string_ref("getaddrinfo() domain"); } // NOLINT - virtual payload_info_t payload_info() const noexcept override { 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 *)}; } + virtual payload_info_t payload_info() const noexcept override + { + 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 *)}; + } protected: virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT diff --git a/include/boost/outcome/experimental/status-code/status-code/http_status_code.hpp b/include/boost/outcome/experimental/status-code/http_status_code.hpp similarity index 96% rename from include/boost/outcome/experimental/status-code/status-code/http_status_code.hpp rename to include/boost/outcome/experimental/status-code/http_status_code.hpp index 8ff053f59..360e7c242 100644 --- a/include/boost/outcome/experimental/status-code/status-code/http_status_code.hpp +++ b/include/boost/outcome/experimental/status-code/http_status_code.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2022-2023 Niall Douglas (5 commits) +(C) 2022-2025 Niall Douglas (5 commits) File Created: Jun 2022 @@ -63,7 +63,6 @@ namespace mixins class _http_status_code_domain : public status_code_domain { template friend class status_code; - template friend class detail::indirecting_domain; using _base = status_code_domain; public: @@ -87,7 +86,11 @@ class _http_status_code_domain : public status_code_domain virtual string_ref name() const noexcept override { return string_ref("HTTP status domain"); } // NOLINT - virtual payload_info_t payload_info() const noexcept override { 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 *)}; } + virtual payload_info_t payload_info() const noexcept override + { + 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 *)}; + } protected: virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT diff --git a/include/boost/outcome/experimental/status-code/status-code/iostream_support.hpp b/include/boost/outcome/experimental/status-code/iostream_support.hpp similarity index 93% rename from include/boost/outcome/experimental/status-code/status-code/iostream_support.hpp rename to include/boost/outcome/experimental/status-code/iostream_support.hpp index e3715a9ed..94d5200ec 100644 --- a/include/boost/outcome/experimental/status-code/status-code/iostream_support.hpp +++ b/include/boost/outcome/experimental/status-code/iostream_support.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: Feb 2018 @@ -44,8 +44,7 @@ BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) // BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED( std::is_same() << std::declval::value_type>())>::type>::value)) -inline std::ostream & -operator<<(std::ostream &s, const status_code &v) +inline std::ostream &operator<<(std::ostream &s, const status_code &v) { if(v.empty()) { @@ -63,7 +62,7 @@ inline std::ostream &operator<<(std::ostream &s, const status_code_domain::strin /*! Print the erased status code to a `std::ostream &`. */ -template inline std::ostream &operator<<(std::ostream &s, const status_code> &v) +template inline std::ostream &operator<<(std::ostream &s, const status_code> &v) { if(v.empty()) { diff --git a/include/boost/outcome/experimental/status-code/nested_status_code.hpp b/include/boost/outcome/experimental/status-code/nested_status_code.hpp new file mode 100644 index 000000000..de5416c69 --- /dev/null +++ b/include/boost/outcome/experimental/status-code/nested_status_code.hpp @@ -0,0 +1,256 @@ +/* Pointer to a SG14 status_code +(C) 2018 - 2023 Niall Douglas (5 commits) +File Created: Sep 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NESTED_STATUS_CODE_HPP +#define BOOST_OUTCOME_SYSTEM_ERROR2_NESTED_STATUS_CODE_HPP + +#include "quick_status_code_from_enum.hpp" + +#include // for allocator + +BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN + +namespace detail +{ + template class indirecting_domain : public status_code_domain + { + template friend class status_code; + using _base = status_code_domain; + + public: + struct payload_type + { + using allocator_traits = std::allocator_traits; + union + { + char _uninit[sizeof(StatusCode)]; + StatusCode sc; + }; + Allocator alloc; + + payload_type(StatusCode _sc, Allocator _alloc) + : alloc(static_cast(_alloc)) + { + allocator_traits::construct(alloc, &sc, static_cast(_sc)); + } + payload_type(const payload_type &) = delete; + payload_type(payload_type &&) = delete; + ~payload_type() { allocator_traits::destroy(alloc, &sc); } + }; + using value_type = payload_type *; + using payload_allocator_traits = typename payload_type::allocator_traits::template rebind_traits; + using _base::string_ref; + + constexpr indirecting_domain() noexcept + : _base(0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id() /* unique-ish based on domain's unique id */) + { + } + indirecting_domain(const indirecting_domain &) = default; + indirecting_domain(indirecting_domain &&) = default; // NOLINT + indirecting_domain &operator=(const indirecting_domain &) = default; + indirecting_domain &operator=(indirecting_domain &&) = default; // NOLINT + ~indirecting_domain() = default; + +#if __cplusplus < 201402L && !defined(_MSC_VER) + static inline const indirecting_domain &get() + { + static indirecting_domain v; + return v; + } +#else + static inline constexpr const indirecting_domain &get(); +#endif + + virtual string_ref name() const noexcept override { return typename StatusCode::domain_type().name(); } // NOLINT + + virtual payload_info_t payload_info() const noexcept override + { + 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 *)}; + } + + protected: + using _mycode = status_code; + virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT + { + assert(code.domain() == *this); + const auto &c = static_cast(code); // NOLINT + return static_cast(typename StatusCode::domain_type())._do_failure(c.value()->sc); + } + virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override // NOLINT + { + assert(code1.domain() == *this); + const auto &c1 = static_cast(code1); // NOLINT + return static_cast(typename StatusCode::domain_type())._do_equivalent(c1.value()->sc, code2); + } + virtual generic_code _generic_code(const status_code &code) const noexcept override // NOLINT + { + assert(code.domain() == *this); + const auto &c = static_cast(code); // NOLINT + return static_cast(typename StatusCode::domain_type())._generic_code(c.value()->sc); + } + virtual string_ref _do_message(const status_code &code) const noexcept override // NOLINT + { + assert(code.domain() == *this); + const auto &c = static_cast(code); // NOLINT + return static_cast(typename StatusCode::domain_type())._do_message(c.value()->sc); + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) + BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT + { + assert(code.domain() == *this); + const auto &c = static_cast(code); // NOLINT + static_cast(typename StatusCode::domain_type())._do_throw_exception(c.value()->sc); + abort(); // suppress buggy GCC warning + } +#endif + virtual bool _do_erased_copy(status_code &dst, const status_code &src, payload_info_t dstinfo) const override // NOLINT + { + // Note that dst may not have its domain set + const auto srcinfo = payload_info(); + assert(src.domain() == *this); + if(dstinfo.total_size < srcinfo.total_size) + { + return false; + } + auto &d = static_cast<_mycode &>(dst); // NOLINT + const auto &_s = static_cast(src); // NOLINT + const payload_type &sp = *_s.value(); + typename payload_allocator_traits::template rebind_alloc payload_alloc(sp.alloc); + auto *dp = payload_allocator_traits::allocate(payload_alloc, 1); +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) + try +#endif + { + payload_allocator_traits::construct(payload_alloc, dp, sp.sc, sp.alloc); + new(BOOST_OUTCOME_SYSTEM_ERROR2_ADDRESS_OF(d)) _mycode(in_place, dp); + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) + catch(...) + { + payload_allocator_traits::deallocate(payload_alloc, dp, 1); + throw; + } +#endif + return true; + } + virtual void _do_erased_destroy(status_code &code, payload_info_t /*unused*/) const noexcept override // NOLINT + { + assert(code.domain() == *this); + auto &c = static_cast<_mycode &>(code); // NOLINT + payload_type *p = c.value(); + typename payload_allocator_traits::template rebind_alloc payload_alloc(p->alloc); + payload_allocator_traits::destroy(payload_alloc, p); + payload_allocator_traits::deallocate(payload_alloc, p, 1); + } + }; +#if __cplusplus >= 201402L || defined(_MSC_VER) + template constexpr indirecting_domain _indirecting_domain{}; + template + inline constexpr const indirecting_domain &indirecting_domain::get() + { + return _indirecting_domain; + } +#endif +} // namespace detail + +/*! Make an erased status code which indirects to a dynamically allocated status code, +using the allocator `alloc`. + +This is useful for shoehorning a rich status code with large value type into a small +erased status code like `system_code`, with which the status code generated by this +function is compatible. Note that this function can throw if the allocator throws. +*/ +BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class T, class Alloc = std::allocator::type>) +BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) // +inline status_code::type>::type>> make_nested_status_code(T &&v, Alloc alloc = {}) +{ + using status_code_type = typename std::decay::type; + using domain_type = detail::indirecting_domain::type>; + using payload_allocator_traits = typename domain_type::payload_allocator_traits; + typename payload_allocator_traits::template rebind_alloc payload_alloc(alloc); + auto *p = payload_allocator_traits::allocate(payload_alloc, 1); +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) + try +#endif + { + payload_allocator_traits::construct(payload_alloc, p, static_cast(v), static_cast(alloc)); + return status_code(in_place, p); + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) + catch(...) + { + payload_allocator_traits::deallocate(payload_alloc, p, 1); + throw; + } +#endif +} + +/*! If a status code refers to a `nested_status_code` which indirects to a status +code of type `StatusCode`, return a pointer to that `StatusCode`. Otherwise return null. +*/ +BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class StatusCode, class U) +BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) inline StatusCode *get_if(status_code> *v) noexcept +{ + if((0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id()) != v->domain().id()) + { + return nullptr; + } + union + { + U value; + StatusCode *ret; + }; + value = v->value(); + return ret; +} +//! \overload Const overload +BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class StatusCode, class U) +BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) +inline const StatusCode *get_if(const status_code> *v) noexcept +{ + if((0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id()) != v->domain().id()) + { + return nullptr; + } + union + { + U value; + const StatusCode *ret; + }; + value = v->value(); + return ret; +} + +/*! If a status code refers to a `nested_status_code`, return the id of the erased +status code's domain. Otherwise return a meaningless number. +*/ +template inline typename status_code_domain::unique_id_type get_id(const status_code> &v) noexcept +{ + return 0xc44f7bdeb2cc50e9 ^ v.domain().id(); +} + +BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END + +#endif diff --git a/include/boost/outcome/experimental/status-code/status-code/nt_code.hpp b/include/boost/outcome/experimental/status-code/nt_code.hpp similarity index 86% rename from include/boost/outcome/experimental/status-code/status-code/nt_code.hpp rename to include/boost/outcome/experimental/status-code/nt_code.hpp index 03dc142ca..a92c52283 100644 --- a/include/boost/outcome/experimental/status-code/status-code/nt_code.hpp +++ b/include/boost/outcome/experimental/status-code/nt_code.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: Feb 2018 @@ -37,17 +37,29 @@ DEALINGS IN THE SOFTWARE. #include "win32_code.hpp" +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 6326) // constant comparison +#endif + BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN //! \exclude namespace win32 { - // A Win32 NTSTATUS - using NTSTATUS = long; - // A Win32 HMODULE - using HMODULE = void *; - // Used to retrieve where the NTDLL DLL is mapped into memory - extern HMODULE __stdcall GetModuleHandleW(const wchar_t *lpModuleName); +#ifdef __MINGW32__ + extern "C" + { +#endif + // A Win32 NTSTATUS + using NTSTATUS = long; + // A Win32 HMODULE + using HMODULE = void *; + // Used to retrieve where the NTDLL DLL is mapped into memory + extern HMODULE __stdcall GetModuleHandleW(const wchar_t *lpModuleName); +#ifdef __MINGW32__ + } +#else #pragma comment(lib, "kernel32.lib") #if(defined(__x86_64__) || defined(_M_X64)) || (defined(__aarch64__) || defined(_M_ARM64)) #pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YAPEAXPEB_W@Z=GetModuleHandleW") @@ -58,6 +70,7 @@ namespace win32 #else #error Unknown architecture #endif +#endif } // namespace win32 class _nt_code_domain; @@ -71,7 +84,6 @@ using nt_error = status_error<_nt_code_domain>; class _nt_code_domain : public status_code_domain { template friend class status_code; - template friend class detail::indirecting_domain; friend class _com_code_domain; using _base = status_code_domain; static int _nt_code_to_errno(win32::NTSTATUS c) @@ -103,7 +115,9 @@ class _nt_code_domain : public status_code_domain { wchar_t buffer[32768]; static win32::HMODULE ntdll = win32::GetModuleHandleW(L"NTDLL.DLL"); - win32::DWORD wlen = win32::FormatMessageW(0x00000800 /*FORMAT_MESSAGE_FROM_HMODULE*/ | 0x00001000 /*FORMAT_MESSAGE_FROM_SYSTEM*/ | 0x00000200 /*FORMAT_MESSAGE_IGNORE_INSERTS*/, ntdll, c, (1 << 10) /*MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)*/, buffer, 32768, nullptr); + win32::DWORD wlen = + win32::FormatMessageW(0x00000800 /*FORMAT_MESSAGE_FROM_HMODULE*/ | 0x00001000 /*FORMAT_MESSAGE_FROM_SYSTEM*/ | 0x00000200 /*FORMAT_MESSAGE_IGNORE_INSERTS*/, + ntdll, c, (1 << 10) /*MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)*/, buffer, 32768, nullptr); size_t allocation = wlen + (wlen >> 1); win32::DWORD bytes; if(wlen == 0) @@ -160,7 +174,11 @@ class _nt_code_domain : public status_code_domain virtual string_ref name() const noexcept override { return string_ref("NT domain"); } // NOLINT - virtual payload_info_t payload_info() const noexcept override { 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 *)}; } + virtual payload_info_t payload_info() const noexcept override + { + 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 *)}; + } protected: virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT @@ -225,4 +243,8 @@ inline constexpr const _nt_code_domain &_nt_code_domain::get() BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif + #endif diff --git a/include/boost/outcome/experimental/status-code/status-code/posix_code.hpp b/include/boost/outcome/experimental/status-code/posix_code.hpp similarity index 91% rename from include/boost/outcome/experimental/status-code/status-code/posix_code.hpp rename to include/boost/outcome/experimental/status-code/posix_code.hpp index cae03a622..7ade9fe88 100644 --- a/include/boost/outcome/experimental/status-code/status-code/posix_code.hpp +++ b/include/boost/outcome/experimental/status-code/posix_code.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: Feb 2018 @@ -39,18 +39,27 @@ DEALINGS IN THE SOFTWARE. #include // for strchr and strerror_r +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 6326) // constant comparison +#endif + BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN // Fix for issue #48 Issue compiling on arm-none-eabi (newlib) with GNU extensions off -#ifdef __APPLE__ -#include -#elif !defined(_MSC_VER) +#if !defined(_MSC_VER) && !defined(__APPLE__) namespace detail { namespace avoid_string_include { +#if defined(__ANDROID__) + using ::strerror_r; +#elif defined(__GLIBC__) && !defined(__UCLIBC__) // This returns int for non-glibc strerror_r, but glibc's is particularly weird so we retain it extern "C" char *strerror_r(int errnum, char *buf, size_t buflen); +#else + extern "C" int strerror_r(int errnum, char *buf, size_t buflen); +#endif } // namespace avoid_string_include } // namespace detail #endif @@ -77,7 +86,6 @@ namespace mixins class _posix_code_domain : public status_code_domain { template friend class status_code; - template friend class detail::indirecting_domain; using _base = status_code_domain; static _base::string_ref _make_string_ref(int c) noexcept @@ -85,7 +93,7 @@ class _posix_code_domain : public status_code_domain char buffer[1024] = ""; #ifdef _WIN32 strerror_s(buffer, sizeof(buffer), c); -#elif defined(__gnu_linux__) && !defined(__ANDROID__) // handle glibc's weird strerror_r() +#elif defined(__GLIBC__) && !defined(__UCLIBC__) // handle glibc's weird strerror_r() char *s = detail::avoid_string_include::strerror_r(c, buffer, sizeof(buffer)); // NOLINT if(s != nullptr) { @@ -126,10 +134,7 @@ class _posix_code_domain : public status_code_domain //! Constexpr singleton getter. Returns constexpr posix_code_domain variable. static inline constexpr const _posix_code_domain &get(); - virtual string_ref name() const noexcept override - { - return string_ref("posix domain"); - } // NOLINT + virtual string_ref name() const noexcept override { return string_ref("posix domain"); } // NOLINT virtual payload_info_t payload_info() const noexcept override { @@ -200,4 +205,8 @@ namespace mixins BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif + #endif diff --git a/include/boost/outcome/experimental/status-code/status-code/quick_status_code_from_enum.hpp b/include/boost/outcome/experimental/status-code/quick_status_code_from_enum.hpp similarity index 82% rename from include/boost/outcome/experimental/status-code/status-code/quick_status_code_from_enum.hpp rename to include/boost/outcome/experimental/status-code/quick_status_code_from_enum.hpp index 757cb7e47..7d552bce4 100644 --- a/include/boost/outcome/experimental/status-code/status-code/quick_status_code_from_enum.hpp +++ b/include/boost/outcome/experimental/status-code/quick_status_code_from_enum.hpp @@ -25,6 +25,10 @@ Distributed under the Boost Software License, Version 1.0. #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_HPP #define BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_HPP +#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES +#define BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES 1 +#endif + #include "generic_code.hpp" BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN @@ -63,7 +67,6 @@ template struct quick_status_code_from_enum_defaults template class _quick_status_code_from_enum_domain : public status_code_domain { template friend class status_code; - template friend class detail::indirecting_domain; using _base = status_code_domain; using _src = quick_status_code_from_enum; @@ -94,7 +97,11 @@ template class _quick_status_code_from_enum_domain : public status_ virtual string_ref name() const noexcept override { return string_ref(_src::domain_name); } - virtual payload_info_t payload_info() const noexcept override { 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 *)}; } + virtual payload_info_t payload_info() const noexcept override + { + 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 *)}; + } protected: // Not sure if a hash table is worth it here, most enumerations won't be long enough to be worth it @@ -116,7 +123,9 @@ template class _quick_status_code_from_enum_domain : public status_ assert(code.domain() == *this); // NOLINT // If `errc::success` is in the generic code mapping, it is not a failure const auto *mapping = _find_mapping(static_cast &>(code).value()); - assert(mapping != nullptr); +#if BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES + assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table +#endif if(mapping != nullptr) { for(errc ec : mapping->code_mappings) @@ -131,7 +140,7 @@ template class _quick_status_code_from_enum_domain : public status_ } virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override { - assert(code1.domain() == *this); // NOLINT + assert(code1.domain() == *this); // NOLINT const auto &c1 = static_cast &>(code1); // NOLINT if(code2.domain() == *this) { @@ -142,7 +151,9 @@ template class _quick_status_code_from_enum_domain : public status_ { const auto &c2 = static_cast(code2); // NOLINT const auto *mapping = _find_mapping(c1.value()); - assert(mapping != nullptr); +#if BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES + assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table +#endif if(mapping != nullptr) { for(errc ec : mapping->code_mappings) @@ -160,7 +171,9 @@ template class _quick_status_code_from_enum_domain : public status_ { assert(code.domain() == *this); // NOLINT const auto *mapping = _find_mapping(static_cast &>(code).value()); - assert(mapping != nullptr); +#if BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES + assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table +#endif if(mapping != nullptr) { if(mapping->code_mappings.size() > 0) @@ -174,7 +187,9 @@ template class _quick_status_code_from_enum_domain : public status_ { assert(code.domain() == *this); // NOLINT const auto *mapping = _find_mapping(static_cast &>(code).value()); - assert(mapping != nullptr); +#if BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES + assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table +#endif if(mapping != nullptr) { return string_ref(mapping->message); @@ -184,7 +199,7 @@ template class _quick_status_code_from_enum_domain : public status_ #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override { - assert(code.domain() == *this); // NOLINT + assert(code.domain() == *this); // NOLINT const auto &c = static_cast &>(code); // NOLINT throw status_error<_quick_status_code_from_enum_domain>(c); } @@ -201,7 +216,8 @@ template inline constexpr const _quick_status_code_from_enum_domain namespace mixins { - template struct mixin> : public quick_status_code_from_enum::template mixin + template + struct mixin> : public quick_status_code_from_enum::template mixin { using quick_status_code_from_enum::template mixin::mixin; }; diff --git a/include/boost/outcome/experimental/status-code/status-code/result.hpp b/include/boost/outcome/experimental/status-code/result.hpp similarity index 99% rename from include/boost/outcome/experimental/status-code/status-code/result.hpp rename to include/boost/outcome/experimental/status-code/result.hpp index 6be7e4468..8cf1cf79d 100644 --- a/include/boost/outcome/experimental/status-code/status-code/result.hpp +++ b/include/boost/outcome/experimental/status-code/result.hpp @@ -1,5 +1,5 @@ /* A partial result based on std::variant and proposed std::error -(C) 2020-2023 Niall Douglas (11 commits) +(C) 2020-2025 Niall Douglas (11 commits) File Created: Jan 2020 diff --git a/include/boost/outcome/experimental/status-code/status-code/status_code_ptr.hpp b/include/boost/outcome/experimental/status-code/status-code/status_code_ptr.hpp deleted file mode 100644 index 7c288e4b8..000000000 --- a/include/boost/outcome/experimental/status-code/status-code/status_code_ptr.hpp +++ /dev/null @@ -1,195 +0,0 @@ -/* Pointer to a SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) -File Created: Sep 2018 - - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - -#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_PTR_HPP -#define BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_PTR_HPP - -#include "status_code.hpp" - -BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN - -namespace detail -{ - template class indirecting_domain : public status_code_domain - { - template friend class status_code; - using _base = status_code_domain; - - public: - using value_type = StatusCode *; - using _base::string_ref; - - constexpr indirecting_domain() noexcept - : _base(0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id() /* unique-ish based on domain's unique id */) - { - } - indirecting_domain(const indirecting_domain &) = default; - indirecting_domain(indirecting_domain &&) = default; // NOLINT - indirecting_domain &operator=(const indirecting_domain &) = default; - indirecting_domain &operator=(indirecting_domain &&) = default; // NOLINT - ~indirecting_domain() = default; - -#if __cplusplus < 201402L && !defined(_MSC_VER) - static inline const indirecting_domain &get() - { - static indirecting_domain v; - return v; - } -#else - static inline constexpr const indirecting_domain &get(); -#endif - - virtual string_ref name() const noexcept override { return typename StatusCode::domain_type().name(); } // NOLINT - - virtual payload_info_t payload_info() const noexcept override { 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 *)}; } - - protected: - using _mycode = status_code; - virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT - { - assert(code.domain() == *this); - const auto &c = static_cast(code); // NOLINT - return typename StatusCode::domain_type()._do_failure(*c.value()); - } - virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override // NOLINT - { - assert(code1.domain() == *this); - const auto &c1 = static_cast(code1); // NOLINT - return typename StatusCode::domain_type()._do_equivalent(*c1.value(), code2); - } - virtual generic_code _generic_code(const status_code &code) const noexcept override // NOLINT - { - assert(code.domain() == *this); - const auto &c = static_cast(code); // NOLINT - return typename StatusCode::domain_type()._generic_code(*c.value()); - } - virtual string_ref _do_message(const status_code &code) const noexcept override // NOLINT - { - assert(code.domain() == *this); - const auto &c = static_cast(code); // NOLINT - return typename StatusCode::domain_type()._do_message(*c.value()); - } -#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) - BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT - { - assert(code.domain() == *this); - const auto &c = static_cast(code); // NOLINT - typename StatusCode::domain_type()._do_throw_exception(*c.value()); - abort(); // suppress buggy GCC warning - } -#endif - virtual bool _do_erased_copy(status_code &dst, const status_code &src, payload_info_t dstinfo) const override // NOLINT - { - // Note that dst may not have its domain set - const auto srcinfo = payload_info(); - assert(src.domain() == *this); - if(dstinfo.total_size < srcinfo.total_size) - { - return false; - } - auto &d = static_cast<_mycode &>(dst); // NOLINT - const auto &_s = static_cast(src); // NOLINT - const StatusCode &s = *_s.value(); - new(&d) _mycode(in_place, new StatusCode(s)); - return true; - } - virtual void _do_erased_destroy(status_code &code, size_t /*unused*/) const noexcept override // NOLINT - { - assert(code.domain() == *this); - auto &c = static_cast<_mycode &>(code); // NOLINT - delete c.value(); // NOLINT - } - }; -#if __cplusplus >= 201402L || defined(_MSC_VER) - template constexpr indirecting_domain _indirecting_domain{}; - template inline constexpr const indirecting_domain &indirecting_domain::get() { return _indirecting_domain; } -#endif -} // namespace detail - -/*! Make an erased status code which indirects to a dynamically allocated status code. -This is useful for shoehorning a rich status code with large value type into a small -erased status code like `system_code`, with which the status code generated by this -function is compatible. Note that this function can throw due to `bad_alloc`. -*/ -BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class T) -BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) // -inline status_code::type>::type>> make_status_code_ptr(T &&v) -{ - using status_code_type = typename std::decay::type; - return status_code>(in_place, new status_code_type(static_cast(v))); -} - -/*! If a status code refers to a `status_code_ptr` which indirects to a status -code of type `StatusCode`, return a pointer to that `StatusCode`. Otherwise return null. -*/ -BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class StatusCode, class U) -BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) inline StatusCode *get_if(status_code> *v) noexcept -{ - if((0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id()) != v->domain().id()) - { - return nullptr; - } - union - { - U value; - StatusCode *ret; - }; - value = v->value(); - return ret; -} -//! \overload Const overload -BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class StatusCode, class U) -BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code::value)) -inline const StatusCode *get_if(const status_code> *v) noexcept -{ - if((0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id()) != v->domain().id()) - { - return nullptr; - } - union - { - U value; - const StatusCode *ret; - }; - value = v->value(); - return ret; -} - -/*! If a status code refers to a `status_code_ptr`, return the id of the erased -status code's domain. Otherwise return a meaningless number. -*/ -template inline typename status_code_domain::unique_id_type get_id(const status_code> &v) noexcept -{ - return 0xc44f7bdeb2cc50e9 ^ v.domain().id(); -} - -BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END - -#endif diff --git a/include/boost/outcome/experimental/status-code/status-code/status_code.hpp b/include/boost/outcome/experimental/status-code/status_code.hpp similarity index 84% rename from include/boost/outcome/experimental/status-code/status-code/status_code.hpp rename to include/boost/outcome/experimental/status-code/status_code.hpp index 797704bec..6735c9dd5 100644 --- a/include/boost/outcome/experimental/status-code/status-code/status_code.hpp +++ b/include/boost/outcome/experimental/status-code/status_code.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018 - 2020 Niall Douglas (5 commits) +(C) 2018 - 2023 Niall Douglas (5 commits) File Created: Feb 2018 @@ -60,15 +60,21 @@ namespace mixins }; } // namespace mixins -/*! A tag for an erased value type for `status_code`. +namespace detail +{ + BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class ErasedType) // + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(traits::is_move_bitcopying::value)) + struct erased + { + using value_type = ErasedType; + }; +} // namespace detail + +/*! The tag type used to specialise erased editions of `status_code`. Available only if `ErasedType` satisfies `traits::is_move_bitcopying::value`. */ -BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class ErasedType) // -BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(traits::is_move_bitcopying::value)) -struct erased -{ - using value_type = ErasedType; -}; +template // +using status_code_erased_tag_type = detail::erased; /*! Specialise this template to quickly wrap a third party enumeration into a custom status code domain. @@ -129,7 +135,7 @@ namespace detail { static constexpr bool value = false; }; - template struct is_erased_status_code>> + template struct is_erased_status_code>> { static constexpr bool value = true; }; @@ -334,41 +340,26 @@ namespace detail // Replace the type erased implementations with type aware implementations for better codegen //! Return the status code domain. - constexpr const domain_type &domain() const noexcept - { - return *static_cast(this->_domain); - } + constexpr const domain_type &domain() const noexcept { return *static_cast(this->_domain); } //! Reset the code to empty. BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 void clear() noexcept { this->_value.~value_type(); this->_domain = nullptr; - new(&this->_value) value_type(); + new(std::addressof(this->_value)) value_type(); } #if __cplusplus >= 201400 || _MSC_VER >= 1910 /* VS2017 */ //! Return a reference to the `value_type`. - constexpr value_type &value() &noexcept - { - return this->_value; - } + constexpr value_type &value() & noexcept { return this->_value; } //! Return a reference to the `value_type`. - constexpr value_type &&value() &&noexcept - { - return static_cast(this->_value); - } + constexpr value_type &&value() && noexcept { return static_cast(this->_value); } #endif //! Return a reference to the `value_type`. - constexpr const value_type &value() const &noexcept - { - return this->_value; - } + constexpr const value_type &value() const & noexcept { return this->_value; } //! Return a reference to the `value_type`. - constexpr const value_type &&value() const &&noexcept - { - return static_cast(this->_value); - } + constexpr const value_type &&value() const && noexcept { return static_cast(this->_value); } protected: status_code_storage() = default; @@ -526,8 +517,8 @@ template class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status Does not check if domains are equal. */ BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class ErasedType) // - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe>::value)) - constexpr explicit status_code(const status_code> &v) noexcept(std::is_nothrow_copy_constructible::value) + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe>::value)) + constexpr explicit status_code(const status_code> &v) noexcept(std::is_nothrow_copy_constructible::value) : status_code(detail::erasure_cast(v.value())) { #if __cplusplus >= 201400 @@ -566,10 +557,11 @@ If it is found, and it generates a status code compatible with this status code, is made available. */ template -class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code> : public mixins::mixin>, erased> +class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code> + : public mixins::mixin>, detail::erased> { template friend class status_code; - using _base = mixins::mixin>, erased>; + using _base = mixins::mixin>, detail::erased>; public: //! The type of the domain (void, as it is erased). @@ -594,7 +586,8 @@ class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code> : { if(nullptr != this->_domain) { - this->_domain->_do_erased_destroy(*this, sizeof(*this)); + status_code_domain::payload_info_t info{sizeof(value_type), sizeof(status_code), alignof(status_code)}; + this->_domain->_do_erased_destroy(*this, info); } } @@ -617,7 +610,7 @@ class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code> : //! Implicit copy construction from any other status code if its value type is trivially copyable, it would fit into our storage, and it is not an erased //! status code. BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) // - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value), + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value), BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(!detail::is_erased_status_code::type>>::value)) constexpr status_code(const status_code &v) noexcept // NOLINT : _base(typename _base::_value_type_constructor{}, v._domain_ptr(), detail::erasure_cast(v.value())) @@ -625,10 +618,14 @@ class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code> : } //! Implicit move construction from any other status code if its value type is trivially copyable or move bitcopying and it would fit into our storage BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) // - BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value)) + BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value)) BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code(status_code &&v) noexcept // NOLINT : _base(typename _base::_value_type_constructor{}, v._domain_ptr(), detail::erasure_cast(v.value())) { + alignas(alignof(typename DomainType::value_type)) char buffer[sizeof(typename DomainType::value_type)]; + new(buffer) typename DomainType::value_type(static_cast &&>(v).value()); + // deliberately do not destruct value moved into buffer + (void) buffer; v._domain = nullptr; } //! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`. @@ -657,7 +654,7 @@ class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code> : #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) //! Explicit copy construction from an unknown status code. Note that this will throw an exception if its value type is not trivially copyable or would not //! fit into our storage or the source domain's `_do_erased_copy()` refused the copy. - explicit BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code(const status_code &v) // NOLINT + explicit BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code(in_place_t, const status_code &v) // NOLINT : _base(typename _base::_value_type_constructor{}, v._domain_ptr(), value_type{}) { status_code_domain::payload_info_t info{sizeof(value_type), sizeof(status_code), alignof(status_code)}; @@ -698,20 +695,19 @@ class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code> : /**** By rights ought to be removed in any formal standard ****/ //! Reset the code to empty. - BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 void clear() noexcept - { - *this = status_code(); - } + BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 void clear() noexcept { *this = status_code(); } //! Return the erased `value_type` by value. - constexpr value_type value() const noexcept - { - return this->_value; - } + constexpr value_type value() const noexcept { return this->_value; } }; +/*! An erased type specialisation of `status_code`. +Available only if `ErasedType` satisfies `traits::is_move_bitcopying::value`. +*/ +template using erased_status_code = status_code>; + namespace traits { - template struct is_move_bitcopying>> + template struct is_move_bitcopying>> { static constexpr bool value = true; }; @@ -719,4 +715,62 @@ namespace traits BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END +#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_DISABLE_INLINE_GDB_PRETTY_PRINTERS +#if defined(__ELF__) +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Woverlength-strings" +#endif +__asm__( +".pushsection \".debug_gdb_scripts\", \"MS\",%progbits,1\n" +".ascii \"\\4gdb.inlined-script.BOOST_OUTCOME_SYSTEM_ERROR2_INLINE_GDB_PRETTY_PRINTERS_H\\n\"\n" +".ascii \"import gdb.printing\\n\"\n" +".ascii \"import gdb\\n\"\n" +".ascii \"import os\\n\"\n" + +".ascii \"def synthesise_gdb_value_from_string(s):\\n\"\n" +".ascii \" '''For when you want to return a synthetic string from children()'''\\n\"\n" +".ascii \" return gdb.Value(s + '\\\\0').cast(gdb.lookup_type('char').pointer())\\n\"\n" + +".ascii \"class StatusCodePrinter(object):\\n\"\n" +".ascii \" '''Print a system_error2::status_code'''\\n\"\n" + +".ascii \" def __init__(self, val):\\n\"\n" +".ascii \" self.val = val\\n\"\n" + +".ascii \" def children(self):\\n\"\n" +".ascii \" s = str(self.val['_domain'])\\n\"\n" +".ascii \" if 'posix_code_domain' in s or 'generic_code_domain' in s:\\n\"\n" +".ascii \" yield ('msg', synthesise_gdb_value_from_string(str(self.val['_value']) + ' (' + os.strerror(int(self.val['_value'])) + ')'))\\n\"\n" +".ascii \" yield ('domain', self.val['_domain'])\\n\"\n" +".ascii \" yield ('value', self.val['_value'])\\n\"\n" + +".ascii \" def display_hint(self):\\n\"\n" +".ascii \" return None\\n\"\n" + +".ascii \" def to_string(self):\\n\"\n" +".ascii \" s = str(self.val['_domain'])\\n\"\n" +".ascii \" if 'posix_code_domain' in s or 'generic_code_domain' in s:\\n\"\n" +".ascii \" return str(self.val['_value']) + ' (' + os.strerror(int(self.val['_value'])) + ')'\\n\"\n" +".ascii \" else:\\n\"\n" +".ascii \" return self.val['_value']\\n\"\n" + +".ascii \"def build_pretty_printer():\\n\"\n" +".ascii \" pp = gdb.printing.RegexpCollectionPrettyPrinter('system_error2')\\n\"\n" +".ascii \" pp.add_printer('system_error2::status_code', '^(boost::)?system_error2::status_code<.*>$', StatusCodePrinter)\\n\"\n" +".ascii \" return pp\\n\"\n" + +".ascii \"def register_printers(obj = None):\\n\"\n" +".ascii \" gdb.printing.register_pretty_printer(obj, build_pretty_printer(), replace = True)\\n\"\n" + +".ascii \"register_printers(gdb.current_objfile())\\n\"\n" + +".byte 0\n" +".popsection\n"); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif // defined(__ELF__) +#endif // !defined(BOOST_OUTCOME_SYSTEM_ERROR2_DISABLE_INLINE_GDB_PRETTY_PRINTERS) + #endif diff --git a/include/boost/outcome/experimental/status-code/status-code/status_code_domain.hpp b/include/boost/outcome/experimental/status-code/status_code_domain.hpp similarity index 94% rename from include/boost/outcome/experimental/status-code/status-code/status_code_domain.hpp rename to include/boost/outcome/experimental/status-code/status_code_domain.hpp index d56f4fc49..7bc429694 100644 --- a/include/boost/outcome/experimental/status-code/status-code/status_code_domain.hpp +++ b/include/boost/outcome/experimental/status-code/status_code_domain.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: Feb 2018 @@ -53,7 +53,7 @@ using generic_code = status_code<_generic_code_domain>; namespace detail { - template class indirecting_domain; + template class indirecting_domain; /* We are severely limited by needing to retain C++ 11 compatibility when doing constexpr string parsing. MSVC lets you throw exceptions within a constexpr evaluation context when exceptions are globally disabled, but won't let you @@ -110,7 +110,7 @@ namespace detail class status_code_domain { template friend class status_code; - template friend class indirecting_domain; + template friend class detail::indirecting_domain; public: //! Type of the unique id for this domain. @@ -261,55 +261,25 @@ class status_code_domain } //! Returns whether the reference is empty or not - BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD constexpr bool empty() const noexcept - { - return _begin == _end; - } + BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD constexpr bool empty() const noexcept { return _begin == _end; } //! Returns the size of the string - constexpr size_type size() const noexcept - { - return _end - _begin; - } + constexpr size_type size() const noexcept { return _end - _begin; } //! Returns a null terminated C string - constexpr const_pointer c_str() const noexcept - { - return _begin; - } + constexpr const_pointer c_str() const noexcept { return _begin; } //! Returns a null terminated C string - constexpr const_pointer data() const noexcept - { - return _begin; - } + constexpr const_pointer data() const noexcept { return _begin; } //! Returns the beginning of the string - BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 iterator begin() noexcept - { - return _begin; - } + BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 iterator begin() noexcept { return _begin; } //! Returns the beginning of the string - BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const_iterator begin() const noexcept - { - return _begin; - } + BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const_iterator begin() const noexcept { return _begin; } //! Returns the beginning of the string - constexpr const_iterator cbegin() const noexcept - { - return _begin; - } + constexpr const_iterator cbegin() const noexcept { return _begin; } //! Returns the end of the string - BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 iterator end() noexcept - { - return _end; - } + BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 iterator end() noexcept { return _end; } //! Returns the end of the string - BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const_iterator end() const noexcept - { - return _end; - } + BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const_iterator end() const noexcept { return _end; } //! Returns the end of the string - constexpr const_iterator cend() const noexcept - { - return _end; - } + constexpr const_iterator cend() const noexcept { return _end; } }; /*! A reference counted, threadsafe reference to a message string. @@ -423,26 +393,14 @@ class status_code_domain public: //! True if the unique ids match. - constexpr bool operator==(const status_code_domain &o) const noexcept - { - return _id == o._id; - } + constexpr bool operator==(const status_code_domain &o) const noexcept { return _id == o._id; } //! True if the unique ids do not match. - constexpr bool operator!=(const status_code_domain &o) const noexcept - { - return _id != o._id; - } + constexpr bool operator!=(const status_code_domain &o) const noexcept { return _id != o._id; } //! True if this unique is lower than the other's unique id. - constexpr bool operator<(const status_code_domain &o) const noexcept - { - return _id < o._id; - } + constexpr bool operator<(const status_code_domain &o) const noexcept { return _id < o._id; } //! Returns the unique id used to identify identical category instances. - constexpr unique_id_type id() const noexcept - { - return _id; - } + constexpr unique_id_type id() const noexcept { return _id; } //! Name of this category. BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual string_ref name() const noexcept = 0; //! Information about the payload of the code for this domain @@ -477,10 +435,7 @@ class status_code_domain BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_throw_exception(const status_code &code) const = 0; #else // Keep a vtable slot for binary compatibility - BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code & /*code*/) const - { - abort(); - } + BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code & /*code*/) const { abort(); } #endif // For a `status_code>` only, copy from `src` to `dst`. Default implementation uses `memcpy()`. You should return false here if your payload is not // trivially copyable or would not fit. @@ -497,10 +452,10 @@ class status_code_domain return true; } // NOLINT // For a `status_code>` only, destroy the erased value type. Default implementation does nothing. - BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_erased_destroy(status_code &code, size_t bytes) const noexcept // NOLINT + BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_erased_destroy(status_code &code, payload_info_t info) const noexcept // NOLINT { (void) code; - (void) bytes; + (void) info; } }; diff --git a/include/boost/outcome/experimental/status-code/status_code_gdb.py b/include/boost/outcome/experimental/status-code/status_code_gdb.py new file mode 100644 index 000000000..306eab5f7 --- /dev/null +++ b/include/boost/outcome/experimental/status-code/status_code_gdb.py @@ -0,0 +1,40 @@ +import gdb.printing +import gdb +import os + +def synthesise_gdb_value_from_string(s): + '''For when you want to return a synthetic string from children()''' + return gdb.Value(s + '\0').cast(gdb.lookup_type('char').pointer()) + +class StatusCodePrinter(object): + '''Print a system_error2::status_code''' + + def __init__(self, val): + self.val = val + + def children(self): + s = str(self.val['_domain']) + if 'posix_code_domain' in s or 'generic_code_domain' in s: + yield ('msg', synthesise_gdb_value_from_string(str(self.val['_value']) + ' (' + os.strerror(int(self.val['_value'])) + ')')) + yield ('domain', self.val['_domain']) + yield ('value', self.val['_value']) + + def display_hint(self): + return None + + def to_string(self): + s = str(self.val['_domain']) + if 'posix_code_domain' in s or 'generic_code_domain' in s: + return str(self.val['_value']) + ' (' + os.strerror(int(self.val['_value'])) + ')' + else: + return self.val['_value'] + +def build_pretty_printer(): + pp = gdb.printing.RegexpCollectionPrettyPrinter('system_error2') + pp.add_printer('system_error2::status_code', '^(boost::)?system_error2::status_code<.*>$', StatusCodePrinter) + return pp + +def register_printers(obj = None): + gdb.printing.register_pretty_printer(obj, build_pretty_printer(), replace = True) + +register_printers(gdb.current_objfile()) diff --git a/include/boost/outcome/experimental/status-code/status-code/status_error.hpp b/include/boost/outcome/experimental/status-code/status_error.hpp similarity index 89% rename from include/boost/outcome/experimental/status-code/status-code/status_error.hpp rename to include/boost/outcome/experimental/status-code/status_error.hpp index ea63ff48a..026203422 100644 --- a/include/boost/outcome/experimental/status-code/status-code/status_error.hpp +++ b/include/boost/outcome/experimental/status-code/status_error.hpp @@ -103,22 +103,22 @@ template class status_error : public status_error /*! Exception type representing a thrown erased status_code */ -template class status_error> : public status_error +template class status_error> : public status_error { - status_code> _code; + status_code> _code; typename status_code_domain::string_ref _msgref; - virtual const status_code> &_do_code() const noexcept override final { return _code; } + virtual const status_code> &_do_code() const noexcept override final { return _code; } public: //! The type of the status domain using domain_type = void; //! The type of the status code - using status_code_type = status_code>; + using status_code_type = status_code>; //! Constructs an instance - explicit status_error(status_code> code) - : _code(static_cast> &&>(code)) + explicit status_error(status_code> code) + : _code(static_cast> &&>(code)) , _msgref(_code.message()) { } diff --git a/include/boost/outcome/experimental/status-code/status-code/std_error_code.hpp b/include/boost/outcome/experimental/status-code/std_error_code.hpp similarity index 94% rename from include/boost/outcome/experimental/status-code/status-code/std_error_code.hpp rename to include/boost/outcome/experimental/status-code/std_error_code.hpp index cb3527339..39fd7badd 100644 --- a/include/boost/outcome/experimental/status-code/status-code/std_error_code.hpp +++ b/include/boost/outcome/experimental/status-code/std_error_code.hpp @@ -61,7 +61,6 @@ namespace mixins class _std_error_code_domain final : public status_code_domain { template friend class status_code; - template friend class detail::indirecting_domain; using _base = status_code_domain; using _error_code_type = std::error_code; using _error_category_type = std::error_category; @@ -84,7 +83,10 @@ class _std_error_code_domain final : public status_code_domain return _base::atomic_refcounted_string_ref(p, msg.size()); } #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) - catch(...) { return _base::string_ref("failed to allocate message"); } + catch(...) + { + return _base::string_ref("failed to allocate message"); + } #endif } @@ -118,7 +120,11 @@ class _std_error_code_domain final : public status_code_domain virtual string_ref name() const noexcept override { return string_ref(_name.c_str(), _name.size()); } // NOLINT - virtual payload_info_t payload_info() const noexcept override { 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 *)}; } + virtual payload_info_t payload_info() const noexcept override + { + 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 *)}; + } protected: virtual bool _do_failure(const status_code &code) const noexcept override; @@ -181,7 +187,7 @@ namespace detail } if(ret == nullptr && count < max_items) { - ret = new(&items[count++].domain) _std_error_code_domain(category); + ret = new(std::addressof(items[count++].domain)) _std_error_code_domain(category); } unlock(); return ret; @@ -306,7 +312,10 @@ BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END // Enable implicit construction of `std_error_code` from `std::error_code`. namespace std { - inline BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::std_error_code make_status_code(error_code c) noexcept { return BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::std_error_code(c); } + inline BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::std_error_code make_status_code(error_code c) noexcept + { + return BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::std_error_code(c); + } } // namespace std #endif diff --git a/include/boost/outcome/experimental/status-code/status-code/system_code.hpp b/include/boost/outcome/experimental/status-code/system_code.hpp similarity index 95% rename from include/boost/outcome/experimental/status-code/status-code/system_code.hpp rename to include/boost/outcome/experimental/status-code/system_code.hpp index 490bae05b..f053a7e39 100644 --- a/include/boost/outcome/experimental/status-code/status-code/system_code.hpp +++ b/include/boost/outcome/experimental/status-code/system_code.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: Feb 2018 @@ -58,7 +58,7 @@ For POSIX, `posix_code` is possible. You are guaranteed that `system_code` can be transported by the compiler in exactly two CPU registers. */ -using system_code = status_code>; +using system_code = erased_status_code; #ifndef NDEBUG static_assert(sizeof(system_code) == 2 * sizeof(void *), "system_code is not exactly two pointers in size!"); diff --git a/include/boost/outcome/experimental/status-code/status-code/system_code_from_exception.hpp b/include/boost/outcome/experimental/status-code/system_code_from_exception.hpp similarity index 94% rename from include/boost/outcome/experimental/status-code/status-code/system_code_from_exception.hpp rename to include/boost/outcome/experimental/status-code/system_code_from_exception.hpp index d4377eccb..5beb54bd0 100644 --- a/include/boost/outcome/experimental/status-code/status-code/system_code_from_exception.hpp +++ b/include/boost/outcome/experimental/status-code/system_code_from_exception.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: June 2018 @@ -44,7 +44,8 @@ BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN /*! A utility function which returns the closest matching system_code to a supplied exception ptr. */ -inline system_code system_code_from_exception(std::exception_ptr &&ep = std::current_exception(), system_code not_matched = generic_code(errc::resource_unavailable_try_again)) noexcept +inline system_code system_code_from_exception(std::exception_ptr &&ep = std::current_exception(), + system_code not_matched = generic_code(errc::resource_unavailable_try_again)) noexcept { if(!ep) { @@ -60,7 +61,7 @@ inline system_code system_code_from_exception(std::exception_ptr &&ep = std::cur { try { - system_code erased(e.code()); + system_code erased(in_place, e.code()); if(!erased.empty()) { return erased; diff --git a/include/boost/outcome/experimental/status-code/status-code/system_error2.hpp b/include/boost/outcome/experimental/status-code/system_error2.hpp similarity index 95% rename from include/boost/outcome/experimental/status-code/status-code/system_error2.hpp rename to include/boost/outcome/experimental/status-code/system_error2.hpp index ad68b7714..538f0c905 100644 --- a/include/boost/outcome/experimental/status-code/status-code/system_error2.hpp +++ b/include/boost/outcome/experimental/status-code/system_error2.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: Feb 2018 diff --git a/include/boost/outcome/experimental/status-code/status-code/win32_code.hpp b/include/boost/outcome/experimental/status-code/win32_code.hpp similarity index 87% rename from include/boost/outcome/experimental/status-code/status-code/win32_code.hpp rename to include/boost/outcome/experimental/status-code/win32_code.hpp index 44ab33c0c..9186b97f7 100644 --- a/include/boost/outcome/experimental/status-code/status-code/win32_code.hpp +++ b/include/boost/outcome/experimental/status-code/win32_code.hpp @@ -1,5 +1,5 @@ /* Proposed SG14 status_code -(C) 2018-2023 Niall Douglas (5 commits) +(C) 2018-2025 Niall Douglas (5 commits) File Created: Feb 2018 @@ -37,21 +37,33 @@ DEALINGS IN THE SOFTWARE. #include "quick_status_code_from_enum.hpp" +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 6326) // constant comparison +#endif + BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN //! \exclude namespace win32 { - // A Win32 DWORD - using DWORD = unsigned long; - // Used to retrieve the current Win32 error code - extern DWORD __stdcall GetLastError(); - // Used to retrieve a locale-specific message string for some error code - extern DWORD __stdcall FormatMessageW(DWORD dwFlags, const void *lpSource, DWORD dwMessageId, DWORD dwLanguageId, wchar_t *lpBuffer, DWORD nSize, - void /*va_list*/ *Arguments); - // Converts UTF-16 message string to UTF-8 - extern int __stdcall WideCharToMultiByte(unsigned int CodePage, DWORD dwFlags, const wchar_t *lpWideCharStr, int cchWideChar, char *lpMultiByteStr, - int cbMultiByte, const char *lpDefaultChar, int *lpUsedDefaultChar); +#ifdef __MINGW32__ + extern "C" + { +#endif + // A Win32 DWORD + using DWORD = unsigned long; + // Used to retrieve the current Win32 error code + extern DWORD __stdcall GetLastError(); + // Used to retrieve a locale-specific message string for some error code + extern DWORD __stdcall FormatMessageW(DWORD dwFlags, const void *lpSource, DWORD dwMessageId, DWORD dwLanguageId, wchar_t *lpBuffer, DWORD nSize, + void /*va_list*/ *Arguments); + // Converts UTF-16 message string to UTF-8 + extern int __stdcall WideCharToMultiByte(unsigned int CodePage, DWORD dwFlags, const wchar_t *lpWideCharStr, int cchWideChar, char *lpMultiByteStr, + int cbMultiByte, const char *lpDefaultChar, int *lpUsedDefaultChar); +#ifdef __MINGW32__ + } +#else #pragma comment(lib, "kernel32.lib") #if(defined(__x86_64__) || defined(_M_X64)) || (defined(__aarch64__) || defined(_M_ARM64)) #pragma comment(linker, "/alternatename:?GetLastError@win32@system_error2@@YAKXZ=GetLastError") @@ -68,6 +80,7 @@ namespace win32 #else #error Unknown architecture #endif +#endif } // namespace win32 class _win32_code_domain; @@ -93,7 +106,6 @@ namespace mixins class _win32_code_domain : public status_code_domain { template friend class status_code; - template friend class detail::indirecting_domain; friend class _com_code_domain; using _base = status_code_domain; static int _win32_code_to_errno(win32::DWORD c) @@ -166,10 +178,7 @@ class _win32_code_domain : public status_code_domain //! Constexpr singleton getter. Returns the constexpr win32_code_domain variable. static inline constexpr const _win32_code_domain &get(); - virtual string_ref name() const noexcept override - { - return string_ref("win32 domain"); - } // NOLINT + virtual string_ref name() const noexcept override { return string_ref("win32 domain"); } // NOLINT virtual payload_info_t payload_info() const noexcept override { @@ -223,7 +232,8 @@ class _win32_code_domain : public status_code_domain } #endif }; -//! (Windows only) A constexpr source variable for the win32 code domain, which is that of `GetLastError()` (Windows). Returned by `_win32_code_domain::get()`. +//! (Windows only) A constexpr source variable for the win32 code domain, which is that of `GetLastError()` (Windows). Returned by +//! `_win32_code_domain::get()`. constexpr _win32_code_domain win32_code_domain; inline constexpr const _win32_code_domain &_win32_code_domain::get() { @@ -240,4 +250,8 @@ namespace mixins BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif + #endif diff --git a/include/boost/outcome/experimental/status_outcome.hpp b/include/boost/outcome/experimental/status_outcome.hpp index 02e97650f..fa4611e15 100644 --- a/include/boost/outcome/experimental/status_outcome.hpp +++ b/include/boost/outcome/experimental/status_outcome.hpp @@ -1,5 +1,5 @@ /* A less simple result type -(C) 2018-2023 Niall Douglas (17 commits) +(C) 2018-2025 Niall Douglas (17 commits) File Created: Apr 2018 @@ -76,7 +76,7 @@ namespace experimental /*! AWAITING HUGO JSON CONVERSION TOOL SIGNATURE NOT RECOGNISED */ - template >, class P = std::exception_ptr, + template , class P = std::exception_ptr, class NoValuePolicy = policy::default_status_outcome_policy> // using status_outcome = basic_outcome; diff --git a/include/boost/outcome/experimental/status_result.hpp b/include/boost/outcome/experimental/status_result.hpp index a27f9218c..bfa64fca1 100644 --- a/include/boost/outcome/experimental/status_result.hpp +++ b/include/boost/outcome/experimental/status_result.hpp @@ -1,5 +1,5 @@ /* A very simple result type -(C) 2018-2023 Niall Douglas (11 commits) +(C) 2018-2025 Niall Douglas (11 commits) File Created: Apr 2018 @@ -34,10 +34,16 @@ DEALINGS IN THE SOFTWARE. #include "../basic_result.hpp" #include "../policy/fail_to_compile_observers.hpp" +#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_USE_STD_ADDRESSOF +#if BOOST_OUTCOME_USE_STD_ADDRESSOF +#define BOOST_OUTCOME_SYSTEM_ERROR2_USE_STD_ADDRESSOF 1 +#endif +#endif + #if __PCPP_ALWAYS_TRUE__ -#include "status-code/status-code/system_error2.hpp" -#elif !BOOST_OUTCOME_USE_SYSTEM_STATUS_CODE && __has_include("status-code/status-code/system_error2.hpp") -#include "status-code/status-code/system_error2.hpp" +#include "status-code/system_error2.hpp" +#elif !BOOST_OUTCOME_USE_SYSTEM_STATUS_CODE && __has_include("status-code/system_error2.hpp") +#include "status-code/system_error2.hpp" #else #include #endif @@ -159,7 +165,7 @@ namespace experimental /*! AWAITING HUGO JSON CONVERSION TOOL SIGNATURE NOT RECOGNISED */ - template >, + template , class NoValuePolicy = policy::default_status_result_policy> // using status_result = basic_result; diff --git a/include/boost/outcome/iostream_support.hpp b/include/boost/outcome/iostream_support.hpp index 346008ac8..3c7393158 100644 --- a/include/boost/outcome/iostream_support.hpp +++ b/include/boost/outcome/iostream_support.hpp @@ -1,5 +1,5 @@ /* iostream specialisations for result and outcome -(C) 2017-2023 Niall Douglas (21 commits) +(C) 2017-2025 Niall Douglas (21 commits) File Created: July 2017 @@ -31,211 +31,19 @@ DEALINGS IN THE SOFTWARE. #ifndef BOOST_OUTCOME_IOSTREAM_SUPPORT_HPP #define BOOST_OUTCOME_IOSTREAM_SUPPORT_HPP -#include "outcome.hpp" +#include "iostream_support_result.hpp" -#include -#include +#include "outcome.hpp" BOOST_OUTCOME_V2_NAMESPACE_BEGIN -namespace detail -{ - template typename std::add_lvalue_reference::type lvalueref() noexcept; - - template