diff --git a/CMakeLists.txt b/CMakeLists.txt index 63f9b499..3f8c1b2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,18 @@ option(CPPSORT_BUILD_TESTING "Build the cpp-sort test suite" ${BUILD_TESTING}) option(CPPSORT_BUILD_EXAMPLES "Build the cpp-sort examples" ${BUILD_EXAMPLES}) option(CPPSORT_ENABLE_AUDITS "Enable assertions in the library" OFF) option(CPPSORT_ENABLE_ASSERTIONS "Enable assertions in the library" ${CPPSORT_ENABLE_AUDITS}) +option(CPPSORT_USE_LIBASSERT "Use libassert for assertions (experimental)" OFF) + +# Optionally use libassert for assertions +if (CPPSORT_USE_LIBASSERT) + include(DownloadProject) + download_project(PROJ libassert + GIT_REPOSITORY https://github.com/jeremy-rifkin/libassert + GIT_TAG v1.2 + UPDATE_DISCONNECTED 1 + ) + add_subdirectory(${libassert_SOURCE_DIR} ${libassert_BINARY_DIR}) +endif() # Create cpp-sort library and configure it add_library(cpp-sort INTERFACE) @@ -29,7 +41,10 @@ target_compile_features(cpp-sort INTERFACE cxx_std_14) # MSVC won't work without a stricter standard compliance if (MSVC) - target_compile_options(cpp-sort INTERFACE /permissive-) + target_compile_options(cpp-sort INTERFACE + /permissive- + /Zc:preprocessor + ) endif() # Handle diagnostic options @@ -40,6 +55,12 @@ if (CPPSORT_ENABLE_AUDITS) target_compile_definitions(cpp-sort INTERFACE CPPSORT_ENABLE_AUDITS) endif() +# Optionally link to libassert +if (CPPSORT_USE_LIBASSERT) + target_link_libraries(cpp-sort INTERFACE assert) + target_compile_definitions(cpp-sort INTERFACE CPPSORT_USE_LIBASSERT) +endif() + add_library(cpp-sort::cpp-sort ALIAS cpp-sort) # Install targets and files diff --git a/docs/Home.md b/docs/Home.md index 812cc1de..a12db98f 100644 --- a/docs/Home.md +++ b/docs/Home.md @@ -85,6 +85,16 @@ The macros `CPPSORT_ENABLE_ASSERTIONS` and `CPPSORT_ENABLE_AUDITS` can be config *Changed in version 1.15.0*: defining `CPPSORT_ENABLE_AUDITS` now automatically defines `CPPSORT_ENABLE_ASSERTIONS`. +#### Rich assertions with libassert (experimental) + +When defined, the macro `CPPSORT_USE_LIBASSERT` makes internal assertions use [libassert][libassert] instead of the standard `assert` macro. This allows assertions to provide additional information when an assertion fires, notably by displaying a full stack trace, and optionally to decompose the asserted expressions. See the library's README for more information. + +This option can be enabled from CMake by setting the `CPPSORT_USE_LIBASSERT` to `ON`. See [the Tooling page][tooling-cmake] for more information. + +*Note: the support for libassert is still experimental and has not been tested on all supported platforms. Errors are to be expected.* + +*New in version 1.15.0* + ## Miscellaneous This wiki also includes a small section about the [original research][original-research] that happened during the conception of the library and the results of this research. While it is not needed to understand how the library works or how to use it, it may be of interest if you want to discover new things about sorting. @@ -97,6 +107,7 @@ Hope you have fun! [assume]: https://en.cppreference.com/w/cpp/language/attributes/assume [benchmarks]: Benchmarks.md + [libassert]: https://github.com/jeremy-rifkin/libassert [original-research]: Original-research.md [quickstart]: Quickstart.md [swappable]: https://en.cppreference.com/w/cpp/concepts/swappable diff --git a/docs/Tooling.md b/docs/Tooling.md index a733dbe7..dbeebcfa 100644 --- a/docs/Tooling.md +++ b/docs/Tooling.md @@ -33,6 +33,7 @@ The project's CMake files offers some options, though they are mainly used to co * `CPPSORT_STATIC_TESTS`: when `ON`, some tests are executed at compile time instead of runtime, defaults to `OFF`. * `CPPSORT_ENABLE_ASSERTIONS`: when `ON`, defines the eponymous macro which enables debug assertions from the library's internals, defaults to the value of `CPPSORT_ENABLE_AUDITS`. * `CPPSORT_ENABLE_AUDITS`: when `ON`, defines the eponymous macro which enables expensive debug assertions from the library's internals, defaults to `OFF`. +* `CPPSORT_USE_LIBASSERT` (experimental): when `ON`, internal assertions use [libassert][libassert] instead of the standard `assert` macro, providing additional information about the errors. Defaults to `OFF`. Some of those options also exist without the `CPPSORT_` prefix, but they are deprecated. For compatibility reasons, the options with the `CPPSORT_` prefix default to the values of the equivalent unprefixed options. @@ -44,7 +45,7 @@ Note: when `CPPSORT_ENABLE_AUDITS` is `ON`, assertions in the library are enable *New in version 1.13.0:* added the option `CPPSORT_STATIC_TESTS`. -*New in version 1.15.0:* `CPPSORT_ENABLE_ASSERTIONS` and `CPPSORT_ENABLE_AUDITS`. +*New in version 1.15.0:* `CPPSORT_ENABLE_ASSERTIONS`, `CPPSORT_ENABLE_AUDITS` and `CPPSORT_USE_LIBASSERT`. ***WARNING:** options without a `CPPSORT_` prefixed are deprecated in version 1.9.0 and removed in version 2.0.0.* @@ -89,3 +90,4 @@ Due to slight markup differences, some pages might not fully render correctly bu [conan]: https://conan.io/ [conan-center]: https://conan.io/center/cpp-sort [gollum]: https://github.com/gollum/gollum + [libassert]: https://github.com/jeremy-rifkin/libassert diff --git a/include/cpp-sort/detail/config.h b/include/cpp-sort/detail/config.h index e3d103bf..020c19aa 100644 --- a/include/cpp-sort/detail/config.h +++ b/include/cpp-sort/detail/config.h @@ -68,6 +68,9 @@ #if defined(CPPSORT_ENABLE_ASSERTIONS) # include +# if defined(CPPSORT_USE_LIBASSERT) +# include +# endif #endif //////////////////////////////////////////////////////////// @@ -78,11 +81,21 @@ // explicitly enabled in cpp-sort #if !defined(NDEBUG) && defined(CPPSORT_ENABLE_ASSERTIONS) -# ifndef CPPSORT_ASSERT -# define CPPSORT_ASSERT(...) assert((__VA_ARGS__)) +# if !defined(CPPSORT_ASSERT) +# if defined(CPPSORT_USE_LIBASSERT) +# define CPPSORT_ASSERT(...) ASSERT(__VA_ARGS__) +# else +# define CPPSORT_ARG2(_0, _1, _2, ...) _2 +# define CPPSORT_NARG2(...) CPPSORT_ARG2(__VA_ARGS__, 2, 1, 0) +# define CPPSORT_ONE_OR_TWO_ARGS_1(condition) assert(condition) +# define CPPSORT_ONE_OR_TWO_ARGS_2(condition, message) assert(condition && message) +# define CPPSORT_ONE_OR_TWO_ARGS_N(N, ...) CPPSORT_ONE_OR_TWO_ARGS_##N(__VA_ARGS__) +# define CPPSORT_ONE_OR_TWO_ARGS(N, ...) CPPSORT_ONE_OR_TWO_ARGS_N(N, __VA_ARGS__) +# define CPPSORT_ASSERT(...) CPPSORT_ONE_OR_TWO_ARGS(CPPSORT_NARG2(__VA_ARGS__), __VA_ARGS__) +# endif # endif #else -# define CPPSORT_ASSERT(...) +# define CPPSORT_ASSERT(...) ((void)0) #endif //////////////////////////////////////////////////////////// @@ -97,7 +110,7 @@ # define CPPSORT_AUDIT(...) CPPSORT_ASSERT(__VA_ARGS__) # endif #else -# define CPPSORT_AUDIT(...) +# define CPPSORT_AUDIT(...) ((void)0) #endif //////////////////////////////////////////////////////////// @@ -118,7 +131,7 @@ #elif defined(_MSC_VER) # define CPPSORT_ASSUME(expression) __assume(expression) #else -# define CPPSORT_ASSUME(cond) +# define CPPSORT_ASSUME(cond) ((void)0) #endif //////////////////////////////////////////////////////////// @@ -129,13 +142,13 @@ // reached #if !defined(NDEBUG) && defined(CPPSORT_ENABLE_AUDITS) -# define CPPSORT_UNREACHABLE CPPSORT_ASSERT(false && "unreachable"); +# define CPPSORT_UNREACHABLE CPPSORT_ASSERT(false, "unreachable") #elif defined(__GNUC__) || defined(__clang__) # define CPPSORT_UNREACHABLE __builtin_unreachable() #elif defined(_MSC_VER) # define CPPSORT_UNREACHABLE __assume(false) #else -# define CPPSORT_UNREACHABLE +# define CPPSORT_UNREACHABLE ((void)0) #endif //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/detail/timsort.h b/include/cpp-sort/detail/timsort.h index 5cc25697..790cc457 100644 --- a/include/cpp-sort/detail/timsort.h +++ b/include/cpp-sort/detail/timsort.h @@ -6,7 +6,7 @@ * - http://cr.openjdk.java.net/~martin/webrevs/openjdk7/timsort/raw_files/new/src/share/classes/java/util/TimSort.java * * Copyright (c) 2011 Fuji, Goro (gfx) . - * Copyright (c) 2015-2022 Morwenn. + * Copyright (c) 2015-2023 Morwenn. * Copyright (c) 2021 Igor Kushnir . * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -565,7 +565,7 @@ namespace detail dest[len2] = iter_move(cursor1); } else { - CPPSORT_ASSERT(len1 != 0 && "comparison function violates its general contract"); + CPPSORT_ASSERT(len1 != 0, "comparison function violates its general contract"); CPPSORT_ASSERT(len2 == 0); CPPSORT_ASSERT(len1 > 1); detail::move(cursor1, cursor1 + len1, dest); @@ -715,7 +715,7 @@ namespace detail detail::move_backward(cursor1 - len1, cursor1, dest + (1 + len1)); *dest = iter_move(cursor2); } else { - CPPSORT_ASSERT(len2 != 0 && "comparison function violates its general contract"); + CPPSORT_ASSERT(len2 != 0, "comparison function violates its general contract"); CPPSORT_ASSERT(len1 == 0); CPPSORT_ASSERT(len2 > 1); detail::move(buffer.get(), buffer.get() + len2, dest - (len2 - 1));