当从宏传递到函数时,避免传递 void 表达式

Avoid passing void expression, when pass from macro to function

我想写一个调试宏,在出现错误时打印调用函数的名称。问题是一些函数 return 值,我想 return 从宏返回任何值。

这是我的尝试:

#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;
}

我遇到的问题是有时 T = void 并且编译器不会让我将 void 类型的表达式传递给函数:

../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));

我无法重新定义要在 check 宏或 check_for_error 函数下调用的函数,它们在我的程序之外。此外,需要在计算表达式后检查错误。

在C++中有解决这个问题的好方法吗?

类似于:“如果此表达式的 decltype 为空,则生成此代码,否则生成该代码”。

  • 初衷

如果您总是需要宏 #define check(expr) _check_expr(#expr, expr) ,则无法避免传递 void。宏没有重载或调度机制。

  • 更新想法

非常感谢@Daniel Jour 提出了将表达式包装到 lambda 中传递的想法,我在 C++11 中提出了一个实现(我更喜欢 C++11,因为我不太熟悉C++17 作为@Daniel Jour),灵感来自@Daniel Jour 的回答:

#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 检查此代码。

A void function can return a void expression。这也适用于 lambda:

#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;
    }
}

可能有更好的解决方案,但这行得通。此外,这至少需要当前形式的 C++17,但可能可以向后移植到 C++14 甚至 C++11。

https://wandbox.org/permlink/YHdoyKL0FIoJUiQb 检查代码。