Skip to content

Commit

Permalink
Enable usage of gsl::narrow with exceptions disabled (#640)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Anna Gringauze committed Mar 15, 2018
1 parent c6bf25a commit d846fe5
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 6 deletions.
60 changes: 56 additions & 4 deletions include/gsl/gsl_assert
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
#include <exception>
#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
// when pre/post conditions on the GSL types are violated:
Expand Down Expand Up @@ -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 <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)

#define GSL_CONTRACT_CHECK(type, cond) \
(GSL_LIKELY(cond) ? static_cast<void>(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<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)

Expand Down
4 changes: 2 additions & 2 deletions include/gsl/gsl_util
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ template <class T, class U>
T narrow(U 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{})))
throw narrowing_error();
gsl::details::throw_exception(narrowing_error());
return t;
}

Expand Down
61 changes: 61 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
$<$<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)
53 changes: 53 additions & 0 deletions tests/no_exception_ensure_tests.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
51 changes: 51 additions & 0 deletions tests/no_exception_throw_tests.cpp
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit d846fe5

Please sign in to comment.