Search code examples
c++metaprogramming

Avoid passing void expression, when pass from macro to function


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".


Solution

  • 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.