Как использовать идеальную пересылку при написании шаблонных функций, которые обертывают существующие функции и проверяют наличие ошибок? - PullRequest
2 голосов
/ 24 сентября 2019

Я хочу обернуть несколько вызовов функций в существующую библиотеку 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<R, Args...> func, Args... args) включал универсальную ссылку для пересылки (т. Е. Args&&... args), но эта подстановка вызывает ошибку компиляции - Clang в качестве примера дает note: candidate template ignored: deduced conflicting types for parameter 'Args' (<int, int> vs. <const int &, const int &>).

Как бы яизменить этот код, чтобы принимать универсальные ссылки?Или я что-то пропустил, и есть гораздо лучший способ проверки ошибок унаследованного кода?

1 Ответ

1 голос
/ 24 сентября 2019

Проблема в том, что у вас есть две функции шаблона, которые, с точки зрения компилятора, отличаются только типом возвращаемого значения.Чтобы решить эту проблему, вы можете использовать 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;
}

, и это решает проблему (предотвращая совпадение вышеуказанной функции шаблона, когда R равен void).

Live демо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...