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 <damian.jarek93@gmail.com>

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
This commit is contained in:
Anna Gringauze 2018-03-15 16:00:08 -07:00 committed by GitHub
parent c6bf25a5f6
commit d846fe50a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 223 additions and 6 deletions

View File

@ -20,6 +20,14 @@
#include <exception> #include <exception>
#include <stdexcept> // for logic_error #include <stdexcept> // 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 // There are three configuration options for this GSL implementation's behavior
// when pre/post conditions on the GSL types are violated: // 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) {} 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 <typename Exception>
[[noreturn]] void throw_exception(Exception&&)
{
gsl::details::terminate();
}
#else
template <typename Exception>
[[noreturn]] void throw_exception(Exception&& exception)
{
throw exception;
}
#endif
} // namespace details
} // namespace gsl
#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) #if defined(GSL_THROW_ON_CONTRACT_VIOLATION)
#define GSL_CONTRACT_CHECK(type, cond) \ #define GSL_CONTRACT_CHECK(type, cond) \
(GSL_LIKELY(cond) ? static_cast<void>(0) \ (GSL_LIKELY(cond) ? static_cast<void>(0) \
: throw gsl::fail_fast("GSL: " type " failure at " __FILE__ \ : gsl::details::throw_exception(gsl::fail_fast( \
": " GSL_STRINGIFY(__LINE__))) "GSL: " type " failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__))))
#elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) #elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION)
#define GSL_CONTRACT_CHECK(type, cond) (GSL_LIKELY(cond) ? static_cast<void>(0) : std::terminate()) #define GSL_CONTRACT_CHECK(type, cond) \
(GSL_LIKELY(cond) ? static_cast<void>(0) : gsl::details::terminate())
#elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION) #elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)

View File

@ -110,9 +110,9 @@ template <class T, class U>
T narrow(U u) T narrow(U u)
{ {
T t = narrow_cast<T>(u); T t = narrow_cast<T>(u);
if (static_cast<U>(t) != u) throw narrowing_error(); if (static_cast<U>(t) != u) gsl::details::throw_exception(narrowing_error());
if (!details::is_same_signedness<T, U>::value && ((t < T{}) != (u < U{}))) if (!details::is_same_signedness<T, U>::value && ((t < T{}) != (u < U{})))
throw narrowing_error(); gsl::details::throw_exception(narrowing_error());
return t; return t;
} }

View File

@ -101,3 +101,64 @@ add_gsl_test(utils_tests)
add_gsl_test(owner_tests) add_gsl_test(owner_tests)
add_gsl_test(byte_tests) add_gsl_test(byte_tests)
add_gsl_test(algorithm_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
$<$<CXX_COMPILER_ID:MSVC>:
/D_HAS_EXCEPTIONS=0
/wd4702
/wd4577
/W4
/WX
>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:
-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)

View File

@ -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 <cstdlib> // for std::exit
#include <gsl/span> // for span
int operator_subscript_no_throw()
{
int arr[10];
gsl::span<int> 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;
}

View File

@ -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 <cstdlib> // for std::exit
#include <gsl/gsl_util> // for narrow
int narrow_no_throw()
{
long long bigNumber = 0x0fffffffffffffff;
return gsl::narrow<int>(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;
}