From 38d3f2fdcbf8c8fe63e9693a3ed94a75d8c59747 Mon Sep 17 00:00:00 2001 From: Oliver Lee Date: Mon, 12 Feb 2024 13:14:51 -0800 Subject: [PATCH] define precondition macro and violation handler --- .../experimental/__p0009_bits/layout_left.hpp | 4 +- .../__p0009_bits/layout_right.hpp | 4 +- .../__p0009_bits/layout_stride.hpp | 16 +++---- include/experimental/__p0009_bits/macros.hpp | 48 +++++++++++++++++++ tests/CMakeLists.txt | 3 ++ ...ternate_precondition_violation_handler.cpp | 29 +++++++++++ tests/test_layout_ctors.cpp | 9 ++++ tests/test_macros.cpp | 24 ++++++++++ 8 files changed, 124 insertions(+), 13 deletions(-) create mode 100644 tests/test_alternate_precondition_violation_handler.cpp create mode 100644 tests/test_macros.cpp diff --git a/include/experimental/__p0009_bits/layout_left.hpp b/include/experimental/__p0009_bits/layout_left.hpp index b07d1edf..3dfae1f5 100644 --- a/include/experimental/__p0009_bits/layout_left.hpp +++ b/include/experimental/__p0009_bits/layout_left.hpp @@ -157,8 +157,8 @@ class layout_left::mapping { * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ - #if !defined(_MDSPAN_HAS_CUDA) && !defined(_MDSPAN_HAS_HIP) && !defined(NDEBUG) - detail::terminate_if_invalid_strides(detail::with_rank{}, layout_left{}, __extents, other); + #if !defined(_MDSPAN_HAS_CUDA) && !defined(_MDSPAN_HAS_HIP) + detail::validate_strides(detail::with_rank{}, layout_left{}, __extents, other); #endif } diff --git a/include/experimental/__p0009_bits/layout_right.hpp b/include/experimental/__p0009_bits/layout_right.hpp index 25d21f79..216424c5 100644 --- a/include/experimental/__p0009_bits/layout_right.hpp +++ b/include/experimental/__p0009_bits/layout_right.hpp @@ -157,8 +157,8 @@ class layout_right::mapping { * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ - #if !defined(_MDSPAN_HAS_CUDA) && !defined(_MDSPAN_HAS_HIP) && !defined(NDEBUG) - detail::terminate_if_invalid_strides(detail::with_rank{}, layout_right{}, __extents, other); + #if !defined(_MDSPAN_HAS_CUDA) && !defined(_MDSPAN_HAS_HIP) + detail::validate_strides(detail::with_rank{}, layout_right{}, __extents, other); #endif } diff --git a/include/experimental/__p0009_bits/layout_stride.hpp b/include/experimental/__p0009_bits/layout_stride.hpp index ac468435..b06d204f 100644 --- a/include/experimental/__p0009_bits/layout_stride.hpp +++ b/include/experimental/__p0009_bits/layout_stride.hpp @@ -25,10 +25,9 @@ # include "no_unique_address.hpp" #endif -#include #include -#include -#include +#include +#include #ifdef __cpp_lib_span #include @@ -578,11 +577,11 @@ struct layout_stride { namespace detail { template -constexpr void terminate_if_invalid_strides(with_rank<0>, Layout, const Extents&, const Mapping&) +constexpr void validate_strides(with_rank<0>, Layout, const Extents&, const Mapping&) {} template -constexpr void terminate_if_invalid_strides(with_rank, Layout, const Extents& ext, const Mapping& other) +constexpr void validate_strides(with_rank, Layout, const Extents& ext, const Mapping& other) { static_assert(std::is_same::value and (std::is_same::value or @@ -597,10 +596,9 @@ constexpr void terminate_if_invalid_strides(with_rank, Layout, const Extents& for (std::size_t r = 0; r < N; r++) { const std::size_t s = is_left ? r : N - 1 - r; - if (not common_integral_compare(stride, other.stride(s))) { - // assigning to layout_{left,right} with invalid strides - std::abort(); - } + MDSPAN_PRECONDITION(common_integral_compare(stride, other.stride(s)) + and "invalid strides for layout_{left,right}"); + stride *= ext.extent(s); } } diff --git a/include/experimental/__p0009_bits/macros.hpp b/include/experimental/__p0009_bits/macros.hpp index 3eeb3975..5b3992b1 100644 --- a/include/experimental/__p0009_bits/macros.hpp +++ b/include/experimental/__p0009_bits/macros.hpp @@ -18,6 +18,8 @@ #include "config.hpp" +#include +#include #include // std::is_void #ifndef _MDSPAN_HOST_DEVICE @@ -101,6 +103,52 @@ #define MDSPAN_IMPL_STANDARD_NAMESPACE_STRING MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_STANDARD_NAMESPACE) #define MDSPAN_IMPL_PROPOSED_NAMESPACE_STRING MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_STANDARD_NAMESPACE) "::" MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_PROPOSED_NAMESPACE) +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) +{ + (void)std::fprintf(::stderr, "%s:%u: precondition failure: `%s`\n", file, line, cond); + std::abort(); +} + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#ifndef MDSPAN_PRECONDITION_VIOLATION_HANDLER +#define MDSPAN_PRECONDITION_VIOLATION_HANDLER(cond, file, line) \ + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::default_precondition_violation_handler(cond, file, line) +#endif + +#ifndef MDSPAN_SKIP_PRECONDITION_VIOLATION_HANDLER + #ifndef NDEBUG + #define MDSPAN_SKIP_PRECONDITION_VIOLATION_HANDLER 0 + #else + #define MDSPAN_SKIP_PRECONDITION_VIOLATION_HANDLER 1 + #endif +#endif + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +template +constexpr void precondition(const char* cond, const char* file, unsigned line) +{ + if (skip) { return; } + + MDSPAN_PRECONDITION_VIOLATION_HANDLER(cond, file, line); +} + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#define MDSPAN_PRECONDITION(...) \ + do { \ + if (not (__VA_ARGS__)) { \ + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::precondition(#__VA_ARGS__, __FILE__, __LINE__); \ + } \ + } while (0) + // end Preprocessor helpers }}}1 //============================================================================== diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 559eae2e..c3c18fd5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,6 +14,7 @@ macro(mdspan_add_test name) target_link_libraries(${name} mdspan gtest_main) add_test(${name} ${name}) set_property(TARGET ${name} PROPERTY COMPILE_WARNING_AS_ERROR ON) + target_compile_definitions(${name} PUBLIC MDSPAN_SKIP_PRECONDITION_VIOLATION_HANDLER=0) endmacro() if(MDSPAN_USE_SYSTEM_GTEST) @@ -49,6 +50,8 @@ else() ) endif() +mdspan_add_test(test_alternate_precondition_violation_handler) +mdspan_add_test(test_macros) mdspan_add_test(test_extents) mdspan_add_test(test_mdspan_ctors) mdspan_add_test(test_mdspan_swap) diff --git a/tests/test_alternate_precondition_violation_handler.cpp b/tests/test_alternate_precondition_violation_handler.cpp new file mode 100644 index 00000000..0068caa4 --- /dev/null +++ b/tests/test_alternate_precondition_violation_handler.cpp @@ -0,0 +1,29 @@ +#include + +#define MDSPAN_PRECONDITION_VIOLATION_HANDLER(cond, file, line) \ + do { \ + throw std::logic_error{"precondition failure"}; \ + } while (0); + +#include +#include + +TEST(mdspan_macros, alternate_precondition_violation_handler) +{ + ASSERT_THROW(MDSPAN_PRECONDITION(false), std::logic_error); +} + +TEST(mdspan_macros, alternate_precondition_check_constexpr_invocable) +{ + struct fn + { + constexpr auto operator()() const + { + MDSPAN_PRECONDITION(1 + 1 == 2); + return 42; + } + }; + + static constexpr auto x = fn{}(); + (void)x; +} diff --git a/tests/test_layout_ctors.cpp b/tests/test_layout_ctors.cpp index c7bbcd70..4dde91b8 100644 --- a/tests/test_layout_ctors.cpp +++ b/tests/test_layout_ctors.cpp @@ -202,6 +202,15 @@ TEST(TestLayoutLeftListInitialization, test_layout_left_extent_initialization) { ASSERT_TRUE(m.is_exhaustive()); } +TEST(TestConvertingConstructionFromLayoutStride, precondition_failure) { + using E = Kokkos::extents; + + const auto stride = Kokkos::layout_stride::mapping{E{}, std::array{2, 2}}; + + ASSERT_DEATH(Kokkos::layout_left::mapping{stride}, "invalid strides"); + ASSERT_DEATH(Kokkos::layout_right::mapping{stride}, "invalid strides"); +} + // FIXME: CUDA NVCC including 12.0 does not like CTAD on nested classes #if defined(_MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) && !defined(__NVCC__) TEST(TestLayoutLeftCTAD, test_layout_left_ctad) { diff --git a/tests/test_macros.cpp b/tests/test_macros.cpp new file mode 100644 index 00000000..8e8cd752 --- /dev/null +++ b/tests/test_macros.cpp @@ -0,0 +1,24 @@ +#include +#include + +TEST(mdspan_macros, precondition_violation) +{ + constexpr auto msg = "hello, world!"; + + ASSERT_DEATH(MDSPAN_PRECONDITION(false and "hello, world!"), msg); +} + +TEST(mdspan_macros, precondition_check_constexpr_invocable) +{ + struct fn + { + constexpr auto operator()() const + { + MDSPAN_PRECONDITION(1 + 1 == 2); + return 42; + } + }; + + static constexpr auto x = fn{}(); + (void)x; +}