Обертывание любой функции API - PullRequest
2 голосов
/ 29 февраля 2012

Я обертываю Windows API и хочу сделать проверку ошибок простой и полезной.В настоящее время у меня есть объект глобальной ошибки с функцией set для обработки новой ошибки.Функция set принимает четыре аргумента: bool Error::set (const int code, const char * file, const char * const function, const int line); Функция использует аргументы file, function и line для отображения их в красиво отформатированном сообщении.

Чтобы упростить установку ошибок, есть макрос #define setError() error.set (GetLastError(), __FILE__, __FUNCTION__, __LINE__); Таким образом, я могу использовать setError() в любое время, чтобы ответить на ошибку, которую установила функция API, добавив ее после того, как явызовите эту функцию API.

К сожалению, это приводит к тому, что код выглядит примерно так:

SomeAPIFunction();
setError();
AnotherAPIFunction();
setError();

Также существует проблема с конструкторами:

MyClass:MyClass()
    : a (SomeAPIFunction), b (AnotherAPIFunction)
{
    setError(); //what if both functions set an error?
}

Каквы можете видеть, используя синтаксис инициализатора члена, я фактически ограничиваю себя.

Один из способов исправить это - обернуть каждую функцию API:

int someAPIFunction()
{
    int ret = SomeAPIFunction();
    setError();
    return ret;
}

Часть functionиз сообщения об ошибке скажет мне, какая функция возникла ошибка.Конечно, это должен быть наихудший из возможных способов решения этой проблемы.

Решение, по-видимому, заключается в использовании шаблонов с переменными параметрами.Проблема в том, что я понятия не имею, что я должен делать, чтобы заставить их работать на это.Я мог бы представить, что окончательный код выглядит примерно так:

wrap<int, SomeAPIFunction (5)>();
wrap<int, SomeAPIFunction, 5>();
wrap<int, SomeAPIFunction> (5);

Я читал что-то о начальных вариационных шаблонах, но они все оставили меня в неведении, как настроить что-то вроде этого,Может ли кто-нибудь указать мне правильное направление?

Я нашел следующее на аналогичный вопрос :

#include <iostream>

template<void f(void)>
struct Wrap {
   void operator()() const {
      std::cout << "Pre call hook" << std::endl;
      f();
   }
};

namespace {
   void test_func() {
      std::cout << "Real function" << std::endl;
   }
}

const Wrap<&test_func> wrapped_test_func = {};

int main() {
   wrapped_test_func();
   return 0;
}

Респондент отметил, что вариационные шаблоны были бы необходимостьюсделать это достаточно общим.Это начало, но я потерян и благодарен за любую помощь в этом вопросе.

1 Ответ

1 голос
/ 29 февраля 2012

Я думаю, вы сможете заставить его работать с этим синтаксисом:

wrap(&SomeAPIFunction, arg1, arg2);

Ключ должен позволить компилятору использовать дедукцию типа для определения параметров типа шаблона, так как они очень быстро запутываются.

Код должен выглядеть примерно так:

template<typename TRet, typename... TArgs>
TRet wrap( TRet(WINAPI *api)(TArgs...), TArgs... args )
{
    return api(args...);
}

Естественно, вы захотите использовать макрос, чтобы скрыть оператор адреса функции, использовать строку для сохранения имени функции, а также сохранить имя файла и номер строки, передавая все это фактической функции переменной. Для этого вам понадобятся переменные макросы. На самом деле, не могли бы вы сделать все это только с помощью макроса с переменным значением и без шаблонов?

...