Skip to content

Commit

Permalink
Ensure LOG* and CHECK* macros are statement-safe (#320)
Browse files Browse the repository at this point in the history
The LOG and LOGF macros had been modified to be statement-safe, but the
LOG_IF, CHECK, LOGF_IF, CHECKF, and CHECK_F macros were all still
unsafe in face of code where single-statement blocks were not enclosed
in {}.

For example code like this:

  if (!foobar)
      CHECKF(goodness, "badness detected!");
  else
      handle_foobar(foobar);

would fail in subtle and possibly dangerous ways.

Fix this by combining multiple if-statements into a single conditional
and inverting the conditions, then adding an empty then-block and moving
the log statement to the else-block.
  • Loading branch information
madscientist authored and Kjell Hedström committed Jul 28, 2019
1 parent 5cb5371 commit f149179
Showing 1 changed file with 8 additions and 12 deletions.
20 changes: 8 additions & 12 deletions src/g3log/g3log.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,19 +145,18 @@ namespace g3 {


// LOG(level) is the API for the stream log
#define LOG(level) if(!g3::logLevel(level)){ } else INTERNAL_LOG_MESSAGE(level).stream()
#define LOG(level) if(!g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).stream()


// 'Conditional' stream log
#define LOG_IF(level, boolean_expression) \
if(true == (boolean_expression)) \
if(g3::logLevel(level)) INTERNAL_LOG_MESSAGE(level).stream()
if (false == (boolean_expression) || !g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).stream()

// 'Design By Contract' stream API. For Broken Contracts:
// unit testing: it will throw std::runtime_error when a contract breaks
// I.R.L : it will exit the application by using fatal signal SIGABRT
#define CHECK(boolean_expression) \
if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).stream()
if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).stream()


/** For details please see this
Expand Down Expand Up @@ -209,22 +208,19 @@ And here is possible output
: Width trick: 10
: A string \endverbatim */
#define LOGF(level, printf_like_message, ...) \
if(!g3::logLevel(level)){ } else INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
if (!g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)

// Conditional log printf syntax
#define LOGF_IF(level,boolean_expression, printf_like_message, ...) \
if(true == (boolean_expression)) \
if(g3::logLevel(level)) INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
if (false == (boolean_expression) || !g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)

// Design By Contract, printf-like API syntax with variadic input parameters.
// Throws std::runtime_eror if contract breaks
#define CHECKF(boolean_expression, printf_like_message, ...) \
if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)
if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)

// Backwards compatible. The same as CHECKF.
// Backwards compatible. The same as CHECKF.
// Design By Contract, printf-like API syntax with variadic input parameters.
// Throws std::runtime_eror if contract breaks
#define CHECK_F(boolean_expression, printf_like_message, ...) \
if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)


if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)

0 comments on commit f149179

Please sign in to comment.