From d846fe50a3f0bb7767c7e087a05f4be95f4da0ec Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Thu, 15 Mar 2018 16:00:08 -0700 Subject: [PATCH] Enable usage of gsl::narrow with exceptions disabled (#640) * Enable usage of gsl::narrow with exceptions disabled This solution uses the approach of boost::asio to enabling usage of the library in environments where exception usage is either prohibited or not feasible (due to code size constraints). A function template gsl::throw_exception has been added, which in a normal environment just throws the exception. However, when GSL_TERMINATE_ON_CONTRACT_VIOLATION is defined the function is only declared by gsl and the definition of this function template must be supplied by the library's user. Closes: #468 Signed-off-by: Damian Jarek Addition: - understand STL no exception macro - use function static variable to set termination handler in kernel mode - add compile-only tests for no-exception mode * added termination tests and fixed bugs * disabled warning C4577 for msvc 2015 --- include/gsl/gsl_assert | 60 ++++++++++++++++++++++++++-- include/gsl/gsl_util | 4 +- tests/CMakeLists.txt | 61 +++++++++++++++++++++++++++++ tests/no_exception_ensure_tests.cpp | 53 +++++++++++++++++++++++++ tests/no_exception_throw_tests.cpp | 51 ++++++++++++++++++++++++ 5 files changed, 223 insertions(+), 6 deletions(-) create mode 100644 tests/no_exception_ensure_tests.cpp create mode 100644 tests/no_exception_throw_tests.cpp diff --git a/include/gsl/gsl_assert b/include/gsl/gsl_assert index eeb3473..2bda149 100644 --- a/include/gsl/gsl_assert +++ b/include/gsl/gsl_assert @@ -20,6 +20,14 @@ #include #include // for logic_error +// +// Temporary until MSVC STL supports no-exceptions mode. +// Currently terminate is a no-op in this mode, so we add termination behavior back +// +#if defined(_MSC_VER) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS +#define GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND +#endif + // // There are three configuration options for this GSL implementation's behavior // when pre/post conditions on the GSL types are violated: @@ -68,18 +76,62 @@ struct fail_fast : public std::logic_error { explicit fail_fast(char const* const message) : std::logic_error(message) {} }; -} + +namespace details +{ +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + + typedef void (*terminate_handler)(); + + inline gsl::details::terminate_handler& get_terminate_handler() noexcept + { + static terminate_handler handler = &abort; + return handler; + } + +#endif + + [[noreturn]] inline void terminate() noexcept + { +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + (*gsl::details::get_terminate_handler())(); +#else + std::terminate(); +#endif + } + +#if defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) + + template + [[noreturn]] void throw_exception(Exception&&) + { + gsl::details::terminate(); + } + +#else + + template + [[noreturn]] void throw_exception(Exception&& exception) + { + throw exception; + } + +#endif + +} // namespace details +} // namespace gsl #if defined(GSL_THROW_ON_CONTRACT_VIOLATION) #define GSL_CONTRACT_CHECK(type, cond) \ (GSL_LIKELY(cond) ? static_cast(0) \ - : throw gsl::fail_fast("GSL: " type " failure at " __FILE__ \ - ": " GSL_STRINGIFY(__LINE__))) + : gsl::details::throw_exception(gsl::fail_fast( \ + "GSL: " type " failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__)))) #elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) -#define GSL_CONTRACT_CHECK(type, cond) (GSL_LIKELY(cond) ? static_cast(0) : std::terminate()) +#define GSL_CONTRACT_CHECK(type, cond) \ + (GSL_LIKELY(cond) ? static_cast(0) : gsl::details::terminate()) #elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION) diff --git a/include/gsl/gsl_util b/include/gsl/gsl_util index 1aa8ba6..2ca171b 100644 --- a/include/gsl/gsl_util +++ b/include/gsl/gsl_util @@ -110,9 +110,9 @@ template T narrow(U u) { T t = narrow_cast(u); - if (static_cast(t) != u) throw narrowing_error(); + if (static_cast(t) != u) gsl::details::throw_exception(narrowing_error()); if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) - throw narrowing_error(); + gsl::details::throw_exception(narrowing_error()); return t; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 86ce5a4..0e08d77 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -101,3 +101,64 @@ add_gsl_test(utils_tests) add_gsl_test(owner_tests) add_gsl_test(byte_tests) add_gsl_test(algorithm_tests) + + +# No exception tests + +foreach(flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + STRING (REGEX REPLACE "/EHsc" "" ${flag_var} "${${flag_var}}") +endforeach(flag_var) + +# this interface adds compile options to how the tests are run +# please try to keep entries ordered =) +add_library(gsl_tests_config_noexcept INTERFACE) +target_compile_options(gsl_tests_config_noexcept INTERFACE + $<$: + /D_HAS_EXCEPTIONS=0 + /wd4702 + /wd4577 + /W4 + /WX + > + $<$>: + -fno-strict-aliasing + -fno-exceptions + -Wall + -Wcast-align + -Wconversion + -Wctor-dtor-privacy + -Werror + -Wextra + -Wno-missing-braces + -Wnon-virtual-dtor + -Wold-style-cast + -Woverloaded-virtual + -Wpedantic + -Wshadow + -Wsign-conversion + > +) + +# set definitions for tests +target_compile_definitions(gsl_tests_config_noexcept INTERFACE + GSL_TERMINATE_ON_CONTRACT_VIOLATION +) + +function(add_gsl_test_noexcept name) + add_executable(${name} ${name}.cpp) + target_link_libraries(${name} + GSL + gsl_tests_config_noexcept + ) + add_test( + ${name} + ${name} + ) + # group all tests under GSL_tests_noexcept + set_property(TARGET ${name} PROPERTY FOLDER "GSL_tests_noexcept") +endfunction() + +add_gsl_test_noexcept(no_exception_throw_tests) +add_gsl_test_noexcept(no_exception_ensure_tests) diff --git a/tests/no_exception_ensure_tests.cpp b/tests/no_exception_ensure_tests.cpp new file mode 100644 index 0000000..5da021e --- /dev/null +++ b/tests/no_exception_ensure_tests.cpp @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include // for std::exit +#include // for span + +int operator_subscript_no_throw() +{ + int arr[10]; + gsl::span sp { arr }; + return sp[11]; +} + + +void test_terminate() +{ + std::exit(0); +} + +void setup_termination_handler() +{ +#if defined(_MSC_VER) + + auto& handler = gsl::details::get_terminate_handler(); + handler = &test_terminate; + +#else + + std::set_terminate(test_terminate); + +#endif +} + + +int main() +{ + setup_termination_handler(); + operator_subscript_no_throw(); + return -1; +} diff --git a/tests/no_exception_throw_tests.cpp b/tests/no_exception_throw_tests.cpp new file mode 100644 index 0000000..dd4e994 --- /dev/null +++ b/tests/no_exception_throw_tests.cpp @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include // for std::exit +#include // for narrow + +int narrow_no_throw() +{ + long long bigNumber = 0x0fffffffffffffff; + return gsl::narrow(bigNumber); +} + +void test_terminate() +{ + std::exit(0); +} + +void setup_termination_handler() +{ +#if defined(_MSC_VER) + + auto& handler = gsl::details::get_terminate_handler(); + handler = &test_terminate; + +#else + + std::set_terminate(test_terminate); + +#endif +} + + +int main() +{ + setup_termination_handler(); + narrow_no_throw(); + return -1; +}