2

I want to write a debugging macro which prints the name of the function called when there is an error. The catch is that some functions return values, and I would like to return any value back from the macro.

This is my attempt:

#define check(expr) _check_expr(#expr, expr)

extern bool check_for_error();

template <typename T> inline T _check_expr(const char *str, T value)
{
    if (check_for_error()) {
        fprintf(stderr, "Failure: %s\n", str);
    }

    return value;
}

The problem I get here is that sometimes T = void and the compiler will not let me pass an expression of void type to a function:

../src/render.cc: In constructor ‘render::impl::impl()’:
../src/render.cc:34:20: error: invalid use of void expression
   34 |     check(glDisable(GL_DEPTH_TEST));

I cannot redefine the functions to be called under the check macro, or the check_for_error function, which are external to my program. Also, checking for error needs to occur after evaluating the expression.

Is there a good way to solve this problem in C++?

Something like: "If decltype of this expression is void, then generate this code, otherwise generate that code".

rustyhu
  • 1,912
  • 19
  • 28
jforberg
  • 6,537
  • 3
  • 29
  • 47

2 Answers2

5

A void function can return a void expression. This also applies to lambdas:

#define check(expr) _check_expr(#expr, [&] () { return expr; })

extern bool check_for_error();

template <typename Fn>
inline auto _check_expr(const char *str, Fn fn)
{
    auto check_ = [str]() {
        if (check_for_error()) {
            fprintf(stderr, "Failure: %s\n", str);
        }
    };

    if constexpr (std::is_same_v<std::invoke_result_t<Fn>, void>) {
        fn();
        check_();
    } else {
        auto v = fn();
        check_();
        return v;
    }
}

There's probably a better solution but this works. Also this requires at least C++17 in the current form, but can probably be back ported to C++14 or even C++11.

https://wandbox.org/permlink/YHdoyKL0FIoJUiQb to check the code.

rustyhu
  • 1,912
  • 19
  • 28
Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
1
  • Original idea

You can not avoid pass void if you always need the macro #define check(expr) _check_expr(#expr, expr) . There is no overload or dispatch mechanism for macro.

  • Updated idea

Much thanks to @Daniel Jour for the idea of wrapping the expression into a lambda to pass, I come up with an implementation in C++11 (I prefer C++11 as I am not so familiar with C++17 as @Daniel Jour), inspired by @Daniel Jour's answer:

#include <iostream>
#include <type_traits>

#define check(expr) _check_expr(#expr, [&]() { return expr; })

//bool check_for_error() { return true; }
extern bool check_for_error();

void check_(const char *str) {
  if (check_for_error()) {
    std::cerr << "Failure in: " << str << std::endl;
  }
}

template <typename Fn>
using returnType = typename std::result_of<Fn()>::type;

template <typename Fn, typename = typename std::enable_if<
                           !std::is_void<returnType<Fn>>::value>::type>
inline auto _check_expr(const char *str, Fn fn) ->
    typename std::result_of<Fn()>::type {
  auto v = fn();
  check_(str);
  return v;
}

template <typename Fn, typename = typename std::enable_if<
                           std::is_void<returnType<Fn>>::value>::type>
inline void _check_expr(const char *str, Fn fn) {
  fn();
  check_(str);
}

https://wandbox.org/permlink/CxsuBYi7Ypz4qCDr to check this code.

rustyhu
  • 1,912
  • 19
  • 28