【问题标题】:How do I use perfect forwarding when writing template functions that wrap existing functions and check for errors?在编写包装现有函数并检查错误的模板函数时,如何使用完美转发?
【发布时间】:2020-01-24 22:42:11
【问题描述】:

我想包装一些对现有 C 库的函数调用,该库调用该函数,检查是否设置了错误条件,然后返回函数的值(如果有)。 (具体来说,这适用于 OpenGL,但也适用于遗留的 C 函数。)由于函数可能返回 void ,这需要单独处理,这使情况变得复杂;并且由于我想抛出异常,这使我无法在保护对象超出范围时检查其析构函数。

以下代码基本有效:

void check_for_error() {
// check and handle legacy error messages
// if (errno != 0)
// if (glGetError() != GL_NO_ERROR)
//   throw std::runtime_error{"suitable error message"};
}

template <class R, class... Args>
using RvalFunc = R(*)(Args...);

// specialisation for funcs which return a value
template <class R, class... Args>
R exec_and_check(RvalFunc<R, Args...> func, Args... args) {
  R rval = func(std::forward<Args>(args)...);
  check_for_error();
  return rval;
}

template <class... Args>
using VoidFunc = void(*)(Args...);

// specialisation for funcs which return void - don't store rval
template <class... Args>
void exec_and_check(VoidFunc<Args...> func, Args... args) {
  func(std::forward<Args>(args)...);
  check_for_error();
}

示例用法:

exec_and_check(glBindBuffer, target, name);
FILE *pf = exec_and_check(fopen, "filename.txt", "rb");

...而不是...

glBindBuffer(target,name);
check_for_error();
FILE *pf = fopen("filename.txt", "rb");
check_for_error();

...检查可能会遗漏的地方,以及代码混乱的地方。我希望 R exec_and_check(RvalFunc&lt;R, Args...&gt; func, Args... args) 包含用于转发的通用引用(即 Args&amp;&amp;... args),但这种替换会导致编译错误 - Clang 给出了 note: candidate template ignored: deduced conflicting types for parameter 'Args' (&lt;int, int&gt; vs. &lt;const int &amp;, const int &amp;&gt;) 的示例。

如何修改此代码以接受通用引用?还是我遗漏了什么,有更好的方法来检查遗留代码?

【问题讨论】:

    标签: c++ templates error-handling


    【解决方案1】:

    问题是您有两个模板函数,就编译器而言,它们仅在返回类型上有所不同。要解决此问题,您可以按如下方式使用 SFINAE:

    // specialisation for funcs which return a value
    template <class R, class... Args>
    std::enable_if_t<!std::is_void<R>::value, R>
    exec_and_check(RvalFunc<R, Args...> func, Args... args) {
      R rval = func(std::forward<Args>(args)...);
      check_for_error();
      return rval;
    }
    

    这解决了问题(通过防止上述模板函数在Rvoid 时匹配)。

    Live demo

    【讨论】:

    • 恐怕这不是问题 - RvalFunc 和 VoidFunc 是函数参数,Clang 能够解决它们。我遇到的问题是我不能写exec_and_check(RvalFunc&lt;R, Args...&gt; func, Args&amp;&amp;... args) 以获得完美的转发,这意味着传递给这些模板的某些参数不能按预期工作。不过,谢谢。
    • 有趣。如果没有建议的更改,gcc 无法解决它们,我认为这就是问题所在。另请参阅this(请注意两个模板中的Args&amp;&amp;)。这就是你想要的吗?
    猜你喜欢
    • 1970-01-01
    • 2017-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-26
    相关资源
    最近更新 更多