├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── examples ├── 0_simplest_example.cpp ├── 1_custom_app_return_codes.cpp ├── 1_win32_hresult_boundary_function_example.cpp ├── 2_custom_app_return_codes_with_messages.cpp ├── 2_mfc_exception_handling.cpp ├── 3_lippincott_blog_post_example.cpp ├── 4_multi_catch.cpp ├── 5_handler_from_function.cpp └── CMakeLists.txt ├── include ├── common_handlers.hpp ├── generic_exception_handler.hpp └── seh_handlers.hpp ├── mittens_icon.png └── tests ├── all_catcher_test.cpp ├── bad_alloc_handler_test.cpp ├── exception_generators.hpp ├── generic_exception_handler_call_arity_tests.cpp ├── generic_exception_handler_test.cpp ├── handler_composition_test.cpp ├── main.cpp ├── seh_converter_test.cpp └── std_exception_handler_test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (Mittens) 2 | cmake_minimum_required(VERSION 2.6) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | set(CMAKE_CXX_STANDARD_REQUIRED on) 6 | 7 | # Find and add Boost for Boost.Test 8 | find_package(Boost REQUIRED) 9 | include_directories(${Boost_INCLUDE_DIR}) 10 | link_directories(${Boost_LIBRARY_DIRS}) 11 | 12 | include_directories(include) 13 | 14 | file(GLOB include_files include/*.hpp) 15 | 16 | add_executable (unit_tests ${include_files} 17 | tests/generic_exception_handler_call_arity_tests.cpp 18 | tests/generic_exception_handler_test.cpp 19 | tests/std_exception_handler_test.cpp 20 | tests/all_catcher_test.cpp 21 | tests/handler_composition_test.cpp 22 | tests/bad_alloc_handler_test.cpp 23 | tests/exception_generators.hpp 24 | tests/main.cpp) 25 | 26 | 27 | target_link_libraries(unit_tests ${Boost_LIBRARIES}) 28 | 29 | 30 | if(MSVC) 31 | add_definitions(/D_CRT_SECURE_NO_WARNINGS) 32 | add_definitions(/D_SCL_SECURE_NO_WARNINGS) 33 | add_definitions(/DNOMINMAX) 34 | endif(MSVC) 35 | 36 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 37 | 38 | add_subdirectory (examples) 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, adishavit 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![icon](mittens_icon.png) Mittens 2 | ======= 3 | 4 | Mittens is a cross-platform, C++ header-only library for centralized handling of exceptions, particularly along module boundaries. 5 | 6 | Robust software needs to be able to handle exceptions as gracefully as possible. This might include last-minute error logging, alternate notification mechanisms (e.g. via the JVM) etc. 7 | 8 | An unexpected exit out of `main()` or an exception escaping from a DLL, COM or JNI function call can cause severe havoc. Module boundaries are stress-points where escaped exceptions should be firmly handled. Dynamically linked libraries (DLLs) export multiple C-like functions forming the dynamic module boundaries. Here, in particular, we would like a uniform and consistent way of handling exceptions. This often requires a lot of copy-paste code since try-catch statements do not naturally compose nor can they be easily encapsulated. 9 | 10 | Mittens is a generic header-only library that allows `try-catch` statement composition, ordering and nesting with customizable actions per exception type. These composite exception handlers can be defined once and reused multiple times where needed. 11 | 12 | ![icon](mittens_icon.png) Overview 13 | ----- 14 | #### Features 15 | - Centralized, uniform, maintainable, consistent and composable exception handling; 16 | - Support both portable cross-platform and platform-specific code; 17 | - Write robust, well ordered, exception-type-specific custom code; 18 | - Particularly useful along module boundaries: e.g command-line applications and dynamically loaded libraries (DLLs, shared-object `.so`, Android JNI, etc.) 19 | 20 | #### TL;DR: Show Me the Code! 21 | ```cpp 22 | #include "common_handlers.hpp" 23 | 24 | auto handleIt = mittens::all_catcher (-1, []() { std::cerr << "Caught exception of unknown type!" << std::endl; }, 25 | mittens::std_exception_handler (-2, [](auto& e){ std::cerr << "Caught std::exception: " << e.what() << std::endl; }, 26 | mittens::generic_handler(-3, [](auto& e){ std::cerr << "Caught thrown `int` = " << e << std::endl; }))); 27 | 28 | int main() try 29 | { 30 | throw std::runtime_error("Goodbye World, Hello Mittens!"); 31 | return EXIT_SUCCESS; 32 | } 33 | catch (...) 34 | { return handleIt(); } 35 | ``` 36 | Application prints: `Caught std::exception: Goodbye World, Hello Mittens!` and the app return value will be `-2`. 37 | 38 | **See the [Tutorial](https://github.com/adishavit/mittens/wiki/The-Mittens-Tutorial) for more.** 39 | 40 | #### Integration 41 | - Single header file `generic_exception_handler.hpp` + optional helper header (`common_handlers.hpp`). 42 | - Cross platform: Windows, Linux OSX; 43 | - No external dependencies (but unit tests use Boost.Test); 44 | - BSD License 45 | 46 | #### Epilogue 47 | - Mittens grew out of the need to robustly and consistently handle exceptions across module boundaries in Windows DLLs and Android JNIs. 48 | - The first version was written in 2013. This repo is a complete rewrite to allow for much more genericity. 49 | - Although the design is very different, Mittens was originally inspired by Matthew Wilson's Quality Matters articles ([QM#6](http://accu.org/index.php/journals/1706), [QM#7](http://accu.org/index.php/articles/1868)). 50 | - Check out the `examples` folder for more examples of how Mittens can help you and more advanced usage. 51 | - Comments, discussions, questions, ideas and PRs are most welcome! 52 | 53 | -------------------------------------------------------------------------------- /examples/0_simplest_example.cpp: -------------------------------------------------------------------------------- 1 | #include "common_handlers.hpp" 2 | #include "exception_generators.hpp" 3 | 4 | // This example shows the simplest usage for catching any unhandled exception thrown from inside main() 5 | // If such an exception is thrown, then the program returns EXIT_FAILURE, otherwise it returns EXIT_SUCCESS. 6 | 7 | #if 1 8 | 9 | ////////////////////////////////////////////////////////////////////////// 10 | // Function-try-block style 11 | 12 | int main() try // Function try block 13 | { 14 | throw_std_exception(); 15 | return EXIT_SUCCESS; 16 | } 17 | catch (...) 18 | { 19 | return mittens::all_catcher(EXIT_FAILURE).handleException(); 20 | } 21 | 22 | #else 23 | 24 | ////////////////////////////////////////////////////////////////////////// 25 | // Internal try-catch block 26 | 27 | int main() 28 | { 29 | try 30 | { 31 | throw_std_exception(); 32 | return EXIT_SUCCESS; 33 | } 34 | catch (...) 35 | { 36 | return mittens::all_catcher(EXIT_FAILURE).handleException(); 37 | } 38 | return EXIT_FAILURE; // should never reach here 39 | } 40 | 41 | #endif -------------------------------------------------------------------------------- /examples/1_custom_app_return_codes.cpp: -------------------------------------------------------------------------------- 1 | #include "common_handlers.hpp" 2 | #include "exception_generators.hpp" 3 | 4 | int main() try // Function try block 5 | { 6 | throw_std_exception(); 7 | return EXIT_SUCCESS; 8 | } 9 | catch (...) 10 | { 11 | using namespace mittens; 12 | return all_catcher(-1, std_exception_handler(-2, bad_alloc_handler(-3))).handleException(); 13 | } -------------------------------------------------------------------------------- /examples/1_win32_hresult_boundary_function_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common_handlers.hpp" 5 | #include "exception_generators.hpp" 6 | 7 | using namespace mittens; 8 | 9 | // Example based on this blog post: http://blogs.msdn.com/b/vcblog/archive/2014/01/16/exception-boundaries.aspx 10 | 11 | /* 12 | From the article: 13 | For discussion purposes, let's say that our library uses its own exception type internally for failures, 14 | named my_hresult_error. In addition, our library makes use of new and delete, so we may also need to handle 15 | std::bad_alloc at the boundary. We don't expect any exceptions other than these at the boundary, so 16 | for all other exceptions, we want to immediately terminate because we do not know what the state of the system is. 17 | */ 18 | struct my_hresult_error 19 | { 20 | my_hresult_error(HRESULT const& hres = E_FAIL): hres_(hres) {} 21 | HRESULT hresult() const { return hres_; } 22 | private: 23 | HRESULT hres_; 24 | }; 25 | 26 | auto boundaryExceptionGuard = all_catcher(E_UNEXPECTED, [](){ std::terminate(); }, 27 | generic_handler(E_UNEXPECTED, [](my_hresult_error const& ex) { return ex.hresult(); }, // In case of my_hresult_error exception, return custom fail-code 28 | std_exception_handler(E_UNEXPECTED, 29 | bad_alloc_handler(E_OUTOFMEMORY)))); 30 | 31 | ////////////////////////////////////////////////////////////////////////// 32 | // First function 33 | extern "C" HRESULT boundary_function() try // Function try block 34 | { 35 | throw_std_exception(); // ... code that throws ... 36 | return S_OK; 37 | } 38 | catch (...) { return boundaryExceptionGuard(); }; 39 | 40 | 41 | ////////////////////////////////////////////////////////////////////////// 42 | // Second function which uses the same handler 43 | extern "C" HRESULT another_boundary_function() try // Function try block 44 | { 45 | throw_bad_alloc(); // ... code that throws ... 46 | return S_OK; 47 | } 48 | catch (...) { return boundaryExceptionGuard(); }; 49 | 50 | 51 | ////////////////////////////////////////////////////////////////////////// 52 | // Third function which uses the same handler 53 | extern "C" HRESULT yet_another_boundary_function() try // Function try block 54 | { 55 | throw my_hresult_error(); 56 | } 57 | catch (...) { return boundaryExceptionGuard(); }; 58 | 59 | 60 | ////////////////////////////////////////////////////////////////////////// 61 | 62 | int main() 63 | { 64 | using namespace std; 65 | 66 | cout << _com_error(boundary_function()).ErrorMessage() << endl; 67 | cout << _com_error(another_boundary_function()).ErrorMessage() << endl; 68 | cout << _com_error(yet_another_boundary_function()).ErrorMessage() << endl; 69 | 70 | return EXIT_SUCCESS; 71 | } 72 | -------------------------------------------------------------------------------- /examples/2_custom_app_return_codes_with_messages.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common_handlers.hpp" 3 | #include "exception_generators.hpp" 4 | 5 | int main() try // Function try block 6 | { 7 | //throw_int(); 8 | throw_std_exception(); 9 | //throw_bad_alloc(); 10 | 11 | return EXIT_SUCCESS; 12 | } 13 | catch (...) 14 | { 15 | using namespace mittens; 16 | return all_catcher( -1, []() { std::cout << "Caught unhandled exception of unknown type!" << std::endl; }, 17 | std_exception_handler(-2, [](auto& e){ std::cout << "Caught unhandled std::exception: " << e.what() << std::endl; }, 18 | bad_alloc_handler( -3, [](auto& e){ std::cout << "Caught unhandled std::bad_alloc: " << e.what() << std::endl; }))). 19 | handleException(); 20 | } -------------------------------------------------------------------------------- /examples/2_mfc_exception_handling.cpp: -------------------------------------------------------------------------------- 1 | // Example to show how to handle MFC exception derived from CException 2 | #define _AFXDLL 3 | 4 | #include 5 | #include 6 | #include "common_handlers.hpp" 7 | 8 | int main() try // Function try block 9 | { 10 | AfxThrowMemoryException(); 11 | return EXIT_SUCCESS; 12 | } 13 | catch (...) 14 | { 15 | using namespace mittens; 16 | return all_catcher(-1, 17 | std_exception_handler(-2, 18 | generic_handler(-3, [](CException* e) { e->Delete(); }, // Release CException object to prevent a leak 19 | bad_alloc_handler(-4, 20 | generic_handler(-5, [](CMemoryException* e) { e->Delete(); } // Release CException object to prevent a leak 21 | ))))).handleException(); 22 | } 23 | -------------------------------------------------------------------------------- /examples/3_lippincott_blog_post_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common_handlers.hpp" 3 | 4 | // Example based on this blog post Using a Lippincott Function for Centralized Exception Handling: 5 | // http://cppsecrets.blogspot.ca/2013/12/using-lippincott-function-for.html 6 | // 7 | // Original code was here: http://ideone.com/m2ZfHN 8 | 9 | #include 10 | 11 | typedef enum foo_Result { 12 | FOO_OK, 13 | FOO_ERROR1, 14 | FOO_ERROR2, 15 | FOO_UNKNOWN 16 | } foo_Result; 17 | 18 | struct MyException1 { }; 19 | struct MyException2 { }; 20 | 21 | 22 | using namespace mittens; 23 | auto lippincott = all_catcher(FOO_UNKNOWN, 24 | generic_handler(FOO_ERROR2, 25 | generic_handler(FOO_ERROR1))); 26 | 27 | ////////////////////////// 28 | 29 | void Snafuscate(bool andDie) 30 | { 31 | if (andDie) 32 | { 33 | throw MyException1(); 34 | } 35 | } 36 | 37 | foo_Result foo_snafuscate(bool andDie) 38 | { 39 | try 40 | { 41 | Snafuscate(andDie); 42 | return FOO_OK; 43 | } 44 | catch (...) 45 | { 46 | return lippincott(); 47 | } 48 | } 49 | 50 | #include 51 | 52 | int main() 53 | { 54 | foo_Result r1 = foo_snafuscate(false); 55 | if (r1 == FOO_OK) std::cout << "r1 == FOO_OK\n"; 56 | 57 | foo_Result r2 = foo_snafuscate(true); 58 | if (r2 != FOO_OK) std::cout << "r2 != FOO_OK (r2 == " << r2 << ")\n"; 59 | } 60 | -------------------------------------------------------------------------------- /examples/4_multi_catch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common_handlers.hpp" 3 | #include "exception_generators.hpp" 4 | 5 | ////////////////////////////////////////////////////////////////////////// 6 | // Define exception handler for logging only. 7 | // It will catch any exception, log that it happened, and then throw a std::runtime_error instead for 8 | // external handlers to catch and handle. 9 | auto loggingHandler = mittens::all_catcher( 0xBad, []() 10 | { 11 | std::cout << "LOG: Caught exception!" << std::endl; 12 | throw std::runtime_error("Unknown exception converted to std::runtime_error()!"); 13 | }, 14 | false); // Prevent (default) custom-action exception-suppression. Allow std::runtime_error to propogate. 15 | 16 | void f() try 17 | { 18 | throw_int(); 19 | } 20 | catch (...) 21 | { 22 | loggingHandler.handleException(); 23 | } 24 | 25 | 26 | ////////////////////////////////////////////////////////////////////////// 27 | 28 | int main() try // Function try block 29 | { 30 | f(); 31 | return EXIT_SUCCESS; 32 | } 33 | catch (...) 34 | { 35 | // Define a separate exception handler un-related to 'loggingHandler' to handle various exceptions. 36 | using namespace mittens; 37 | return all_catcher( -1, []() { std::cout << "Caught unhandled exception of unknown type!" << std::endl; }, 38 | std_exception_handler(-2, [](std::exception&e) { std::cout << "Caught unhandled std::exception: " << e.what() << std::endl; }, 39 | bad_alloc_handler( 0xBadA110c, [](std::bad_alloc&e){ std::cout << "Caught unhandled std::bad_alloc: " << e.what() << std::endl; }))). 40 | handleException(); 41 | } 42 | -------------------------------------------------------------------------------- /examples/5_handler_from_function.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common_handlers.hpp" 3 | #include "exception_generators.hpp" 4 | #include 5 | 6 | struct JNIEnv 7 | { 8 | int foo = 42; 9 | }; 10 | 11 | auto getHandler(JNIEnv* jvm) 12 | { 13 | using namespace mittens; 14 | return all_catcher(false, [jvm]() 15 | { 16 | // __android_log_print(ANDROID_LOG_ERROR,"MY_JNI","Unknown exception"); 17 | // throw_JavaException(jvm, "Unknown exception"); 18 | std::cerr << jvm->foo << std::endl; 19 | }, 20 | std_exception_handler(false, [jvm](auto &e) 21 | { 22 | // __android_log_print(ANDROID_LOG_ERROR,"MY_JNI","std::exception exception caught: '%s'",e.what()); 23 | // throw_JavaException(jvm, e.what()); 24 | std::cerr << jvm->foo << std::endl; 25 | })); 26 | }; 27 | 28 | bool f(JNIEnv* jvm) try 29 | { 30 | throw_int(); 31 | return true; 32 | } 33 | catch (...) 34 | { 35 | return getHandler(jvm).handleException(); 36 | } 37 | 38 | bool g(JNIEnv* jvm) try 39 | { 40 | throw_std_exception(); 41 | return true; 42 | } 43 | catch (...) 44 | { 45 | return getHandler(jvm).handleException(); 46 | } 47 | 48 | 49 | ////////////////////////////////////////////////////////////////////////// 50 | 51 | int main() 52 | { 53 | JNIEnv jvm; 54 | std::cout << std::boolalpha << f(&jvm) << std::endl << g(&jvm) << std::endl; 55 | return EXIT_SUCCESS; 56 | } 57 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | include_directories(${Boost_INCLUDE_DIR}) 3 | 4 | include_directories(../include) 5 | include_directories(../tests) 6 | 7 | 8 | add_executable (0_simplest_example 0_simplest_example.cpp) 9 | set_property(TARGET 0_simplest_example PROPERTY FOLDER "examples/general") 10 | 11 | add_executable (1_custom_app_return_codes 1_custom_app_return_codes.cpp) 12 | set_property(TARGET 1_custom_app_return_codes PROPERTY FOLDER "examples/general") 13 | 14 | add_executable (2_custom_app_return_codes_with_messages 2_custom_app_return_codes_with_messages.cpp) 15 | set_property(TARGET 2_custom_app_return_codes_with_messages PROPERTY FOLDER "examples/general") 16 | 17 | add_executable (3_lippincott_blog_post_example 3_lippincott_blog_post_example.cpp) 18 | set_property(TARGET 3_lippincott_blog_post_example PROPERTY FOLDER "examples/general") 19 | 20 | add_executable (4_multi_catch 4_multi_catch.cpp) 21 | set_property(TARGET 4_multi_catch PROPERTY FOLDER "examples/general") 22 | 23 | add_executable (5_handler_from_function 5_handler_from_function.cpp) 24 | set_property(TARGET 5_handler_from_function PROPERTY FOLDER "examples/general") 25 | 26 | 27 | if(MSVC) 28 | add_executable (1_win32_hresult_boundary_function_example 1_win32_hresult_boundary_function_example.cpp) 29 | set_property(TARGET 1_win32_hresult_boundary_function_example PROPERTY FOLDER "examples/windows") 30 | 31 | add_executable (2_mfc_exception_handling 2_mfc_exception_handling.cpp) 32 | set_property(TARGET 2_mfc_exception_handling PROPERTY FOLDER "examples/windows") 33 | 34 | # add_executable (3_seh_exception_handling 3_seh_exception_handling.cpp) 35 | # set_property(TARGET 3_seh_exception_handling PROPERTY FOLDER "examples/windows") 36 | 37 | endif(MSVC) 38 | 39 | 40 | if(MSVC) 41 | add_definitions(/D_CRT_SECURE_NO_WARNINGS) 42 | add_definitions(/D_SCL_SECURE_NO_WARNINGS) 43 | add_definitions(/DNOMINMAX) 44 | endif(MSVC) 45 | -------------------------------------------------------------------------------- /include/common_handlers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "generic_exception_handler.hpp" 6 | 7 | namespace mittens 8 | { 9 | ////////////////////////////////////////////////////////////////////////// 10 | // Catch-all handler maker 11 | template 12 | inline auto all_catcher(typename NestedHandler::FailCodeType failCode, Callable customAction, NestedHandler const& nestedHandler, bool supressExceptionsInAction = true) 13 | { return generic_handler(failCode, customAction, nestedHandler, supressExceptionsInAction); } 14 | 15 | template 16 | inline auto all_catcher(FailCodeType failCode, Callable func, bool supressExceptionsInAction = true) 17 | { return generic_handler(failCode, func, supressExceptionsInAction); } 18 | 19 | template 20 | inline auto all_catcher(FailCodeType failCode) 21 | { return generic_handler(failCode); } 22 | 23 | ////////////////////////////////////////////////////////////////////////// 24 | // std::exception handler maker 25 | template 26 | inline auto std_exception_handler(typename NestedHandler::FailCodeType failCode, Callable customAction, NestedHandler const& nestedHandler, bool supressExceptionsInAction = true) 27 | { return generic_handler(failCode, customAction, nestedHandler, supressExceptionsInAction); } 28 | 29 | template 30 | inline auto std_exception_handler(FailCodeType failCode, Callable func, bool supressExceptionsInAction = true) 31 | { return generic_handler(failCode, func, supressExceptionsInAction); } 32 | 33 | template 34 | inline auto std_exception_handler(FailCodeType failCode) 35 | { return generic_handler(failCode); } 36 | 37 | ////////////////////////////////////////////////////////////////////////// 38 | // std::bad_alloc handler maker 39 | template 40 | inline auto bad_alloc_handler(typename NestedHandler::FailCodeType failCode, Callable customAction, NestedHandler const& nestedHandler, bool supressExceptionsInAction = true) 41 | { return generic_handler(failCode, customAction, nestedHandler, supressExceptionsInAction); } 42 | 43 | template 44 | inline auto bad_alloc_handler(FailCodeType failCode, Callable func, bool supressExceptionsInAction = true) 45 | { return generic_handler(failCode, func, supressExceptionsInAction); } 46 | 47 | template 48 | inline auto bad_alloc_handler(FailCodeType failCode) 49 | { return generic_handler(failCode); } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /include/generic_exception_handler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include // for std::is_same<> 5 | #include 6 | #include // for std::false_type/std::true_type 7 | #include // for std::is_same<> 8 | 9 | namespace mittens 10 | { 11 | ////////////////////////////////////////////////////////////////////////// 12 | 13 | template 14 | class UnHandler 15 | { 16 | public: 17 | typedef FailCodeType_ FailCodeType; 18 | 19 | FailCodeType handleException() 20 | { 21 | // Instead of a call to `throw` we will check if the function was called inside a 22 | // catch clause. If so, `std::rethrow_exception(eptr)` is the same as `throw`. 23 | // Otherwise, throw an `std::logic_error` since this is obviously a bug. 24 | if (std::exception_ptr eptr = std::current_exception()) 25 | std::rethrow_exception(eptr); 26 | 27 | throw std::logic_error("Mittens handler called outside of a catch clause! This is a BUG."); 28 | } 29 | FailCodeType operator()() { return handleException(); } 30 | }; 31 | 32 | 33 | ////////////////////////////////////////////////////////////////////////// 34 | 35 | // Helper default no-action type 36 | template 37 | struct DefaultNoAction 38 | { void operator()(ExceptionType&){} }; 39 | 40 | template <> 41 | struct DefaultNoAction 42 | { void operator()(){} }; 43 | 44 | ////////////////////////////////////////////////////////////////////////// 45 | 46 | template > 47 | class GenericExceptionHandler 48 | { 49 | public: 50 | typedef typename NestedHandler::FailCodeType FailCodeType; 51 | 52 | GenericExceptionHandler(FailCodeType failCode, NestedHandler const& nestedHandler, Callable customAction = Callable(), bool supressExceptionsInAction = true): 53 | failCode_(failCode), 54 | nestedHandler(nestedHandler), 55 | customAction_(customAction), 56 | supressExceptionsInAction_(supressExceptionsInAction) 57 | {} 58 | 59 | FailCodeType operator()() { return handleException(); } 60 | FailCodeType handleException() 61 | { 62 | // Handle the general non-catch-all case 63 | static_assert(!std::is_same::value, "Something wrong: specialization failed. ExceptionType is 'void'"); 64 | try 65 | { 66 | return nestedHandler(); 67 | } 68 | catch (ExceptionType& e) 69 | { 70 | try 71 | { 72 | // Run custom action, passing it 'e' 73 | // If the return type of the custom action is the same as FailCodeType (as opposed to void or even less plausible something else) 74 | // then return the result of custom action as the fail code. 75 | using ReturnActionResult = typename std::is_same< FailCodeType, decltype(customAction_(e))>::type; 76 | return runAction(e, ReturnActionResult{}); 77 | } 78 | catch (...) 79 | { 80 | // Force the action no-throw by suppressing any exceptions unless requested not to. 81 | if (!supressExceptionsInAction_) 82 | throw; 83 | } 84 | return failCode_; 85 | } 86 | } 87 | 88 | private: 89 | FailCodeType runAction(ExceptionType& e, std::true_type) 90 | { 91 | return customAction_(e); 92 | } 93 | 94 | FailCodeType runAction(ExceptionType& e, std::false_type) 95 | { 96 | customAction_(e); 97 | return failCode_; 98 | } 99 | 100 | private: 101 | FailCodeType failCode_; 102 | NestedHandler nestedHandler; 103 | Callable customAction_; 104 | bool supressExceptionsInAction_; 105 | }; 106 | 107 | ////////////////////////////////////////////////////////////////////////// 108 | // Specialize for the catch-all (...) case. Indicated by ExceptionType being void 109 | template 110 | class GenericExceptionHandler 111 | { 112 | public: 113 | typedef typename NestedHandler::FailCodeType FailCodeType; 114 | 115 | GenericExceptionHandler(FailCodeType failCode, NestedHandler const& nestedHandler, Callable customAction = Callable(), bool supressExceptionsInAction = true) : 116 | failCode_(failCode), 117 | nestedHandler(nestedHandler), 118 | customAction_(customAction), 119 | supressExceptionsInAction_(supressExceptionsInAction) 120 | {} 121 | 122 | FailCodeType operator()() { return handleException(); } 123 | FailCodeType handleException() 124 | { 125 | try 126 | { 127 | return nestedHandler(); 128 | } 129 | catch (...) 130 | { 131 | try 132 | { 133 | // Run custom action. 134 | // If the return type of the custom action is the same as FailCodeType (as opposed to void or even less plausible something else) 135 | // then return the result of custom action as the fail code. 136 | using ReturnActionResult = typename std::is_same< FailCodeType, decltype(customAction_())>::type; 137 | return runAction(ReturnActionResult{}); 138 | } 139 | catch (...) 140 | { 141 | // Force the action no-throw by suppressing any exceptions unless requested not to. 142 | if (!supressExceptionsInAction_) 143 | throw; 144 | } 145 | return failCode_; 146 | } 147 | } 148 | 149 | private: 150 | FailCodeType runAction(std::true_type) 151 | { 152 | return customAction_(); 153 | } 154 | 155 | FailCodeType runAction(std::false_type) 156 | { 157 | customAction_(); 158 | return failCode_; 159 | } 160 | 161 | private: 162 | FailCodeType failCode_; 163 | NestedHandler nestedHandler; 164 | Callable customAction_; 165 | bool supressExceptionsInAction_; 166 | }; 167 | 168 | 169 | template 170 | inline auto generic_handler(typename NestedHandler::FailCodeType failCode, Callable customAction, NestedHandler const& nestedHandler, bool supressExceptionsInAction = true) 171 | { return GenericExceptionHandler(failCode, nestedHandler, customAction, supressExceptionsInAction); } 172 | 173 | namespace 174 | { 175 | namespace detail 176 | { 177 | ////////////////////////////////////////////////////////////////////////// 178 | // Helper functions for SFINAE overload resolution 179 | template< typename ... Ts > 180 | using void_t = void; 181 | 182 | template< typename T, typename = void > 183 | struct hasNestedFailCodeType : std::false_type {}; 184 | 185 | template< typename T > 186 | struct hasNestedFailCodeType< T, void_t > : std::true_type {}; 187 | ////////////////////////////////////////////////////////////////////////// 188 | 189 | template 190 | inline auto generic_handler_impl(FailCodeType failCode, Callable customAction, bool supressExceptionsInAction, std::false_type) 191 | { return generic_handler(failCode, customAction, UnHandler(), supressExceptionsInAction); } 192 | 193 | template 194 | inline auto generic_handler_impl(FailCodeType failCode, NestedHandler const& nestedHandler, bool supressExceptionsInAction, std::true_type) 195 | { return generic_handler(failCode, DefaultNoAction(), nestedHandler, supressExceptionsInAction); } 196 | } 197 | } 198 | 199 | template 200 | inline auto generic_handler(FailCodeType code, Callable func, bool supressExceptionsInAction = true) 201 | { return detail::generic_handler_impl(code, func, supressExceptionsInAction, detail::hasNestedFailCodeType{}); } 202 | 203 | template 204 | inline auto generic_handler(FailCodeType failCode) 205 | { return generic_handler(failCode, DefaultNoAction(), UnHandler()); } 206 | } 207 | -------------------------------------------------------------------------------- /include/seh_handlers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | 10 | namespace 11 | { 12 | // from : http://stackoverflow.com/questions/3523716/is-there-a-function-to-convert-exception-pointers-struct-to-a-string 13 | // http://msdn.microsoft.com/en-us/library/windows/desktop/aa363082(v=vs.85).aspx 14 | static const char* seDescription( DWORD& code ) 15 | { 16 | switch( code ) 17 | { 18 | case EXCEPTION_ACCESS_VIOLATION: return "EXCEPTION_ACCESS_VIOLATION - The thread tried to read from or write to a virtual address for which it does not have the appropriate access."; 19 | case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED - The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking."; 20 | case EXCEPTION_BREAKPOINT: return "EXCEPTION_BREAKPOINT - A breakpoint was encountered."; 21 | case EXCEPTION_DATATYPE_MISALIGNMENT: return "EXCEPTION_DATATYPE_MISALIGNMENT - The thread tried to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries; 32-bit values on 4-byte boundaries, and so on."; 22 | case EXCEPTION_FLT_DENORMAL_OPERAND: return "EXCEPTION_FLT_DENORMAL_OPERAND - One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value."; 23 | case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "EXCEPTION_FLT_DIVIDE_BY_ZERO - The thread tried to divide a floating-point value by a floating-point divisor of zero."; 24 | case EXCEPTION_FLT_INEXACT_RESULT: return "EXCEPTION_FLT_INEXACT_RESULT - The result of a floating-point operation cannot be represented exactly as a decimal fraction."; 25 | case EXCEPTION_FLT_INVALID_OPERATION: return "EXCEPTION_FLT_INVALID_OPERATION - This exception represents any floating-point exception not included in this list."; 26 | case EXCEPTION_FLT_OVERFLOW: return "EXCEPTION_FLT_OVERFLOW - The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type."; 27 | case EXCEPTION_FLT_STACK_CHECK: return "EXCEPTION_FLT_STACK_CHECK - The stack overflowed or underflowed as the result of a floating-point operation."; 28 | case EXCEPTION_FLT_UNDERFLOW: return "EXCEPTION_FLT_UNDERFLOW - The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type."; 29 | case EXCEPTION_ILLEGAL_INSTRUCTION: return "EXCEPTION_ILLEGAL_INSTRUCTION - The thread tried to execute an invalid instruction."; 30 | case EXCEPTION_IN_PAGE_ERROR: return "EXCEPTION_IN_PAGE_ERROR - The thread tried to access a page that was not present, and the system was unable to load the page. For example, this exception might occur if a network connection is lost while running a program over the network."; 31 | case EXCEPTION_INT_DIVIDE_BY_ZERO: return "EXCEPTION_INT_DIVIDE_BY_ZERO - The thread tried to divide an integer value by an integer divisor of zero."; 32 | case EXCEPTION_INT_OVERFLOW: return "EXCEPTION_INT_OVERFLOW - The result of an integer operation caused a carry out of the most significant bit of the result."; 33 | case EXCEPTION_INVALID_DISPOSITION: return "EXCEPTION_INVALID_DISPOSITION - An exception handler returned an invalid disposition to the exception dispatcher. Programmers using a high-level language such as C should never encounter this exception."; 34 | case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "EXCEPTION_NONCONTINUABLE_EXCEPTION - The thread tried to continue execution after a noncontinuable exception occurred."; 35 | case EXCEPTION_PRIV_INSTRUCTION: return "EXCEPTION_PRIV_INSTRUCTION - The thread tried to execute an instruction whose operation is not allowed in the current machine mode."; 36 | case EXCEPTION_SINGLE_STEP: return "EXCEPTION_SINGLE_STEP - A trace trap or other single-instruction mechanism signaled that one instruction has been executed."; 37 | case EXCEPTION_STACK_OVERFLOW: return "EXCEPTION_STACK_OVERFLOW - The thread used up its stack."; 38 | default: return "UNKNOWN STRUCTURED EXCEPTION" ; 39 | } 40 | } 41 | } 42 | 43 | namespace mittens 44 | { 45 | class GlobalSEHExceptionConverter 46 | { 47 | public: 48 | GlobalSEHExceptionConverter() 49 | { _set_se_translator(trans_func); } // calling _set_se_translator() requires /EHa 50 | 51 | static void trans_func( unsigned int u, EXCEPTION_POINTERS* pExp ) 52 | { 53 | if (pExp && pExp->ExceptionRecord) 54 | throw std::runtime_error(std::string("SEH Exception: '") + seDescription(pExp->ExceptionRecord->ExceptionCode) + "'"); 55 | 56 | throw std::runtime_error("Unknown SEH Exception thrown!"); 57 | } 58 | }; 59 | 60 | 61 | // For use in nested initialization 62 | template 63 | class NestedSEHExceptionConverter: public GlobalSEHExceptionConverter 64 | { 65 | public: 66 | typedef typename NestedHandler::FailCodeType FailCodeType; 67 | 68 | NestedSEHExceptionConverter(NestedHandler const& nestedHandler): 69 | nestedHandler(nestedHandler) 70 | {} 71 | 72 | FailCodeType handleException() { return nestedHandler(); } 73 | FailCodeType operator()() { return handleException(); } 74 | 75 | private: 76 | NestedHandler nestedHandler; 77 | }; 78 | 79 | template 80 | inline NestedSEHExceptionConverter seh_to_std_exception_converter(NestedHandler const& nestedHandler) 81 | { return NestedSEHExceptionConverter(nestedHandler); } 82 | 83 | template 84 | inline NestedSEHExceptionConverter> seh_to_std_exception_converter() 85 | { return NestedSEHExceptionConverter>(UnHandler()); } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /mittens_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adishavit/mittens/8bd55304dbb881989168b9d9ca637e940ffb5e88/mittens_icon.png -------------------------------------------------------------------------------- /tests/all_catcher_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | ////////////////////////////////////////////////////////////////////////// 4 | // Tested API 5 | #include "common_handlers.hpp" 6 | 7 | 8 | ////////////////////////////////////////////////////////////////////////// 9 | #include "exception_generators.hpp" 10 | 11 | 12 | using namespace mittens; 13 | 14 | BOOST_AUTO_TEST_SUITE(AllCatcher_Tests) 15 | 16 | 17 | BOOST_AUTO_TEST_CASE(Test_that_all_catcher_works) 18 | { 19 | { 20 | auto catcher = all_catcher(EXIT_FAILURE, UnHandler()); 21 | try 22 | { 23 | throw_int(); 24 | } 25 | catch (...) 26 | { 27 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 28 | } 29 | } 30 | { 31 | auto catcher = all_catcher(EXIT_FAILURE); // default nest handler 32 | try 33 | { 34 | throw_int(); 35 | } 36 | catch (...) 37 | { 38 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 39 | } 40 | } 41 | 42 | } 43 | 44 | BOOST_AUTO_TEST_CASE(Test_that_all_catcher_with_differnt_fail_code_types_works) 45 | { 46 | { 47 | auto catcher = all_catcher(false); // use bool fail code type 48 | 49 | try 50 | { 51 | throw_int(); 52 | } 53 | catch (...) 54 | { 55 | BOOST_CHECK_EQUAL(false, catcher.handleException()); 56 | } 57 | } 58 | 59 | { 60 | auto catcher = all_catcher(0.7); // use float fail code type 61 | 62 | try 63 | { 64 | throw_int(); 65 | } 66 | catch (...) 67 | { 68 | BOOST_CHECK_EQUAL(0.7, catcher.handleException()); 69 | } 70 | } 71 | } 72 | 73 | BOOST_AUTO_TEST_CASE(Internal_catch_handler_all_catcher_works) 74 | { 75 | { 76 | try 77 | { 78 | throw_int(); 79 | } 80 | catch (...) 81 | { 82 | BOOST_CHECK_EQUAL(EXIT_FAILURE, all_catcher(EXIT_FAILURE, UnHandler()).handleException()); 83 | } 84 | } 85 | } 86 | 87 | BOOST_AUTO_TEST_CASE(Test_all_catcher_with_custom_action) 88 | { 89 | int hit = 0; 90 | { 91 | auto catcher = all_catcher(EXIT_FAILURE, [&hit](){++hit;}, UnHandler()); 92 | try 93 | { 94 | throw_int(); 95 | } 96 | catch (...) 97 | { 98 | BOOST_CHECK_EQUAL(0, hit); 99 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 100 | BOOST_CHECK_EQUAL(1, hit); 101 | } 102 | } 103 | { 104 | auto catcher = all_catcher(EXIT_FAILURE, [&hit](){++hit;}); // using default (null) handler 105 | try 106 | { 107 | throw_int(); 108 | } 109 | catch (...) 110 | { 111 | BOOST_CHECK_EQUAL(1, hit); 112 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 113 | BOOST_CHECK_EQUAL(2, hit); 114 | } 115 | } 116 | } 117 | 118 | 119 | BOOST_AUTO_TEST_SUITE_END() 120 | -------------------------------------------------------------------------------- /tests/bad_alloc_handler_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | ////////////////////////////////////////////////////////////////////////// 4 | // Tested API 5 | #include "common_handlers.hpp" 6 | 7 | 8 | ////////////////////////////////////////////////////////////////////////// 9 | #include "exception_generators.hpp" 10 | 11 | 12 | using namespace mittens; 13 | 14 | BOOST_AUTO_TEST_SUITE(BadAllocCatcher_Tests) 15 | 16 | 17 | 18 | ////////////////////////////////////////////////////////////////////////// 19 | 20 | 21 | BOOST_AUTO_TEST_CASE(Test_that_bad_alloc_catcher_works) 22 | { 23 | { 24 | auto catcher = bad_alloc_handler(EXIT_FAILURE, UnHandler()); 25 | try 26 | { 27 | throw_bad_alloc(); 28 | } 29 | catch (...) 30 | { 31 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 32 | } 33 | } 34 | { 35 | auto catcher = bad_alloc_handler(EXIT_FAILURE); // default nest handler 36 | try 37 | { 38 | throw_bad_alloc(); 39 | } 40 | catch (...) 41 | { 42 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 43 | } 44 | } 45 | 46 | } 47 | 48 | BOOST_AUTO_TEST_CASE(bad_alloc_catcher_does_not_catch_non_bad_alloc_exception) 49 | { 50 | { 51 | auto catcher = bad_alloc_handler(EXIT_FAILURE); 52 | try 53 | { 54 | throw_int(); 55 | } 56 | catch (...) 57 | { 58 | BOOST_CHECK_THROW(catcher.handleException(), int); // should re-throw an 'int' exception 59 | } 60 | } 61 | { 62 | auto catcher = bad_alloc_handler(EXIT_FAILURE); 63 | try 64 | { 65 | throw_std_exception(); 66 | } 67 | catch (...) 68 | { 69 | BOOST_CHECK_THROW(catcher.handleException(), std::exception); // should re-throw an 'int' exception 70 | } 71 | } 72 | } 73 | 74 | 75 | BOOST_AUTO_TEST_SUITE_END() 76 | -------------------------------------------------------------------------------- /tests/exception_generators.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // #ifdef _MSC_VER 5 | // #include 6 | // #endif 7 | 8 | inline void throw_int(int val = 0) 9 | { 10 | throw val; 11 | } 12 | 13 | inline void throw_bad_alloc() 14 | { 15 | //int* myarray= new int[100000000]; 16 | // just in case the new works 17 | //delete myarray; 18 | 19 | throw std::bad_alloc(); 20 | } 21 | 22 | inline void throw_runtime_error() 23 | { 24 | throw std::runtime_error("std::runtime_error!"); 25 | } 26 | 27 | inline void throw_std_exception() 28 | { 29 | throw std::logic_error("std::exception!"); 30 | } 31 | 32 | //inline void throw_seh_exception() 33 | //{ 34 | // *(int*)(0) = 0; 35 | //} 36 | 37 | // inline void throw_AfxThrowMemoryException() 38 | // { 39 | // AfxThrowMemoryException(); 40 | // } 41 | 42 | inline void throw_terminate_signal() 43 | { 44 | std::terminate(); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /tests/generic_exception_handler_call_arity_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common_handlers.hpp" 3 | 4 | ////////////////////////////////////////////////////////////////////////// 5 | // Tested API 6 | #include "generic_exception_handler.hpp" 7 | 8 | 9 | ////////////////////////////////////////////////////////////////////////// 10 | #include "exception_generators.hpp" 11 | 12 | 13 | using namespace mittens; 14 | 15 | 16 | BOOST_AUTO_TEST_SUITE(GenericExceptionCatcher_Call_Arity_Tests) 17 | 18 | 19 | BOOST_AUTO_TEST_CASE(generic_catcher_1_param) 20 | { 21 | { 22 | auto allCatcher = generic_handler(EXIT_FAILURE); 23 | try 24 | { 25 | throw_int(); 26 | } 27 | catch (...) 28 | { 29 | BOOST_CHECK_EQUAL(EXIT_FAILURE, allCatcher.handleException()); 30 | } 31 | } 32 | } 33 | 34 | BOOST_AUTO_TEST_CASE(generic_catcher_2_param_with_custom_action) 35 | { 36 | int hit=0; 37 | { 38 | auto allCatcher = generic_handler(EXIT_FAILURE, [&hit](){ ++hit; }); 39 | { 40 | try 41 | { 42 | throw_int(); 43 | } 44 | catch (...) 45 | { 46 | BOOST_CHECK_EQUAL(0,hit); 47 | BOOST_CHECK_EQUAL(EXIT_FAILURE, allCatcher.handleException()); 48 | BOOST_CHECK_EQUAL(1,hit); 49 | } 50 | } 51 | } 52 | } 53 | 54 | BOOST_AUTO_TEST_CASE(generic_catcher_2_param_with_nested_handler) 55 | { 56 | { 57 | auto stdCatcher = generic_handler(-2); 58 | auto catcher = generic_handler(EXIT_FAILURE, stdCatcher); 59 | { 60 | try 61 | { 62 | throw_int(); 63 | } 64 | catch (...) 65 | { 66 | BOOST_CHECK_EQUAL(-2, catcher.handleException()); 67 | } 68 | } 69 | } 70 | } 71 | 72 | 73 | BOOST_AUTO_TEST_CASE(generic_catcher_2_param_with_multiple_nested_handlers_0) 74 | { 75 | using namespace mittens; 76 | 77 | { 78 | auto catcher = generic_handler(-1, 79 | generic_handler(-2, 80 | generic_handler(-3))); 81 | 82 | try 83 | { 84 | throw_int(); 85 | } 86 | catch (...) 87 | { 88 | BOOST_CHECK_EQUAL(-2, catcher.handleException()); 89 | } 90 | } 91 | 92 | { 93 | struct MyException1 { }; 94 | auto catcher = all_catcher(-1, 95 | generic_handler(-2, 96 | generic_handler(-3))); 97 | 98 | try 99 | { 100 | throw_int(); 101 | } 102 | catch (...) 103 | { 104 | BOOST_CHECK_EQUAL(-2, catcher.handleException()); 105 | } 106 | } 107 | } 108 | 109 | 110 | 111 | BOOST_AUTO_TEST_SUITE_END() 112 | -------------------------------------------------------------------------------- /tests/generic_exception_handler_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | ////////////////////////////////////////////////////////////////////////// 4 | // Tested API 5 | #include "generic_exception_handler.hpp" 6 | 7 | 8 | ////////////////////////////////////////////////////////////////////////// 9 | #include "exception_generators.hpp" 10 | 11 | 12 | using namespace mittens; 13 | 14 | 15 | BOOST_AUTO_TEST_SUITE(GenericExceptionCatcher_Tests) 16 | 17 | BOOST_AUTO_TEST_CASE(GenericExceptionCatcher_first_test) 18 | { 19 | auto allCatcher = GenericExceptionHandler>(EXIT_FAILURE, UnHandler<>()); 20 | { 21 | try 22 | { 23 | throw_int(); 24 | } 25 | catch (...) 26 | { 27 | BOOST_CHECK_EQUAL(EXIT_FAILURE, allCatcher.handleException()); 28 | } 29 | } 30 | 31 | auto stdExceptionCatcher = GenericExceptionHandler>(EXIT_FAILURE, UnHandler<>()); 32 | { 33 | try 34 | { 35 | throw_std_exception(); 36 | } 37 | catch (...) 38 | { 39 | BOOST_CHECK_EQUAL(EXIT_FAILURE, stdExceptionCatcher.handleException()); 40 | } 41 | } 42 | 43 | { 44 | try 45 | { 46 | throw_int(); 47 | } 48 | catch (...) 49 | { 50 | BOOST_CHECK_THROW(stdExceptionCatcher.handleException(), int); 51 | BOOST_CHECK_EQUAL(EXIT_FAILURE, allCatcher.handleException()); 52 | } 53 | } 54 | } 55 | 56 | 57 | BOOST_AUTO_TEST_CASE(generic_catcher_simple_tests) 58 | { 59 | auto allCatcher = generic_handler(EXIT_FAILURE, UnHandler<>()); 60 | { 61 | try 62 | { 63 | throw_int(); 64 | } 65 | catch (...) 66 | { 67 | BOOST_CHECK_EQUAL(EXIT_FAILURE, allCatcher.handleException()); 68 | } 69 | } 70 | 71 | auto stdExceptionCatcher = generic_handler(EXIT_FAILURE, UnHandler<>()); // 72 | { 73 | try 74 | { 75 | throw_std_exception(); 76 | } 77 | catch (...) 78 | { 79 | BOOST_CHECK_EQUAL(EXIT_FAILURE, stdExceptionCatcher.handleException()); 80 | } 81 | } 82 | 83 | { 84 | try 85 | { 86 | throw_int(); 87 | } 88 | catch (...) 89 | { 90 | BOOST_CHECK_THROW(stdExceptionCatcher.handleException(), int); 91 | BOOST_CHECK_EQUAL(EXIT_FAILURE, allCatcher.handleException()); 92 | } 93 | } 94 | 95 | { 96 | // composition test 97 | auto catcher = generic_handler(-1, stdExceptionCatcher); 98 | 99 | try 100 | { 101 | throw_int(); 102 | } 103 | catch (...) 104 | { 105 | BOOST_CHECK_EQUAL(-1, catcher.handleException()); 106 | } 107 | } 108 | } 109 | 110 | BOOST_AUTO_TEST_CASE(generic_catcher_with_custom_action) 111 | { 112 | int hit=0; 113 | { 114 | auto allCatcher = generic_handler(EXIT_FAILURE, [&hit](){ ++hit; }, UnHandler<>()); 115 | { 116 | try 117 | { 118 | throw_int(); 119 | } 120 | catch (...) 121 | { 122 | BOOST_CHECK_EQUAL(0,hit); 123 | BOOST_CHECK_EQUAL(EXIT_FAILURE, allCatcher.handleException()); 124 | BOOST_CHECK_EQUAL(1,hit); 125 | } 126 | } 127 | } 128 | 129 | { 130 | auto stdExceptionCatcher = generic_handler(EXIT_FAILURE, [&hit](std::exception&){ ++hit; }, UnHandler<>()); 131 | { 132 | try 133 | { 134 | throw_std_exception(); 135 | } 136 | catch (...) 137 | { 138 | BOOST_CHECK_EQUAL(1,hit); 139 | BOOST_CHECK_EQUAL(EXIT_FAILURE, stdExceptionCatcher.handleException()); 140 | BOOST_CHECK_EQUAL(2,hit); 141 | } 142 | } 143 | } 144 | } 145 | 146 | BOOST_AUTO_TEST_CASE(generic_catcher_with_custom_action_which_return_fail_code) 147 | { 148 | int DEFAULT_RETURN_VALUE = 0; 149 | { 150 | int EXPECTED_RETURN_VALUE = 1; 151 | auto allCatcher = generic_handler(DEFAULT_RETURN_VALUE, [&](){ return EXPECTED_RETURN_VALUE; }); 152 | { 153 | try 154 | { 155 | throw_int(); 156 | } 157 | catch (...) 158 | { 159 | BOOST_CHECK_EQUAL(EXPECTED_RETURN_VALUE, allCatcher.handleException()); 160 | } 161 | } 162 | } 163 | { 164 | auto allCatcher = generic_handler(DEFAULT_RETURN_VALUE, [&](){ return 2.5; }); // labda retunrs a double != int, so default value should return 165 | { 166 | try 167 | { 168 | throw_int(); 169 | } 170 | catch (...) 171 | { 172 | BOOST_CHECK_EQUAL(DEFAULT_RETURN_VALUE, allCatcher.handleException()); 173 | } 174 | } 175 | } 176 | { 177 | int EXPECTED_RETURN_VALUE = 1; 178 | auto intCatcher = generic_handler(DEFAULT_RETURN_VALUE, [&](int&){ return EXPECTED_RETURN_VALUE; }); 179 | { 180 | try 181 | { 182 | throw_int(); 183 | } 184 | catch (...) 185 | { 186 | BOOST_CHECK_EQUAL(EXPECTED_RETURN_VALUE, intCatcher.handleException()); 187 | } 188 | } 189 | } 190 | { 191 | auto intCatcher = generic_handler(DEFAULT_RETURN_VALUE, [&](int&){ return 2.5; }); // labda retunrs a double != int, so default value should return 192 | { 193 | try 194 | { 195 | throw_int(); 196 | } 197 | catch (...) 198 | { 199 | BOOST_CHECK_EQUAL(DEFAULT_RETURN_VALUE, intCatcher.handleException()); 200 | } 201 | } 202 | } 203 | { 204 | auto intCatcher = generic_handler(0.1, [&](int&) { return 2.5; }); // labda retunrs a double != int, so default value should return 205 | { 206 | try 207 | { 208 | throw_int(); 209 | } 210 | catch (...) 211 | { 212 | BOOST_CHECK_EQUAL(2.5, intCatcher.handleException()); 213 | } 214 | } 215 | } 216 | } 217 | 218 | BOOST_AUTO_TEST_CASE(GenericExceptionCatcher_paren_operator_test) 219 | { 220 | auto allCatcher = generic_handler(EXIT_FAILURE); 221 | { 222 | try 223 | { 224 | throw_int(); 225 | } 226 | catch (...) 227 | { 228 | BOOST_CHECK_EQUAL(EXIT_FAILURE, allCatcher()); 229 | } 230 | } 231 | } 232 | 233 | BOOST_AUTO_TEST_CASE(GenericExceptionCatcher_call_outside_catch_clause_test) 234 | { 235 | auto allCatcher = generic_handler(-1, 236 | generic_handler(-2)); 237 | 238 | // We call allCatcher() outside a catch-block, when there is no exception "in-the-air". 239 | // This is ALWAYS a BUG. The call will generate an std::logic_error that will be intercepted by generic_handler. 240 | BOOST_CHECK_EQUAL(-2, allCatcher()); 241 | } 242 | 243 | 244 | BOOST_AUTO_TEST_SUITE_END() 245 | -------------------------------------------------------------------------------- /tests/handler_composition_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | ////////////////////////////////////////////////////////////////////////// 4 | // Tested API 5 | #include "common_handlers.hpp" 6 | 7 | ////////////////////////////////////////////////////////////////////////// 8 | #include "exception_generators.hpp" 9 | 10 | 11 | using namespace mittens; 12 | 13 | BOOST_AUTO_TEST_SUITE(CatcherComposition_Tests) 14 | 15 | BOOST_AUTO_TEST_CASE(CatcherComposition_works) 16 | { 17 | auto catcher = all_catcher(-1, std_exception_handler(-2)); // external handler 18 | try 19 | { 20 | throw_std_exception(); 21 | } 22 | catch (...) 23 | { 24 | BOOST_CHECK_EQUAL(-2, catcher.handleException()); 25 | } 26 | 27 | try 28 | { 29 | throw_int(); 30 | } 31 | catch (...) 32 | { 33 | BOOST_CHECK_EQUAL(-1, catcher.handleException()); 34 | } 35 | } 36 | 37 | BOOST_AUTO_TEST_CASE(CatcherComposition_with_3_catchers_works) 38 | { 39 | auto catcher = all_catcher(-1, std_exception_handler(-2, bad_alloc_handler(-3))); // external handler 40 | 41 | try 42 | { 43 | throw_bad_alloc(); 44 | } 45 | catch (...) 46 | { 47 | BOOST_CHECK_EQUAL(-3, catcher.handleException()); 48 | } 49 | 50 | try 51 | { 52 | throw_std_exception(); 53 | } 54 | catch (...) 55 | { 56 | BOOST_CHECK_EQUAL(-2, catcher.handleException()); 57 | } 58 | 59 | try 60 | { 61 | throw_int(); 62 | } 63 | catch (...) 64 | { 65 | BOOST_CHECK_EQUAL(-1, catcher.handleException()); 66 | } 67 | } 68 | 69 | BOOST_AUTO_TEST_CASE(CatcherComposition_with_custom_action_works) 70 | { 71 | int hit=0; 72 | auto catcher = all_catcher( -1, [&hit](){ ++hit; }, 73 | std_exception_handler(-2, [&hit](std::exception&){ ++hit; }, 74 | bad_alloc_handler( -3, [&hit](std::bad_alloc&){ ++hit; }, 75 | UnHandler<>()))); 76 | 77 | try 78 | { 79 | throw_bad_alloc(); 80 | } 81 | catch (...) 82 | { 83 | BOOST_CHECK_EQUAL(0,hit); 84 | BOOST_CHECK_EQUAL(-3, catcher.handleException()); 85 | BOOST_CHECK_EQUAL(1,hit); 86 | } 87 | 88 | try 89 | { 90 | throw_std_exception(); 91 | } 92 | catch (...) 93 | { 94 | BOOST_CHECK_EQUAL(1,hit); 95 | BOOST_CHECK_EQUAL(-2, catcher.handleException()); 96 | BOOST_CHECK_EQUAL(2,hit); 97 | } 98 | 99 | try 100 | { 101 | throw_int(); 102 | } 103 | catch (...) 104 | { 105 | BOOST_CHECK_EQUAL(2,hit); 106 | BOOST_CHECK_EQUAL(-1, catcher.handleException()); 107 | BOOST_CHECK_EQUAL(3,hit); 108 | } 109 | } 110 | 111 | 112 | BOOST_AUTO_TEST_CASE(Catch_internal_CatcherComposition_with_custom_action_works) 113 | { 114 | int hit=0; 115 | for (int i=0; i<3; ++i) 116 | { 117 | try 118 | { 119 | switch(i) 120 | { 121 | case 0: throw_bad_alloc(); 122 | case 1: throw_std_exception(); 123 | case 2: throw_int(); 124 | } 125 | } 126 | catch (...) 127 | { 128 | auto catcher = all_catcher( 2, [&hit](){ ++hit; }, 129 | std_exception_handler(1, [&hit](std::exception&){ ++hit; }, 130 | bad_alloc_handler( 0, [&hit](std::bad_alloc&){ ++hit; }, 131 | UnHandler<>()))); 132 | 133 | BOOST_CHECK_EQUAL(i,hit); 134 | BOOST_CHECK_EQUAL(i, catcher.handleException()); 135 | BOOST_CHECK_EQUAL(i+1,hit); 136 | } 137 | } 138 | } 139 | 140 | 141 | 142 | 143 | BOOST_AUTO_TEST_SUITE_END() 144 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE MittensTesting 2 | 3 | #include 4 | #include 5 | 6 | -------------------------------------------------------------------------------- /tests/seh_converter_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | ////////////////////////////////////////////////////////////////////////// 6 | // Tested API 7 | #include "seh_handlers.hpp" 8 | 9 | 10 | ////////////////////////////////////////////////////////////////////////// 11 | #include "common_handlers.hpp" 12 | #include "exception_generators.hpp" 13 | 14 | 15 | using namespace mittens; 16 | 17 | BOOST_AUTO_TEST_SUITE(SEHExceptionCatcher_Tests) 18 | 19 | ////////////////////////////////////////////////////////////////////////// 20 | 21 | 22 | BOOST_AUTO_TEST_CASE(Internal_SEH_Conversion_works) 23 | { 24 | auto catcher = std_exception_handler(-1, seh_to_std_exception_converter()); // must pass fail-code type as template argument 25 | try 26 | { 27 | throw_std_exception(); 28 | } 29 | catch (...) 30 | { 31 | BOOST_CHECK_EQUAL(-1, catcher.handleException()); 32 | } 33 | 34 | try 35 | { 36 | throw_seh_exception(); 37 | } 38 | catch (...) 39 | { 40 | BOOST_CHECK_EQUAL(-1, catcher.handleException()); 41 | } 42 | } 43 | 44 | BOOST_AUTO_TEST_CASE(External_SEH_Conversion_works) 45 | { 46 | auto catcher = seh_to_std_exception_converter(std_exception_handler(-1)); 47 | try 48 | { 49 | throw_std_exception(); 50 | } 51 | catch (...) 52 | { 53 | BOOST_CHECK_EQUAL(-1, catcher.handleException()); 54 | } 55 | 56 | try 57 | { 58 | throw_seh_exception(); 59 | } 60 | catch (...) 61 | { 62 | BOOST_CHECK_EQUAL(-1, catcher.handleException()); 63 | } 64 | } 65 | 66 | BOOST_AUTO_TEST_CASE(External_SEH_Conversion_works_with_printing) 67 | { 68 | auto catcher = seh_to_std_exception_converter(std_exception_handler(-1, [](std::exception&e){ std::cout << "Caught unhandled std::exception: " << e.what() << std::endl; })); 69 | try 70 | { 71 | throw_std_exception(); 72 | } 73 | catch (...) 74 | { 75 | BOOST_CHECK_EQUAL(-1, catcher.handleException()); 76 | } 77 | 78 | try 79 | { 80 | throw_seh_exception(); 81 | } 82 | catch (...) 83 | { 84 | BOOST_CHECK_EQUAL(-1, catcher.handleException()); 85 | } 86 | } 87 | 88 | 89 | BOOST_AUTO_TEST_CASE(Test_that_GlobalSEHException_works) 90 | { 91 | GlobalSEHExceptionConverter sehConverter; 92 | { 93 | auto catcher = std_exception_handler(EXIT_FAILURE); // default nest handler 94 | try 95 | { 96 | throw_seh_exception(); 97 | } 98 | catch (...) 99 | { 100 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 101 | } 102 | } 103 | 104 | } 105 | 106 | 107 | BOOST_AUTO_TEST_SUITE_END() 108 | -------------------------------------------------------------------------------- /tests/std_exception_handler_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | ////////////////////////////////////////////////////////////////////////// 6 | // Tested API 7 | #include "common_handlers.hpp" 8 | 9 | 10 | ////////////////////////////////////////////////////////////////////////// 11 | #include "exception_generators.hpp" 12 | 13 | 14 | using namespace mittens; 15 | 16 | BOOST_AUTO_TEST_SUITE(StdExceptionCatcher_Tests) 17 | 18 | ////////////////////////////////////////////////////////////////////////// 19 | 20 | 21 | BOOST_AUTO_TEST_CASE(Test_that_std_exception_catcher_works) 22 | { 23 | { 24 | auto catcher = std_exception_handler(EXIT_FAILURE, UnHandler()); 25 | try 26 | { 27 | throw_std_exception(); 28 | } 29 | catch (...) 30 | { 31 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 32 | } 33 | } 34 | { 35 | auto catcher = std_exception_handler(EXIT_FAILURE); // default nest handler 36 | try 37 | { 38 | throw_std_exception(); 39 | } 40 | catch (...) 41 | { 42 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 43 | } 44 | } 45 | 46 | } 47 | 48 | BOOST_AUTO_TEST_CASE(Test_that_std_exception_catcher_with_differnt_fail_code_types_works) 49 | { 50 | { 51 | auto catcher = std_exception_handler(false); // use bool fail code type 52 | 53 | try 54 | { 55 | throw_std_exception(); 56 | } 57 | catch (...) 58 | { 59 | BOOST_CHECK_EQUAL(false, catcher.handleException()); 60 | } 61 | } 62 | 63 | { 64 | auto catcher = std_exception_handler(0.7); // use float fail code type 65 | 66 | try 67 | { 68 | throw_std_exception(); 69 | } 70 | catch (...) 71 | { 72 | BOOST_CHECK_EQUAL(0.7, catcher.handleException()); 73 | } 74 | } 75 | } 76 | 77 | BOOST_AUTO_TEST_CASE(Internal_catch_handler_std_exception_catcher_works) 78 | { 79 | { 80 | try 81 | { 82 | throw_std_exception(); 83 | } 84 | catch (...) 85 | { 86 | BOOST_CHECK_EQUAL(EXIT_FAILURE, std_exception_handler(EXIT_FAILURE, UnHandler()).handleException()); 87 | } 88 | } 89 | } 90 | 91 | 92 | BOOST_AUTO_TEST_CASE(std_exception_catcher_does_not_catch_non_std_exception) 93 | { 94 | { 95 | auto catcher = std_exception_handler(EXIT_FAILURE); 96 | try 97 | { 98 | throw_int(); 99 | } 100 | catch (...) 101 | { 102 | BOOST_CHECK_THROW(catcher.handleException(), int); // should re-throw an 'int' exception 103 | } 104 | } 105 | } 106 | 107 | 108 | BOOST_AUTO_TEST_CASE(std_exception_catcher_catches_bad_alloc_exception) 109 | { 110 | auto catcher = std_exception_handler(EXIT_FAILURE, UnHandler()); 111 | try 112 | { 113 | throw_bad_alloc(); 114 | } 115 | catch (...) 116 | { 117 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 118 | } 119 | } 120 | 121 | BOOST_AUTO_TEST_CASE(std_exception_catcher_with_custom_action) 122 | { 123 | int hit = 0; 124 | { 125 | auto catcher = std_exception_handler(EXIT_FAILURE, [&hit](std::exception&){++hit;}, UnHandler()); 126 | try 127 | { 128 | throw_std_exception(); 129 | } 130 | catch (...) 131 | { 132 | BOOST_CHECK_EQUAL(0, hit); 133 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 134 | BOOST_CHECK_EQUAL(1, hit); 135 | } 136 | } 137 | { 138 | auto catcher = std_exception_handler(EXIT_FAILURE, [&hit](std::exception&){++hit;}); // with default nested handler 139 | try 140 | { 141 | throw_std_exception(); 142 | } 143 | catch (...) 144 | { 145 | BOOST_CHECK_EQUAL(1, hit); 146 | BOOST_CHECK_EQUAL(EXIT_FAILURE, catcher.handleException()); 147 | BOOST_CHECK_EQUAL(2, hit); 148 | } 149 | } 150 | } 151 | 152 | 153 | BOOST_AUTO_TEST_SUITE_END() 154 | --------------------------------------------------------------------------------