diff --git a/docs/index.html b/docs/index.html index 713f7ce..adc8c4b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1624,33 +1624,33 @@

2.10.2 New array operations

2.11 Error handling

+

ra:: tries to detect a many errors as possible at compile time, but some errors, such as mismatch between expressions of runtime shape or out of range indices, aren’t apparent until runtime. +

Runtime error handling in ra:: is controlled by two macros.

-

The behavior is as follows. +

They work as follows:

-

RA_DO_CHECK defaults to 2. After including ra/ra.hh, it will always be defined. -

-

ra:: contains uses of assert for checking invariants or for sanity checks that are separate from uses of RA_ASSERT. Those can be disabled in the usual way with -DNDEBUG, but note that -DNDEBUG will also disable assert used in RA_ASSERT (e.g. if RA_DO_CHECK is 1). +

ra:: contains uses of assert for checking invariants or for sanity checks that are separate from uses of RA_ASSERT. Those can be disabled in the usual way with -DNDEBUG, but note that -DNDEBUG will also disable any asserts that are a result of RA_DO_CHECK=1.

-

You can redefine RA_ASSERT to something that is more appropriate for your program. As an example, examples/throw.cc shows how to throw a user-defined exception. +

The performance cost of the runtime checks depends on the program. Without custom RA_ASSERT, RA_DO_CHECK=1 usually has an acceptable cost, but RA_DO_CHECK=2 may be more expensive. The default is RA_DO_CHECK=1.

-

The following is an example of how errors might be reported depending on RA_DO_CHECK. +

The following example shows how errors might be reported depending on RA_DO_CHECK.

+ diff --git a/docs/ra-ra.texi b/docs/ra-ra.texi index cfb2d33..b79a7ef 100644 --- a/docs/ra-ra.texi +++ b/docs/ra-ra.texi @@ -1591,33 +1591,33 @@ my_fun(A && ... a) @section Error handling @c ------------------------------------------------ +@code{ra::} tries to detect a many errors as possible at compile time, but some errors, such as @ref{Rank extension,mismatch} between expressions of runtime shape or @ref{Slicing,out of range indices}, aren't apparent until runtime. + Runtime error handling in @code{ra::} is controlled by two macros. @itemize @item @code{RA_ASSERT(cond, ...)} --- -@code{cond} evaluates to true (in the @code{ra::} namespace) if the check is satisfied. The other arguments are informative. +check that @code{cond} evaluates to true in the @code{ra::} namespace. The other arguments are informative. @item @code{RA_DO_CHECK} --- must have one of the values 0, 1, or 2. @end itemize -The behavior is as follows. +They work as follows: @itemize @item If @code{RA_DO_CHECK} is 0, runtime checks are skipped. -@item If @code{RA_DO_CHECK} is not 0, runtime checks are done: +@item If @code{RA_DO_CHECK} is not 0, runtime checks are done. @itemize - @item If @code{RA_ASSERT} is user defined, then that is used. - @item If it isn't, then @code{ra::} provides one depending on the value of @code{RA_DO_CHECK}. The two options are 1 (plain @code{assert}) and 2 (prints the informative arguments before aborting). Other values are an error. + @item If @code{RA_ASSERT} is defined, using @code{RA_ASSERT}. + @item If @code{RA_ASSERT} isn't defined, the method depends on the value of @code{RA_DO_CHECK}. The two options are 1 (plain @code{assert}) and 2 (prints the informative arguments and aborts). Other values are an error. @end itemize @end itemize -@code{RA_DO_CHECK} defaults to 2. After including @code{ra/ra.hh}, it will always be defined. - -@code{ra::} contains uses of @code{assert} for checking invariants or for sanity checks that are separate from uses of @code{RA_ASSERT}. Those can be disabled in the usual way with @option{-DNDEBUG}, but note that @option{-DNDEBUG} will also disable @code{assert} used in @code{RA_ASSERT} (e.g. if @code{RA_DO_CHECK is 1}). +@code{ra::} contains uses of @code{assert} for checking invariants or for sanity checks that are separate from uses of @code{RA_ASSERT}. Those can be disabled in the usual way with @option{-DNDEBUG}, but note that @option{-DNDEBUG} will also disable any @code{assert}s that are a result of @code{RA_DO_CHECK=1}. -You can redefine @code{RA_ASSERT} to something that is more appropriate for your program. As an example, @code{examples/throw.cc} shows how to throw a user-defined exception. +The performance cost of the runtime checks depends on the program. Without custom @code{RA_ASSERT}, @code{RA_DO_CHECK=1} usually has an acceptable cost, but @code{RA_DO_CHECK=2} may be more expensive. The default is @code{RA_DO_CHECK=1}. -The following is an example of how errors might be reported depending on @code{RA_DO_CHECK}. +The following example shows how errors might be reported depending on @code{RA_DO_CHECK}. @cartouche @verbatim @@ -1634,18 +1634,16 @@ cout << sum(a+b) << endl; @end verbatim @item @code{RA_DO_CHECK=1} @verbatim -*** ./ra/expr.hh:389: constexpr ra::Match, std::tuple...> >::Match(P ...) +./ra/expr.hh:389: constexpr ra::Match, std::tuple...> >::Match(P ...) [with bool checkp = true; P = {ra::Cell >, std::tuple >, std::tuple >&, std::integral_constant >, ra::Cell >, std::tuple >, std::tuple >&, std::integral_constant >}; int ...I = {0, 1}]: Assertion `check()' failed. @end verbatim -@item @code{RA_DO_CHECK=0} - -@emph{crickets} @end itemize -The results with @code{RA_DO_CHECK=0} will of course vary. +@cindex exception +You can redefine @code{RA_ASSERT} to something that is more appropriate for your program. For example, if you run @code{ra::} code under a shell, an abort may not be acceptable. @code{examples/throw.cc} shows how to throw a user-defined exception instead. @c ------------------------------------------------ @node Extras diff --git a/examples/throw.cc b/examples/throw.cc index 6491855..58696a9 100644 --- a/examples/throw.cc +++ b/examples/throw.cc @@ -21,14 +21,11 @@ struct ra_error: public std::exception { std::string s; template ra_error(A && ... a): s(ra::format(std::forward(a) ...)) {} - virtual char const * what() const throw () - { - return s.c_str(); - } + virtual char const * what() const throw () { return s.c_str(); } }; #define RA_ASSERT( cond, ... ) \ - { if (!( cond )) throw ra_error("ra::", std::source_location::current(), " (" STRINGIZE(cond) ") " __VA_OPT__(,) __VA_ARGS__); } + { if (!( cond )) [[unlikely]] throw ra_error("ra::", std::source_location::current(), " (" STRINGIZE(cond) ") " __VA_OPT__(,) __VA_ARGS__); } // The override will be in effect for the rest of ra::. diff --git a/ra/expr.hh b/ra/expr.hh index b64caca..0c629e2 100644 --- a/ra/expr.hh +++ b/ra/expr.hh @@ -17,43 +17,33 @@ // error handling. See examples/throw.cc for how to customize. // -------------------- -#include // might not be needed with a different RA_ASSERT. -#define RA_ASSERT_MSG(cond, ...) \ - { \ - if (std::is_constant_evaluated()) { \ - assert(cond /* FIXME show args */); \ - } else { \ - if (!(cond)) [[unlikely]] { \ - std::cerr << ra::format("*** ra::", std::source_location::current(), " (" STRINGIZE(cond) ") " __VA_OPT__(,) __VA_ARGS__, " ***") << std::endl; \ - std::abort(); \ - } \ - } \ - } -#define RA_ASSERT_PLAIN(cond, ...) \ - { assert(cond /* FIXME show args */); } - #if !defined(RA_DO_CHECK) - #define RA_DO_CHECK 2 // tell users so they need not know the default + #define RA_DO_CHECK 1 // tell users #endif - -#ifndef RA_ASSERT - #if RA_DO_CHECK==0 - #define RA_CHECK(...) +#if RA_DO_CHECK==0 + #define RA_CHECK(...) // good luck +#else + #ifdef RA_ASSERT + #define RA_CHECK(...) RA_ASSERT(__VA_ARGS__) #elif RA_DO_CHECK==1 - #define RA_CHECK(...) RA_ASSERT_PLAIN(__VA_ARGS__) + #define RA_CHECK(cond, ...) { assert(cond); } #elif RA_DO_CHECK==2 - #define RA_CHECK(...) RA_ASSERT_MSG(__VA_ARGS__) + #include + #define RA_CHECK(cond, ...) \ +{ \ + if (std::is_constant_evaluated()) { \ + assert(cond /* FIXME show args */); \ + } else { \ + if (!(cond)) [[unlikely]] { \ + std::cerr << ra::format("*** ra::", std::source_location::current(), " (" STRINGIZE(cond) ") " __VA_OPT__(,) __VA_ARGS__, " ***") << std::endl; \ + std::abort(); \ + } \ + } \ +} #else #error Bad value for RA_DO_CHECK #endif -#else - #if RA_DO_CHECK==0 - #define RA_CHECK(...) - #else - #define RA_CHECK(...) RA_ASSERT(__VA_ARGS__) - #endif #endif - #define RA_AFTER_CHECK Yes namespace ra { diff --git a/test/checks.cc b/test/checks.cc index dddc995..145d2b0 100644 --- a/test/checks.cc +++ b/test/checks.cc @@ -17,20 +17,17 @@ // ------------------------------------- -// bit from example/throw.cc which FIXME should be easier, maybe a prepared option. +// bit from example/throw.cc which FIXME maybe a prepared option. struct ra_error: public std::exception { std::string s; template ra_error(A && ... a): s(ra::format(std::forward(a) ...)) {} - virtual char const * what() const throw () - { - return s.c_str(); - } + virtual char const * what() const throw () { return s.c_str(); } }; #define RA_ASSERT( cond, ... ) \ - { if (!( cond )) throw ra_error("ra::", std::source_location::current(), " (" STRINGIZE(cond) ") " __VA_OPT__(,) __VA_ARGS__); } + { if (!( cond )) [[unlikely]] throw ra_error("ra::", std::source_location::current(), " (" STRINGIZE(cond) ") " __VA_OPT__(,) __VA_ARGS__); } // ------------------------------------- #include "ra/test.hh"
ra::Big<int, 2> a({10, 3}, 1);
@@ -1662,17 +1662,15 @@ 

2.10.2 New array operations

  • RA_DO_CHECK=2
    *** ra::./ra/expr.hh:389,17 (check()) Mismatched shapes [10 3] [40 3]. ***}
     
  • RA_DO_CHECK=1 -
    *** ./ra/expr.hh:389: constexpr ra::Match<checkp, std::tuple<_UTypes ...>, std::tuple<std::integral_constant<int, I>...> >::Match(P ...)
    +
    ./ra/expr.hh:389: constexpr ra::Match<checkp, std::tuple<_UTypes ...>, std::tuple<std::integral_constant<int, I>...> >::Match(P ...)
     [with bool checkp = true; P = {ra::Cell<int, const ra::SmallArray<ra::Dim, std::tuple<std::integral_constant<int, 2> >,
     std::tuple<std::integral_constant<int, 1> >, std::tuple<ra::Dim, ra::Dim> >&, std::integral_constant<int, 0> >, ra::Cell<int, const
     ra::SmallArray<ra::Dim, std::tuple<std::integral_constant<int, 2> >, std::tuple<std::integral_constant<int, 1> >, std::tuple<ra::Dim,
     ra::Dim> >&, std::integral_constant<int, 0> >}; int ...I = {0, 1}]: Assertion `check()' failed.
    -
  • RA_DO_CHECK=0 - -

    crickets -

  • +
    -

    The results with RA_DO_CHECK=0 will of course vary. + +

    You can redefine RA_ASSERT to something that is more appropriate for your program. For example, if you run ra:: code under a shell, an abort may not be acceptable. examples/throw.cc shows how to throw a user-defined exception instead.


    @@ -2980,6 +2978,7 @@

    6.3 Etc

    end, Octave/Matlab: Slicing
    error: Extension
    every: Reference
    exception: Error handling
    explode: Reference

    F