Skip to content

Commit

Permalink
Default RA_DO_CHECK to 1
Browse files Browse the repository at this point in the history
* ra/expr.hh: As stated.
* docs/ra-ra.texi: Reword explanation.
  • Loading branch information
lloda committed Mar 21, 2024
1 parent fa08040 commit 9e7778a
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 71 deletions.
31 changes: 15 additions & 16 deletions docs/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 13 additions & 15 deletions docs/ra-ra.texi
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<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.
@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
Expand Down
7 changes: 2 additions & 5 deletions examples/throw.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,11 @@ struct ra_error: public std::exception
{
std::string s;
template <class ... A> ra_error(A && ... a): s(ra::format(std::forward<A>(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::.

Expand Down
48 changes: 19 additions & 29 deletions ra/expr.hh
Original file line number Diff line number Diff line change
Expand Up @@ -17,43 +17,33 @@
// error handling. See examples/throw.cc for how to customize.
// --------------------

#include <iostream> // 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 <iostream>
#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 {
Expand Down
9 changes: 3 additions & 6 deletions test/checks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <class ... A> ra_error(A && ... a): s(ra::format(std::forward<A>(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"
Expand Down

0 comments on commit 9e7778a

Please sign in to comment.