2012-02-28 3 views
2

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

Чтобы облегчить установку ошибок, существует макрос #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); 

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

Я нашел следующее на a similar question:

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

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

+0

Для начала не все функции API используют механизм GetLastError. И тогда, еще более хлопотно, можно только вызвать GetLastError, когда эти функции API сообщают об ошибке.Таким образом, код в вашем вопросе обречен на провал. –

+0

Большинство функций API Win32 задают значение 'LastError' *, только если на самом деле была ошибка *. В случае успеха они оставляют значение «LastError» в одиночку. Есть исключения из этого; документация по Win32 API точно описывает, что делает каждая функция. –

+0

Я знаю об этом. Если 'GetLastError' возвращает 0 (ERROR_SUCCESS), он игнорируется. Я знаю, что некоторые части также используют другие механизмы, но я пока не добавил к ним никакой поддержки. По мере того, как я продолжаю расширяться, я добавлю, что необходимо. Вызов 'SetLastError (0);' когда я закончу обработку этого в рамках подготовки к следующему вызову функции API, также является возможностью. – chris

ответ

1

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

wrap(&SomeAPIFunction, arg1, arg2); 

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

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

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

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

+0

Похоже, что это должно работать с быстрым взглядом и простым в использовании :) Я попробую. – chris

+0

Я пробовал это, похоже, проблема с 'std :: forward()' или '(int &)' или тем, что вы нажимаете, не объявляя, а просто другими формами. Я попытался немного поиграть с ним, но ничего не получил. Я не думаю, что компилятор требует/и на самом деле, мне никогда не придется вводить аргументы функции. О переменных макросах, похоже, это сработает. Во все более запутанной поспешности шаблона я забыл об этом. – chris

+1

@chris: Вы правы, что вам не нужна совершенная переадресация при вызове C-совместимого API. Поэтому просто отпустите 'std :: forward' и rvalue-reference. –

Смежные вопросы