├── .gitignore ├── example ├── CMakeLists.txt ├── scope_fail_example.cpp ├── scope_success_example.cpp └── scope_exit_example.cpp ├── .github └── workflows │ ├── macos.yml │ ├── windows.yml │ └── ubuntu.yml ├── LICENSE ├── test ├── 3rdparty │ ├── Catch2 │ │ ├── LICENSE │ │ └── catch_trompeloeil.hpp │ └── Trompeloeil │ │ └── LICENSE ├── CMakeLists.txt └── test.cpp ├── CMakeLists.txt ├── README.md └── include └── scope_guard.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ 3 | .vs/ 4 | 5 | ### C++ gitignore ### 6 | # Prerequisites 7 | *.d 8 | 9 | # Compiled Object files 10 | *.slo 11 | *.lo 12 | *.o 13 | *.obj 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Compiled Dynamic libraries 20 | *.so 21 | *.dylib 22 | *.dll 23 | 24 | # Fortran module files 25 | *.mod 26 | *.smod 27 | 28 | # Compiled Static libraries 29 | *.lai 30 | *.la 31 | *.a 32 | *.lib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | 39 | ### CMake gitignore ### 40 | CMakeLists.txt.user 41 | CMakeCache.txt 42 | CMakeFiles 43 | CMakeScripts 44 | Testing 45 | Makefile 46 | cmake_install.cmake 47 | install_manifest.txt 48 | compile_commands.json 49 | CTestTestfile.cmake 50 | _deps 51 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(CheckCXXCompilerFlag) 2 | 3 | if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) 4 | set(CMAKE_VERBOSE_MAKEFILE ON) 5 | set(OPTIONS -Wall -Wextra -pedantic-errors -Werror) 6 | elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 7 | set(OPTIONS /W4 /WX) 8 | check_cxx_compiler_flag(/permissive HAS_PERMISSIVE_FLAG) 9 | if(HAS_PERMISSIVE_FLAG) 10 | set(OPTIONS ${OPTIONS} /permissive-) 11 | endif() 12 | set(OPTIONS ${OPTIONS} /wd4702) # Disable warning C4702: unreachable code 13 | endif() 14 | 15 | function(make_example target) 16 | add_executable(${target} ${target}.cpp ${CMAKE_SOURCE_DIR}/include/${CMAKE_PROJECT_NAME}.hpp) 17 | set_target_properties(${target} PROPERTIES CXX_EXTENSIONS OFF) 18 | target_compile_features(${target} PRIVATE cxx_std_11) 19 | target_compile_options(${target} PRIVATE ${OPTIONS}) 20 | target_link_libraries(${target} PRIVATE ${CMAKE_PROJECT_NAME}) 21 | endfunction() 22 | 23 | make_example(scope_exit_example) 24 | make_example(scope_fail_example) 25 | make_example(scope_success_example) 26 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macos 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | jobs: 8 | build: 9 | runs-on: ${{ matrix.config.os }} 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | config: 14 | - { os: macos-11 } # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11-Readme.md#xcode 15 | - { os: macos-12 } # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-12-Readme.md#xcode 16 | 17 | name: "${{ matrix.config.os }}" 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Build Release 22 | run: | 23 | rm -rf build 24 | mkdir build 25 | cd build 26 | cmake .. -DCMAKE_BUILD_TYPE=Release 27 | cmake --build . -j 4 --config Release 28 | ctest --output-on-failure -C Release 29 | 30 | - name: Build Debug 31 | run: | 32 | rm -rf build 33 | mkdir build 34 | cd build 35 | cmake .. -DCMAKE_BUILD_TYPE=Debug 36 | cmake --build . -j 4 --config Debug 37 | ctest --output-on-failure -C Debug 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 - 2024 Daniil Goncharov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/3rdparty/Catch2/LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /test/3rdparty/Trompeloeil/LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: windows 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | jobs: 8 | build: 9 | runs-on: ${{ matrix.config.os }} 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | config: 14 | - { os: windows-2019, vs: "Visual Studio 2019" } # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#visual-studio-enterprise-2019 15 | - { os: windows-2022, vs: "Visual Studio 2022" } # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2022-Readme.md#visual-studio-enterprise-2022 16 | 17 | name: "${{ matrix.config.vs }}" 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Build Win32 22 | shell: bash 23 | run: | 24 | rm -rf build 25 | mkdir build 26 | cd build 27 | cmake .. -A Win32 28 | cmake --build . -j 4 --config Release 29 | ctest --output-on-failure -C Release 30 | cmake --build . -j 4 --config Debug 31 | ctest --output-on-failure -C Debug 32 | 33 | - name: Build x64 34 | shell: bash 35 | run: | 36 | rm -rf build 37 | mkdir build 38 | cd build 39 | cmake .. -A x64 40 | cmake --build . -j 4 --config Release 41 | ctest --output-on-failure -C Release 42 | cmake --build . -j 4 --config Debug 43 | ctest --output-on-failure -C Debug 44 | -------------------------------------------------------------------------------- /test/3rdparty/Catch2/catch_trompeloeil.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Trompeloeil C++ mocking framework 3 | * 4 | * Copyright Björn Fahller 2014-2019 5 | * Copyright Tore Martin Hagen 2019 6 | * 7 | * Use, modification and distribution is subject to the 8 | * Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at 10 | * http://www.boost.org/LICENSE_1_0.txt) 11 | * 12 | * Project home: https://github.com/rollbear/trompeloeil 13 | */ 14 | 15 | 16 | #ifndef TROMPELOEIL_CATCH2_HPP_ 17 | #define TROMPELOEIL_CATCH2_HPP_ 18 | 19 | #ifndef CATCH_VERSION_MAJOR 20 | #error " must be included before " 21 | #endif 22 | 23 | #include 24 | 25 | namespace trompeloeil 26 | { 27 | template <> 28 | inline void reporter::send( 29 | severity s, 30 | const char* file, 31 | unsigned long line, 32 | const char* msg) 33 | { 34 | std::ostringstream os; 35 | if (line) os << file << ':' << line << '\n'; 36 | os << msg; 37 | auto failure = os.str(); 38 | if (s == severity::fatal) 39 | { 40 | FAIL(failure); 41 | } 42 | else 43 | { 44 | CAPTURE(failure); 45 | CHECK(failure.empty()); 46 | } 47 | } 48 | 49 | template <> 50 | inline void reporter::sendOk( 51 | const char* trompeloeil_mock_calls_done_correctly) 52 | { 53 | REQUIRE(trompeloeil_mock_calls_done_correctly != 0); 54 | } 55 | } 56 | 57 | 58 | #endif //TROMPELOEIL_CATCH2_HPP_ 59 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(scope_guard VERSION "0.9.1" LANGUAGES CXX) 4 | 5 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 6 | set(IS_TOPLEVEL_PROJECT TRUE) 7 | else() 8 | set(IS_TOPLEVEL_PROJECT FALSE) 9 | endif() 10 | 11 | option(SCOPE_GUARD_OPT_BUILD_EXAMPLES "Build scope_guard examples" ${IS_TOPLEVEL_PROJECT}) 12 | option(SCOPE_GUARD_OPT_BUILD_TESTS "Build and perform scope_guard tests" ${IS_TOPLEVEL_PROJECT}) 13 | option(SCOPE_GUARD_OPT_INSTALL "Generate and install scope_guard target" ${IS_TOPLEVEL_PROJECT}) 14 | 15 | if(SCOPE_GUARD_OPT_BUILD_EXAMPLES) 16 | add_subdirectory(example) 17 | endif() 18 | 19 | if(SCOPE_GUARD_OPT_BUILD_TESTS) 20 | enable_testing() 21 | add_subdirectory(test) 22 | endif() 23 | 24 | include(CMakePackageConfigHelpers) 25 | 26 | add_library(${PROJECT_NAME} INTERFACE) 27 | add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 28 | target_include_directories(${PROJECT_NAME} 29 | INTERFACE 30 | $ 31 | $) 32 | 33 | write_basic_package_version_file(${PROJECT_NAME}ConfigVersion.cmake 34 | VERSION ${PROJECT_VERSION} 35 | COMPATIBILITY AnyNewerVersion 36 | ARCH_INDEPENDENT) 37 | 38 | if(SCOPE_GUARD_OPT_INSTALL) 39 | install(TARGETS ${PROJECT_NAME} 40 | EXPORT ${PROJECT_NAME}Config) 41 | 42 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 43 | DESTINATION lib/cmake/${PROJECT_NAME}) 44 | 45 | install(EXPORT ${PROJECT_NAME}Config 46 | NAMESPACE ${PROJECT_NAME}:: 47 | DESTINATION lib/cmake/${PROJECT_NAME}) 48 | 49 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include 50 | DESTINATION .) 51 | 52 | export(EXPORT ${PROJECT_NAME}Config 53 | NAMESPACE ${PROJECT_NAME}::) 54 | endif() 55 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(CheckCXXCompilerFlag) 2 | 3 | set(SOURCES test.cpp) 4 | 5 | if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 6 | set(OPTIONS /W4 /WX) 7 | check_cxx_compiler_flag(/permissive HAS_PERMISSIVE_FLAG) 8 | if(HAS_PERMISSIVE_FLAG) 9 | set(OPTIONS ${OPTIONS} /permissive-) 10 | endif() 11 | set(OPTIONS ${OPTIONS} /wd4702) # Disable warning C4702: unreachable code 12 | 13 | set(HAS_CPP11_FLAG TRUE) 14 | check_cxx_compiler_flag(/std:c++14 HAS_CPP14_FLAG) 15 | check_cxx_compiler_flag(/std:c++17 HAS_CPP17_FLAG) 16 | check_cxx_compiler_flag(/std:c++20 HAS_CPP20_FLAG) 17 | check_cxx_compiler_flag(/std:c++latest HAS_CPPLATEST_FLAG) 18 | elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 19 | set(CMAKE_VERBOSE_MAKEFILE ON) 20 | set(OPTIONS -Wall -Wextra -pedantic-errors -Werror) 21 | 22 | check_cxx_compiler_flag(-std=c++14 HAS_CPP14_FLAG) 23 | check_cxx_compiler_flag(-std=c++17 HAS_CPP17_FLAG) 24 | check_cxx_compiler_flag(-std=c++20 HAS_CPP20_FLAG) 25 | endif() 26 | 27 | function(make_test target std) 28 | add_executable(${target} ${SOURCES}) 29 | target_compile_options(${target} PRIVATE ${OPTIONS}) 30 | target_include_directories(${target} PRIVATE 3rdparty/Catch2) 31 | target_include_directories(${target} PRIVATE 3rdparty/Trompeloeil) 32 | target_link_libraries(${target} PRIVATE ${CMAKE_PROJECT_NAME}) 33 | set_target_properties(${target} PROPERTIES CXX_EXTENSIONS OFF) 34 | if(std) 35 | if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | target_compile_options(${target} PRIVATE /std:${std}) 37 | else() 38 | target_compile_options(${target} PRIVATE -std=${std}) 39 | endif() 40 | endif() 41 | add_test(NAME ${target} COMMAND ${target}) 42 | endfunction() 43 | 44 | if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 45 | make_test(${CMAKE_PROJECT_NAME}-cpp11.t "") 46 | else() 47 | make_test(${CMAKE_PROJECT_NAME}-cpp11.t c++11) 48 | endif() 49 | 50 | if(HAS_CPP14_FLAG) 51 | make_test(${CMAKE_PROJECT_NAME}-cpp14.t c++14) 52 | endif() 53 | 54 | if(HAS_CPP17_FLAG) 55 | make_test(${CMAKE_PROJECT_NAME}-cpp17.t c++17) 56 | endif() 57 | 58 | if(HAS_CPP20_FLAG) 59 | make_test(${CMAKE_PROJECT_NAME}-cpp20.t c++20) 60 | endif() 61 | 62 | if(HAS_CPPLATEST_FLAG) 63 | make_test(${CMAKE_PROJECT_NAME}-cpplatest.t c++latest) 64 | endif() 65 | -------------------------------------------------------------------------------- /example/scope_fail_example.cpp: -------------------------------------------------------------------------------- 1 | // Licensed under the MIT License . 2 | // SPDX-License-Identifier: MIT 3 | // Copyright (c) 2018 - 2024 Daniil Goncharov . 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | int main() { 30 | try { 31 | std::fstream file; 32 | file.open("test.txt", std::fstream::out | std::fstream::trunc); 33 | SCOPE_FAIL{ 34 | file.close(); 35 | std::cout << "[1] error write file" << std::endl; 36 | }; 37 | 38 | MAKE_SCOPE_FAIL(scope_fail_1) { 39 | std::cout << "[1] error write file #1" << std::endl; 40 | }; 41 | 42 | auto scope_fail_2 = scope_guard::make_scope_fail([&]() { 43 | std::cout << "[1] error write file #2" << std::endl; 44 | }); 45 | 46 | WITH_SCOPE_FAIL({ std::cout << "[1] leave WITH_SCOPE_FAIL" << std::endl; }) { 47 | std::cout << "[1] inside WITH_SCOPE_FAIL" << std::endl; 48 | } 49 | 50 | file << "example" << std::endl; 51 | std::cout << "[1] write to file" << std::endl; 52 | 53 | scope_fail_1.dismiss(); 54 | 55 | throw std::runtime_error{"error"}; 56 | 57 | scope_fail_2.dismiss(); 58 | 59 | file.close(); 60 | } 61 | catch (...) { 62 | std::cout << "[1] error" << std::endl; 63 | } 64 | 65 | std::fstream file; 66 | SCOPE_FAIL{ 67 | file.close(); 68 | std::cout << "[2] error write file" << std::endl; 69 | }; 70 | file.open("test.txt", std::fstream::out | std::fstream::trunc); 71 | file << "[2] example" << std::endl; 72 | std::cout << "[2] write to file" << std::endl; 73 | file.close(); 74 | 75 | return 0; 76 | 77 | // prints "[1] inside WITH_SCOPE_FAIL". 78 | // prints "[1] write to file". 79 | // prints "[1] error write file #2". 80 | // prints "[1] error write file". 81 | // prints "[1] error". 82 | // prints "[2] write to file". 83 | } 84 | -------------------------------------------------------------------------------- /example/scope_success_example.cpp: -------------------------------------------------------------------------------- 1 | // Licensed under the MIT License . 2 | // SPDX-License-Identifier: MIT 3 | // Copyright (c) 2018 - 2024 Daniil Goncharov . 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | int main() { 30 | try { 31 | std::fstream file; 32 | file.open("test.txt", std::fstream::out | std::fstream::trunc); 33 | SCOPE_SUCCESS{ 34 | file.close(); 35 | std::cout << "[1] file write success" << std::endl; 36 | }; 37 | 38 | MAKE_SCOPE_SUCCESS(scope_success_1) { 39 | std::cout << "[1] file write success" << std::endl; 40 | }; 41 | 42 | auto scope_success_2 = scope_guard::make_scope_success([&]() { 43 | std::cout << "[1] file write success" << std::endl; 44 | }); 45 | 46 | WITH_SCOPE_SUCCESS({ std::cout << "[1] leave WITH_SCOPE_SUCCESS" << std::endl; }) { 47 | std::cout << "[1] inside WITH_SCOPE_SUCCESS" << std::endl; 48 | } 49 | 50 | file << "example" << std::endl; 51 | std::cout << "[1] write to file" << std::endl; 52 | file.close(); 53 | 54 | scope_success_1.dismiss(); 55 | 56 | throw std::runtime_error{"error"}; 57 | 58 | scope_success_2.dismiss(); 59 | } 60 | catch (...) { 61 | std::cout << "[1] error" << std::endl; 62 | } 63 | 64 | std::fstream file; 65 | SCOPE_SUCCESS{ 66 | file.close(); 67 | std::cout << "[2] file write success" << std::endl; 68 | }; 69 | file.open("test.txt", std::fstream::out | std::fstream::trunc); 70 | file << "[2] example" << std::endl; 71 | std::cout << "[2] write to file" << std::endl; 72 | 73 | return 0; 74 | 75 | // prints "[1] inside WITH_SCOPE_SUCCESS". 76 | // prints "[1] leave WITH_SCOPE_SUCCESS". 77 | // prints "[1] write to file". 78 | // prints "[1] error". 79 | // prints "[2] write to file". 80 | // prints "[2] file write success". 81 | } 82 | -------------------------------------------------------------------------------- /example/scope_exit_example.cpp: -------------------------------------------------------------------------------- 1 | // Licensed under the MIT License . 2 | // SPDX-License-Identifier: MIT 3 | // Copyright (c) 2018 - 2024 Daniil Goncharov . 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #define SCOPE_GUARD_SUPPRESS_THROW_ACTION 28 | #define SCOPE_GUARD_CATCH_HANDLER std::cout << "exception in scope_guard!" << std::endl; 29 | 30 | #include 31 | 32 | int main() { 33 | try { 34 | std::fstream file; 35 | file.open("test.txt", std::fstream::out | std::fstream::trunc); 36 | SCOPE_EXIT{ 37 | file.close(); 38 | std::cout << "[1] close file" << std::endl; 39 | throw std::runtime_error{"error close file"}; 40 | }; 41 | 42 | MAKE_SCOPE_EXIT(scope_exit_1) { 43 | file.close(); 44 | std::cout << "[1] close file #1" << std::endl; 45 | }; 46 | 47 | auto scope_exit_2 = scope_guard::make_scope_exit([&]() { 48 | file.close(); 49 | std::cout << "[1] close file #2" << std::endl; 50 | }); 51 | 52 | WITH_SCOPE_EXIT({ std::cout << "[1] leave WITH_SCOPE_EXIT" << std::endl; }) { 53 | std::cout << "[1] inside WITH_SCOPE_EXIT" << std::endl; 54 | } 55 | 56 | file << "example" << std::endl; 57 | std::cout << "[1] write to file" << std::endl; 58 | 59 | scope_exit_1.dismiss(); 60 | 61 | throw std::runtime_error{"error"}; 62 | 63 | scope_exit_2.dismiss(); 64 | 65 | file.close(); 66 | } 67 | catch (...) { 68 | std::cout << "[1] error" << std::endl; 69 | } 70 | 71 | std::fstream file; 72 | SCOPE_EXIT{ 73 | file.close(); 74 | std::cout << "[2] close file" << std::endl; 75 | }; 76 | file.open("test.txt", std::fstream::out | std::fstream::trunc); 77 | file << "[2] example" << std::endl; 78 | std::cout << "[2] write to file" << std::endl; 79 | file.close(); 80 | 81 | return 0; 82 | 83 | // prints "[1] inside WITH_SCOPE_EXIT". 84 | // prints "[1] leave WITH_SCOPE_EXIT". 85 | // prints "[1] write to file". 86 | // prints "[1] close file #2". 87 | // prints "[1] close file". 88 | // prints "[1] error". 89 | // prints "[2] write to file". 90 | // prints "[2] close file". 91 | } 92 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: ubuntu 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | jobs: 8 | ubuntu: 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | compiler: 13 | - { cc: "gcc-9", cxx: "g++-9", os: "ubuntu-20.04" } 14 | - { cc: "gcc-10", cxx: "g++-10", os: "ubuntu-20.04" } 15 | - { cc: "gcc-10", cxx: "g++-10", os: "ubuntu-20.04" } 16 | - { cc: "gcc-11", cxx: "g++-11", os: "ubuntu-20.04" } 17 | - { cc: "gcc-11", cxx: "g++-11", os: "ubuntu-20.04" } 18 | - { cc: "gcc-12", cxx: "g++-12", os: "ubuntu-22.04" } 19 | - { cc: "clang-9", cxx: "clang++-9", os: "ubuntu-20.04" } 20 | - { cc: "clang-10", cxx: "clang++-10", os: "ubuntu-20.04" } 21 | - { cc: "clang-11", cxx: "clang++-11", os: "ubuntu-20.04" } 22 | - { cc: "clang-12", cxx: "clang++-12", os: "ubuntu-20.04" } 23 | - { cc: "clang-13", cxx: "clang++-13", os: "ubuntu-20.04" } 24 | - { cc: "clang-14", cxx: "clang++-14", os: "ubuntu-20.04" } 25 | - { cc: "clang-15", cxx: "clang++-15", os: "ubuntu-20.04" } 26 | - { cc: "clang-16", cxx: "clang++-16", os: "ubuntu-20.04" } 27 | 28 | name: "${{ matrix.compiler.cc }}" 29 | runs-on: ${{ matrix.compiler.os }} 30 | steps: 31 | - uses: actions/checkout@v4 32 | 33 | - name: Configure clang 34 | run: | 35 | if [[ "${{ matrix.compiler.cc }}" == "clang"* ]]; then 36 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - 37 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-9 main" 38 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-10 main" 39 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-11 main" 40 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main" 41 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main" 42 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main" 43 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main" 44 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-16 main" 45 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main" 46 | sudo apt update 47 | sudo apt install ${{ matrix.compiler.cc }} -y 48 | fi 49 | 50 | - name: Configure gcc 51 | run: | 52 | if [[ "${{ matrix.compiler.cc }}" == "gcc"* ]]; then 53 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y 54 | sudo apt update 55 | sudo apt install ${{ matrix.compiler.cxx }} -y 56 | fi 57 | 58 | - name: Build Release 59 | run: | 60 | rm -rf build 61 | mkdir build 62 | cd build 63 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} 64 | cmake --build . -j 4 --config Release 65 | ctest --output-on-failure -C Release 66 | 67 | - name: Build Debug 68 | run: | 69 | rm -rf build 70 | mkdir build 71 | cd build 72 | cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} 73 | cmake --build . -j 4 --config Debug 74 | ctest --output-on-failure -C Debug 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Github Releases](https://img.shields.io/github/release/Neargye/scope_guard.svg)](https://github.com/Neargye/scope_guard/releases) 2 | [![License](https://img.shields.io/github/license/Neargye/scope_guard.svg)](LICENSE) 3 | 4 | # Scope Guard & Defer C++ 5 | 6 | Scope Guard statement invokes a function with deferred execution until surrounding function returns in cases: 7 | 8 | * scope_exit - executing action on scope exit. 9 | 10 | * scope_fail - executing action on scope exit when an exception has been thrown. 11 | 12 | * scope_success - executing action on scope exit when no exceptions have been thrown. 13 | 14 | Program control transferring does not influence Scope Guard statement execution. Hence, Scope Guard statement can be used to perform manual resource management, such as file descriptors closing, and to perform actions even if an error occurs. 15 | 16 | ## Features 17 | 18 | * C++11 19 | * Header-only 20 | * Dependency-free 21 | * Thin callback wrapping, no added std::function or virtual table penalties 22 | * No implicitly ignored return, check callback return void 23 | * Defer or Scope Guard syntax and "With" syntax 24 | 25 | ## [Examples](example) 26 | 27 | * [Scope Guard on exit](example/scope_exit_example.cpp) 28 | 29 | ```cpp 30 | std::fstream file("test.txt"); 31 | SCOPE_EXIT{ file.close(); }; // File closes when exit the enclosing scope or errors occur. 32 | ``` 33 | 34 | * [Scope Guard on fail](example/scope_fail_example.cpp) 35 | 36 | ```cpp 37 | persons.push_back(person); // Add the person to db. 38 | SCOPE_FAIL{ persons.pop_back(); }; // If errors occur, we should roll back. 39 | ``` 40 | 41 | * [Scope Guard on success](example/scope_success_example.cpp) 42 | 43 | ```cpp 44 | person = new Person{/*...*/}; 45 | // ... 46 | SCOPE_SUCCESS{ persons.push_back(person); }; // If no errors occur, we should add the person to db. 47 | ``` 48 | 49 | * Custom Scope Guard 50 | 51 | ```cpp 52 | persons.push_back(person); // Add the person to db. 53 | 54 | MAKE_SCOPE_EXIT(scope_exit) { // Following block is executed when exit the enclosing scope or errors occur. 55 | persons.pop_back(); // If the db insertion fails, we should roll back. 56 | }; 57 | // MAKE_SCOPE_EXIT(name) {action} - macro is used to create a new scope_exit object. 58 | scope_exit.dismiss(); // An exception was not thrown, so don't execute the scope_exit. 59 | ``` 60 | 61 | ```cpp 62 | persons.push_back(person); // Add the person to db. 63 | 64 | auto scope_exit = make_scope_exit([]() { persons.pop_back(); }); 65 | // make_scope_exit(A&& action) - function is used to create a new scope_exit object. It can be instantiated with a lambda function, a std::function, a functor, or a void(*)() function pointer. 66 | // ... 67 | scope_exit.dismiss(); // An exception was not thrown, so don't execute the scope_exit. 68 | ``` 69 | 70 | * With Scope Guard 71 | 72 | ```cpp 73 | std::fstream file("test.txt"); 74 | WITH_SCOPE_EXIT({ file.close(); }) { // File closes when exit the enclosing with scope or errors occur. 75 | // ... 76 | }; 77 | ``` 78 | 79 | ## Synopsis 80 | 81 | ### Reference 82 | 83 | #### scope_exit 84 | 85 | * `scope_exit make_scope_exit(F&& action);` - return scope_exit with the action. 86 | * `SCOPE_EXIT{action};` - macro for creating scope_exit with the action. 87 | * `MAKE_SCOPE_EXIT(name) {action};` - macro for creating named scope_exit with the action. 88 | * `WITH_SCOPE_EXIT({action}) {/*...*/};` - macro for creating scope with scope_exit with the action. 89 | 90 | #### scope_fail 91 | 92 | * `scope_fail make_scope_fail(F&& action);` - return scope_fail with the action. 93 | * `SCOPE_FAIL{action};` - macro for creating scope_fail with the action. 94 | * `MAKE_SCOPE_FAIL(name) {action};` - macro for creating named scope_fail with the action. 95 | * `WITH_SCOPE_FAIL({action}) {/*...*/};` - macro for creating scope with scope_fail with the action. 96 | 97 | #### scope_success 98 | 99 | * `scope_success make_scope_success(F&& action);` - return scope_success with the action. 100 | * `SCOPE_SUCCESS{action};` - macro for creating scope_success with the action. 101 | * `MAKE_SCOPE_SUCCESS(name) {action};` - macro for creating named scope_success with the action. 102 | * `WITH_SCOPE_SUCCESS({action}) {/*...*/};` - macro for creating scope with scope_success with the action. 103 | 104 | #### defer 105 | 106 | * `DEFER{action};` - macro for creating defer with the action. 107 | * `MAKE_DEFER(name) {action};` - macro for creating named defer with the action. 108 | * `WITH_DEFER({action}) {/*...*/};` - macro for creating scope with defer with the action. 109 | 110 | ### Interface of scope_guard 111 | 112 | scope_exit, scope_fail, scope_success implement scope_guard interface. 113 | 114 | * `dismiss()` - dismiss executing action on scope exit. 115 | 116 | #### Throwable settings 117 | 118 | * `SCOPE_GUARD_NOTHROW_CONSTRUCTIBLE` define this to require nothrow constructible action. 119 | 120 | * `SCOPE_GUARD_MAY_THROW_ACTION` define this to action may throw exceptions. 121 | 122 | * `SCOPE_GUARD_NO_THROW_ACTION` define this to require noexcept action. 123 | 124 | * `SCOPE_GUARD_SUPPRESS_THROW_ACTIONS` define this to exceptions during action will be suppressed. 125 | 126 | * By default using `SCOPE_GUARD_MAY_THROW_ACTION`. 127 | 128 | * `SCOPE_GUARD_CATCH_HANDLER` define this to add exceptions handler. If `SCOPE_GUARD_SUPPRESS_THROW_ACTIONS` is not defined, it will do nothing. 129 | 130 | ### Remarks 131 | 132 | * If multiple Scope Guard statements appear in the same scope, the order they appear is the reverse of the order they are executed. 133 | 134 | ```cpp 135 | void f() { 136 | SCOPE_EXIT{ std::cout << "First" << std::endl; }; 137 | SCOPE_EXIT{ std::cout << "Second" << std::endl; }; 138 | SCOPE_EXIT{ std::cout << "Third" << std::endl; }; 139 | ... // Other code. 140 | // Prints "Third". 141 | // Prints "Second". 142 | // Prints "First". 143 | } 144 | ``` 145 | 146 | ## Integration 147 | 148 | You should add required file [scope_guard.hpp](include/scope_guard.hpp). 149 | 150 | ## References 151 | 152 | * [Andrei Alexandrescu "Systematic Error Handling in C++"](https://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C) 153 | * [Andrei Alexandrescu “Declarative Control Flow"](https://youtu.be/WjTrfoiB0MQ) 154 | 155 | ## Licensed under the [MIT License](LICENSE) 156 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | // Licensed under the MIT License . 2 | // SPDX-License-Identifier: MIT 3 | // Copyright (c) 2018 - 2024 Daniil Goncharov . 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #define CATCH_CONFIG_MAIN 24 | #include 25 | 26 | #define TROMPELOEIL_SANITY_CHECKS 27 | #include 28 | #include 29 | 30 | #define SCOPE_GUARD_NO_THROW_CONSTRUCTIBLE 31 | #include 32 | 33 | #include 34 | 35 | struct ExecutionCounter { 36 | MAKE_MOCK0(Execute, void()); 37 | }; 38 | 39 | class F { 40 | public: 41 | F() = default; 42 | F(F&&) = default; 43 | F(const F&) = default; 44 | ~F() = default; 45 | F& operator=(const F&) = default; 46 | F& operator=(F&&) = default; 47 | 48 | void operator() () {} 49 | }; 50 | 51 | TEST_CASE("called on scope leave") { 52 | SECTION("scope_exit") { 53 | ExecutionCounter m; 54 | REQUIRE_CALL_V(m, Execute(), 55 | .TIMES(1)); 56 | 57 | REQUIRE_NOTHROW([&]() { 58 | SCOPE_EXIT{ m.Execute(); }; 59 | }()); 60 | } 61 | 62 | SECTION("scope_fail") { 63 | ExecutionCounter m; 64 | REQUIRE_CALL_V(m, Execute(), 65 | .TIMES(0)); 66 | 67 | REQUIRE_NOTHROW([&]() { 68 | SCOPE_FAIL{ m.Execute(); }; 69 | }()); 70 | } 71 | 72 | SECTION("scope_success") { 73 | ExecutionCounter m; 74 | REQUIRE_CALL_V(m, Execute(), 75 | .TIMES(1)); 76 | 77 | REQUIRE_NOTHROW([&]() { 78 | SCOPE_SUCCESS{ m.Execute(); }; 79 | }()); 80 | } 81 | } 82 | 83 | TEST_CASE("called on exception") { 84 | SECTION("scope_exit") { 85 | ExecutionCounter m; 86 | REQUIRE_CALL_V(m, Execute(), 87 | .TIMES(1)); 88 | 89 | REQUIRE_THROWS([&]() { 90 | SCOPE_EXIT{ m.Execute(); }; 91 | 92 | throw std::exception{}; 93 | }()); 94 | } 95 | 96 | SECTION("scope_fail") { 97 | ExecutionCounter m; 98 | REQUIRE_CALL_V(m, Execute(), 99 | .TIMES(1)); 100 | 101 | REQUIRE_THROWS([&]() { 102 | SCOPE_FAIL{ m.Execute(); }; 103 | 104 | throw std::exception{}; 105 | }()); 106 | } 107 | 108 | SECTION("scope_success") { 109 | ExecutionCounter m; 110 | REQUIRE_CALL_V(m, Execute(), 111 | .TIMES(0)); 112 | 113 | REQUIRE_THROWS([&]() { 114 | SCOPE_SUCCESS{ m.Execute(); }; 115 | 116 | throw std::exception{}; 117 | }()); 118 | } 119 | } 120 | 121 | TEST_CASE("dismiss before scope leave") { 122 | SECTION("scope_exit") { 123 | ExecutionCounter m; 124 | REQUIRE_CALL_V(m, Execute(), 125 | .TIMES(0)); 126 | 127 | REQUIRE_NOTHROW([&]() { 128 | MAKE_SCOPE_EXIT(sg){ m.Execute(); }; 129 | sg.dismiss(); 130 | }()); 131 | } 132 | 133 | SECTION("scope_fail") { 134 | ExecutionCounter m; 135 | REQUIRE_CALL_V(m, Execute(), 136 | .TIMES(0)); 137 | 138 | REQUIRE_NOTHROW([&]() { 139 | MAKE_SCOPE_FAIL(sg){ m.Execute(); }; 140 | sg.dismiss(); 141 | }()); 142 | } 143 | 144 | SECTION("scope_success") { 145 | ExecutionCounter m; 146 | REQUIRE_CALL_V(m, Execute(), 147 | .TIMES(0)); 148 | 149 | REQUIRE_NOTHROW([&]() { 150 | MAKE_SCOPE_SUCCESS(sg){ m.Execute(); }; 151 | sg.dismiss(); 152 | }()); 153 | } 154 | } 155 | 156 | TEST_CASE("dismiss before exception") { 157 | SECTION("scope_exit") { 158 | ExecutionCounter m; 159 | REQUIRE_CALL_V(m, Execute(), 160 | .TIMES(0)); 161 | 162 | REQUIRE_THROWS([&]() { 163 | MAKE_SCOPE_EXIT(sg){ m.Execute(); }; 164 | 165 | sg.dismiss(); 166 | 167 | throw std::exception{}; 168 | }()); 169 | } 170 | 171 | SECTION("scope_fail") { 172 | ExecutionCounter m; 173 | REQUIRE_CALL_V(m, Execute(), 174 | .TIMES(0)); 175 | 176 | REQUIRE_THROWS([&]() { 177 | MAKE_SCOPE_FAIL(sg){ m.Execute(); }; 178 | 179 | sg.dismiss(); 180 | 181 | throw std::exception{}; 182 | }()); 183 | } 184 | 185 | SECTION("scope_success") { 186 | ExecutionCounter m; 187 | REQUIRE_CALL_V(m, Execute(), 188 | .TIMES(0)); 189 | 190 | REQUIRE_THROWS([&]() { 191 | MAKE_SCOPE_SUCCESS(sg){ m.Execute(); }; 192 | 193 | sg.dismiss(); 194 | 195 | throw std::exception{}; 196 | }()); 197 | } 198 | } 199 | 200 | TEST_CASE("called on exception, dismiss after exception") { 201 | SECTION("scope_exit") { 202 | ExecutionCounter m; 203 | REQUIRE_CALL_V(m, Execute(), 204 | .TIMES(1)); 205 | 206 | REQUIRE_THROWS([&]() { 207 | MAKE_SCOPE_EXIT(sg){ m.Execute(); }; 208 | 209 | throw std::exception{}; 210 | 211 | sg.dismiss(); 212 | }()); 213 | } 214 | 215 | SECTION("scope_fail") { 216 | ExecutionCounter m; 217 | REQUIRE_CALL_V(m, Execute(), 218 | .TIMES(1)); 219 | 220 | REQUIRE_THROWS([&]() { 221 | MAKE_SCOPE_FAIL(sg){ m.Execute(); }; 222 | 223 | throw std::exception{}; 224 | 225 | sg.dismiss(); 226 | }()); 227 | } 228 | 229 | SECTION("scope_success") { 230 | ExecutionCounter m; 231 | REQUIRE_CALL_V(m, Execute(), 232 | .TIMES(0)); 233 | 234 | REQUIRE_THROWS([&]() { 235 | MAKE_SCOPE_SUCCESS(sg){ m.Execute(); }; 236 | 237 | throw std::exception{}; 238 | 239 | sg.dismiss(); 240 | }()); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /include/scope_guard.hpp: -------------------------------------------------------------------------------- 1 | // _____ _____ _ _____ 2 | // / ____| / ____| | | / ____|_ _ 3 | // | (___ ___ ___ _ __ ___ | | __ _ _ __ _ _ __ __| | | | _| |_ _| |_ 4 | // \___ \ / __/ _ \| '_ \ / _ \ | | |_ | | | |/ _` | '__/ _` | | | |_ _|_ _| 5 | // ____) | (_| (_) | |_) | __/ | |__| | |_| | (_| | | | (_| | | |____|_| |_| 6 | // |_____/ \___\___/| .__/ \___| \_____|\__,_|\__,_|_| \__,_| \_____| 7 | // | | https://github.com/Neargye/scope_guard 8 | // |_| version 0.9.1 9 | // 10 | // Licensed under the MIT License . 11 | // SPDX-License-Identifier: MIT 12 | // Copyright (c) 2018 - 2024 Daniil Goncharov . 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining a copy 15 | // of this software and associated documentation files (the "Software"), to deal 16 | // in the Software without restriction, including without limitation the rights 17 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the Software is 19 | // furnished to do so, subject to the following conditions: 20 | // 21 | // The above copyright notice and this permission notice shall be included in all 22 | // copies or substantial portions of the Software. 23 | // 24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | // SOFTWARE. 31 | 32 | #ifndef NEARGYE_SCOPE_GUARD_HPP 33 | #define NEARGYE_SCOPE_GUARD_HPP 34 | 35 | #define SCOPE_GUARD_VERSION_MAJOR 0 36 | #define SCOPE_GUARD_VERSION_MINOR 9 37 | #define SCOPE_GUARD_VERSION_PATCH 1 38 | 39 | #include 40 | #if (defined(_MSC_VER) && _MSC_VER >= 1900) || ((defined(__clang__) || defined(__GNUC__)) && __cplusplus >= 201700L) 41 | #include 42 | #endif 43 | 44 | // scope_guard throwable settings: 45 | // SCOPE_GUARD_NO_THROW_CONSTRUCTIBLE requires nothrow constructible action. 46 | // SCOPE_GUARD_MAY_THROW_ACTION action may throw exceptions. 47 | // SCOPE_GUARD_NO_THROW_ACTION requires noexcept action. 48 | // SCOPE_GUARD_SUPPRESS_THROW_ACTION exceptions during action will be suppressed. 49 | // SCOPE_GUARD_CATCH_HANDLER exceptions handler. If SCOPE_GUARD_SUPPRESS_THROW_ACTIONS is not defined, it will do nothing. 50 | 51 | #if !defined(SCOPE_GUARD_MAY_THROW_ACTION) && !defined(SCOPE_GUARD_NO_THROW_ACTION) && !defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION) 52 | # define SCOPE_GUARD_MAY_THROW_ACTION 53 | #elif (defined(SCOPE_GUARD_MAY_THROW_ACTION) + defined(SCOPE_GUARD_NO_THROW_ACTION) + defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION)) > 1 54 | # error Only one of SCOPE_GUARD_MAY_THROW_ACTION and SCOPE_GUARD_NO_THROW_ACTION and SCOPE_GUARD_SUPPRESS_THROW_ACTION may be defined. 55 | #endif 56 | 57 | #if !defined(SCOPE_GUARD_CATCH_HANDLER) 58 | # define SCOPE_GUARD_CATCH_HANDLER /* Suppress exception.*/ 59 | #endif 60 | 61 | namespace scope_guard { 62 | 63 | namespace detail { 64 | 65 | #if defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION) && (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)) 66 | # define NEARGYE_NOEXCEPT(...) noexcept 67 | # define NEARGYE_TRY try { 68 | # define NEARGYE_CATCH } catch (...) { SCOPE_GUARD_CATCH_HANDLER } 69 | #else 70 | # define NEARGYE_NOEXCEPT(...) noexcept(__VA_ARGS__) 71 | # define NEARGYE_TRY 72 | # define NEARGYE_CATCH 73 | #endif 74 | 75 | #define NEARGYE_MOV(...) static_cast::type&&>(__VA_ARGS__) 76 | #define NEARGYE_FWD(...) static_cast(__VA_ARGS__) 77 | 78 | // NEARGYE_NODISCARD encourages the compiler to issue a warning if the return value is discarded. 79 | #if !defined(NEARGYE_NODISCARD) 80 | # if defined(__clang__) 81 | # if (__clang_major__ * 10 + __clang_minor__) >= 39 && __cplusplus >= 201703L 82 | # define NEARGYE_NODISCARD [[nodiscard]] 83 | # else 84 | # define NEARGYE_NODISCARD __attribute__((__warn_unused_result__)) 85 | # endif 86 | # elif defined(__GNUC__) 87 | # if __GNUC__ >= 7 && __cplusplus >= 201703L 88 | # define NEARGYE_NODISCARD [[nodiscard]] 89 | # else 90 | # define NEARGYE_NODISCARD __attribute__((__warn_unused_result__)) 91 | # endif 92 | # elif defined(_MSC_VER) 93 | # if _MSC_VER >= 1911 && defined(_MSVC_LANG) && _MSVC_LANG >= 201703L 94 | # define NEARGYE_NODISCARD [[nodiscard]] 95 | # elif defined(_Check_return_) 96 | # define NEARGYE_NODISCARD _Check_return_ 97 | # else 98 | # define NEARGYE_NODISCARD 99 | # endif 100 | # else 101 | # define NEARGYE_NODISCARD 102 | # endif 103 | #endif 104 | 105 | #if defined(_MSC_VER) && _MSC_VER < 1900 106 | inline int uncaught_exceptions() noexcept { 107 | return *(reinterpret_cast(static_cast(static_cast(_getptd())) + (sizeof(void*) == 8 ? 0x100 : 0x90))); 108 | } 109 | #elif (defined(__clang__) || defined(__GNUC__)) && __cplusplus < 201700L 110 | struct __cxa_eh_globals; 111 | extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept; 112 | inline int uncaught_exceptions() noexcept { 113 | return static_cast(*(reinterpret_cast(static_cast(static_cast(__cxa_get_globals())) + sizeof(void*)))); 114 | } 115 | #else 116 | inline int uncaught_exceptions() noexcept { 117 | return std::uncaught_exceptions(); 118 | } 119 | #endif 120 | 121 | class on_exit_policy { 122 | bool execute_; 123 | 124 | public: 125 | explicit on_exit_policy(bool execute) noexcept : execute_{execute} {} 126 | 127 | void dismiss() noexcept { 128 | execute_ = false; 129 | } 130 | 131 | bool should_execute() const noexcept { 132 | return execute_; 133 | } 134 | }; 135 | 136 | class on_fail_policy { 137 | int ec_; 138 | 139 | public: 140 | explicit on_fail_policy(bool execute) noexcept : ec_{execute ? uncaught_exceptions() : -1} {} 141 | 142 | void dismiss() noexcept { 143 | ec_ = -1; 144 | } 145 | 146 | bool should_execute() const noexcept { 147 | return ec_ != -1 && ec_ < uncaught_exceptions(); 148 | } 149 | }; 150 | 151 | class on_success_policy { 152 | int ec_; 153 | 154 | public: 155 | explicit on_success_policy(bool execute) noexcept : ec_{execute ? uncaught_exceptions() : -1} {} 156 | 157 | void dismiss() noexcept { 158 | ec_ = -1; 159 | } 160 | 161 | bool should_execute() const noexcept { 162 | return ec_ != -1 && ec_ >= uncaught_exceptions(); 163 | } 164 | }; 165 | 166 | template 167 | struct is_noarg_returns_void_action 168 | : std::false_type {}; 169 | 170 | template 171 | struct is_noarg_returns_void_action())())> 172 | : std::true_type {}; 173 | 174 | template ::value> 175 | struct is_nothrow_invocable_action 176 | : std::false_type {}; 177 | 178 | template 179 | struct is_nothrow_invocable_action 180 | : std::integral_constant())())> {}; 181 | 182 | template 183 | class scope_guard { 184 | using A = typename std::decay::type; 185 | 186 | static_assert(is_noarg_returns_void_action::value, 187 | "scope_guard requires no-argument action, that returns void."); 188 | static_assert(std::is_same::value || std::is_same::value || std::is_same::value, 189 | "scope_guard requires on_exit_policy, on_fail_policy or on_success_policy."); 190 | #if defined(SCOPE_GUARD_NO_THROW_ACTION) 191 | static_assert(is_nothrow_invocable_action::value, 192 | "scope_guard requires noexcept invocable action."); 193 | #endif 194 | #if defined(SCOPE_GUARD_NO_THROW_CONSTRUCTIBLE) 195 | static_assert(std::is_nothrow_move_constructible::value, 196 | "scope_guard requires nothrow constructible action."); 197 | #endif 198 | 199 | P policy_; 200 | A action_; 201 | 202 | void* operator new(std::size_t) = delete; 203 | void operator delete(void*) = delete; 204 | 205 | public: 206 | scope_guard() = delete; 207 | scope_guard(const scope_guard&) = delete; 208 | scope_guard& operator=(const scope_guard&) = delete; 209 | scope_guard& operator=(scope_guard&&) = delete; 210 | 211 | scope_guard(scope_guard&& other) noexcept(std::is_nothrow_move_constructible::value) 212 | : policy_{false}, 213 | action_{NEARGYE_MOV(other.action_)} { 214 | policy_ = NEARGYE_MOV(other.policy_); 215 | other.policy_.dismiss(); 216 | } 217 | 218 | scope_guard(const A& action) = delete; 219 | scope_guard(A& action) = delete; 220 | 221 | explicit scope_guard(A&& action) noexcept(std::is_nothrow_move_constructible::value) 222 | : policy_{true}, 223 | action_{NEARGYE_MOV(action)} {} 224 | 225 | void dismiss() noexcept { 226 | policy_.dismiss(); 227 | } 228 | 229 | ~scope_guard() NEARGYE_NOEXCEPT(is_nothrow_invocable_action::value) { 230 | if (policy_.should_execute()) { 231 | NEARGYE_TRY 232 | action_(); 233 | NEARGYE_CATCH 234 | } 235 | } 236 | }; 237 | 238 | template 239 | using scope_exit = scope_guard; 240 | 241 | template ::value, int>::type = 0> 242 | NEARGYE_NODISCARD scope_exit make_scope_exit(F&& action) noexcept(noexcept(scope_exit{NEARGYE_FWD(action)})) { 243 | return scope_exit{NEARGYE_FWD(action)}; 244 | } 245 | 246 | template 247 | using scope_fail = scope_guard; 248 | 249 | template ::value, int>::type = 0> 250 | NEARGYE_NODISCARD scope_fail make_scope_fail(F&& action) noexcept(noexcept(scope_fail{NEARGYE_FWD(action)})) { 251 | return scope_fail{NEARGYE_FWD(action)}; 252 | } 253 | 254 | template 255 | using scope_success = scope_guard; 256 | 257 | template ::value, int>::type = 0> 258 | NEARGYE_NODISCARD scope_success make_scope_success(F&& action) noexcept(noexcept(scope_success{NEARGYE_FWD(action)})) { 259 | return scope_success{NEARGYE_FWD(action)}; 260 | } 261 | 262 | struct scope_exit_tag {}; 263 | 264 | template ::value, int>::type = 0> 265 | scope_exit operator<<(scope_exit_tag, F&& action) noexcept(noexcept(scope_exit{NEARGYE_FWD(action)})) { 266 | return scope_exit{NEARGYE_FWD(action)}; 267 | } 268 | 269 | struct scope_fail_tag {}; 270 | 271 | template ::value, int>::type = 0> 272 | scope_fail operator<<(scope_fail_tag, F&& action) noexcept(noexcept(scope_fail{NEARGYE_FWD(action)})) { 273 | return scope_fail{NEARGYE_FWD(action)}; 274 | } 275 | 276 | struct scope_success_tag {}; 277 | 278 | template ::value, int>::type = 0> 279 | scope_success operator<<(scope_success_tag, F&& action) noexcept(noexcept(scope_success{NEARGYE_FWD(action)})) { 280 | return scope_success{NEARGYE_FWD(action)}; 281 | } 282 | 283 | #undef NEARGYE_MOV 284 | #undef NEARGYE_FWD 285 | #undef NEARGYE_NOEXCEPT 286 | #undef NEARGYE_TRY 287 | #undef NEARGYE_CATCH 288 | #undef NEARGYE_NODISCARD 289 | 290 | } // namespace scope_guard::detail 291 | 292 | using detail::make_scope_exit; 293 | using detail::make_scope_fail; 294 | using detail::make_scope_success; 295 | 296 | } // namespace scope_guard 297 | 298 | // NEARGYE_MAYBE_UNUSED suppresses compiler warnings on unused entities, if any. 299 | #if !defined(NEARGYE_MAYBE_UNUSED) 300 | # if defined(__clang__) 301 | # if (__clang_major__ * 10 + __clang_minor__) >= 39 && __cplusplus >= 201703L 302 | # define NEARGYE_MAYBE_UNUSED [[maybe_unused]] 303 | # else 304 | # define NEARGYE_MAYBE_UNUSED __attribute__((__unused__)) 305 | # endif 306 | # elif defined(__GNUC__) 307 | # if __GNUC__ >= 7 && __cplusplus >= 201703L 308 | # define NEARGYE_MAYBE_UNUSED [[maybe_unused]] 309 | # else 310 | # define NEARGYE_MAYBE_UNUSED __attribute__((__unused__)) 311 | # endif 312 | # elif defined(_MSC_VER) 313 | # if _MSC_VER >= 1911 && defined(_MSVC_LANG) && _MSVC_LANG >= 201703L 314 | # define NEARGYE_MAYBE_UNUSED [[maybe_unused]] 315 | # else 316 | # define NEARGYE_MAYBE_UNUSED __pragma(warning(suppress : 4100 4101 4189)) 317 | # endif 318 | # else 319 | # define NEARGYE_MAYBE_UNUSED 320 | # endif 321 | #endif 322 | 323 | #if !defined(NEARGYE_STR_CONCAT) 324 | # define NEARGYE_STR_CONCAT_(s1, s2) s1##s2 325 | # define NEARGYE_STR_CONCAT(s1, s2) NEARGYE_STR_CONCAT_(s1, s2) 326 | #endif 327 | 328 | #if !defined(NEARGYE_COUNTER) 329 | # if defined(__COUNTER__) 330 | # define NEARGYE_COUNTER __COUNTER__ 331 | # elif defined(__LINE__) 332 | # define NEARGYE_COUNTER __LINE__ 333 | # endif 334 | #endif 335 | 336 | #if defined(SCOPE_GUARD_NO_THROW_ACTION) 337 | # define NEARGYE_MAKE_SCOPE_GUARD_ACTION [&]() noexcept -> void 338 | #else 339 | # define NEARGYE_MAKE_SCOPE_GUARD_ACTION [&]() -> void 340 | #endif 341 | 342 | #define NEARGYE_MAKE_SCOPE_EXIT ::scope_guard::detail::scope_exit_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION 343 | #define NEARGYE_MAKE_SCOPE_FAIL ::scope_guard::detail::scope_fail_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION 344 | #define NEARGYE_MAKE_SCOPE_SUCCESS ::scope_guard::detail::scope_success_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION 345 | 346 | #define NEARGYE_SCOPE_GUARD_WITH_(g, i) for (int i = 1; i--; g) 347 | #define NEARGYE_SCOPE_GUARD_WITH(g) NEARGYE_SCOPE_GUARD_WITH_(g, NEARGYE_STR_CONCAT(NEARGYE_INTERNAL_OBJECT_, NEARGYE_COUNTER)) 348 | 349 | // SCOPE_EXIT executing action on scope exit. 350 | #define MAKE_SCOPE_EXIT(name) auto name = NEARGYE_MAKE_SCOPE_EXIT 351 | #define SCOPE_EXIT NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_EXIT(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_EXIT_, NEARGYE_COUNTER)) 352 | #define WITH_SCOPE_EXIT(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_EXIT{ guard }) 353 | 354 | // SCOPE_FAIL executing action on scope exit when an exception has been thrown before scope exit. 355 | #define MAKE_SCOPE_FAIL(name) auto name = NEARGYE_MAKE_SCOPE_FAIL 356 | #define SCOPE_FAIL NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_FAIL(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_FAIL_, NEARGYE_COUNTER)) 357 | #define WITH_SCOPE_FAIL(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_FAIL{ guard }) 358 | 359 | // SCOPE_SUCCESS executing action on scope exit when no exceptions have been thrown before scope exit. 360 | #define MAKE_SCOPE_SUCCESS(name) auto name = NEARGYE_MAKE_SCOPE_SUCCESS 361 | #define SCOPE_SUCCESS NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_SUCCESS(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_SUCCESS_, NEARGYE_COUNTER)) 362 | #define WITH_SCOPE_SUCCESS(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_SUCCESS{ guard }) 363 | 364 | // DEFER executing action on scope exit. 365 | #define MAKE_DEFER(name) MAKE_SCOPE_EXIT(name) 366 | #define DEFER SCOPE_EXIT 367 | #define WITH_DEFER(guard) WITH_SCOPE_EXIT(guard) 368 | 369 | #endif // NEARGYE_SCOPE_GUARD_HPP 370 | --------------------------------------------------------------------------------