4

C++ knows assert() which allows runtime checks that compile to nothing in dependence of NDEBUG.

I would like to replace that macro using compiler-code and avoiding the preprocessor. I need to do the following:

  • Break or terminate if expressions evaluates to false
  • Log the code line where the assert is called
  • Discard the check and the passed expression for NDEBUG builds

Breaking/terminating the application is easy.

In C++20 there is std::experimental::source_location which I can use to get the code location of the assertion.

A compile time conditional could be done using requires or constexpr if

However I do not know how I could avoid the evaluation of the expression. When implementing myAssert(expression) as a function, I need to pass the expression result as a function argument which means it is evaluated anyway, even if the parameter is not used inside the function.

Is there a way to solve this in C++20?

EDIT: A templated example:

template <typename T> requires (gDebug)
void assertTrue(const T& pResult, const std::experimental::source_location& pLocation) noexcept
{
   if (!static_cast<bool>(pResult))
   {
      // error handling
   }
}

template <typename T> requires (!gDebug)
void assertTrue(const T&) noexcept
{
}
Silicomancer
  • 8,604
  • 10
  • 63
  • 130
  • can you show an example? I am a bit confused by not evaluating the expression, but evaluating the expression anyhow – 463035818_is_not_an_ai Jul 27 '20 at 14:31
  • 7
    This is one of the few jobs for a macro. You should use the tool that fits the job. Also, `assert` is a run time thing, `if constexpr` requires compile time. – NathanOliver Jul 27 '20 at 14:31
  • @NathanOliver Yes. The check itself is runtime. However removing the check in NDEBUG build is compile time. – Silicomancer Jul 27 '20 at 14:33
  • 1
    @NathanOliver I believe the condition of `if constexpr` should serve as a (compile-time) switch for enabling/disabling assertion itself. – Daniel Langr Jul 27 '20 at 14:33
  • 2
    Did `assert` or the preprocessor offend you in some way? They've been working fine for the rest of us for decades. – 3Dave Jul 27 '20 at 14:35
  • @NathanOliver Maybe your are right. I am mainly just curious. However macros also have real drawbacks like problems with commas in expressions. – Silicomancer Jul 27 '20 at 14:37
  • @DanielLangr Aha, that makes sense. Unfortuneltly that gets in the way of having the ability to: *Discard the check and the passed expression for NDEBUG builds* – NathanOliver Jul 27 '20 at 14:38
  • 1
    @Silicomancer *"commas in expression"* Well, that's a problem only with the standard `assert`. You can write your own one using `...` and `__VA_ARGS__`. – HolyBlackCat Jul 27 '20 at 14:39
  • 3
    Nothing can depend on `NDEBUG` without depending on the preprocessor because `NDEBUG` is a preprocessor macro. – eerorika Jul 27 '20 at 15:11

1 Answers1

3

I suppose you are talking about the case when you disabled debugging and you want the function to be a noop. I see 2 options:

You can use a macro. Macros can be misused, but they have their place and "passing an expression" without evaluating it is a case for a macro.

Alternatively, pass a callable that returns the result you want to assert for. Only call it when gDebug == True:

template <typename F> requires (gDebug)
void assertTrue(const F& f, const std::experimental::source_location& pLocation) noexcept
{
   if (!static_cast<bool>(f()))
   {
      // error handling
   }
}

Though this will make the call rather verbose. For example one that fails always:

assertTrue( [](){ return false; });
Silicomancer
  • 8,604
  • 10
  • 63
  • 130
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185