Проверьте во время компиляции, является ли аргумент шаблона пустым


Я пытаюсь обернуть функции API Windows для проверки ошибок, когда я так выбираю. Как я выяснил в предыдущем вопросе SO, я мог бы использовать шаблонную функцию для вызова функции API, а затем вызвать GetLastError() для извлечения любой ошибки, которую она могла бы установить. Затем я мог бы передать эту ошибку в свой класс Error, чтобы сообщить мне об этом.

Вот код для функции шаблона:

template<typename TRet, typename... TArgs>
TRet Wrap(TRet(WINAPI *api)(TArgs...), TArgs... args)
{
    TRet ret = api(args...);
    //check for errors
    return ret;
}

Используя это, я могу получить следующий код

int WINAPI someFunc (int param1, BOOL param2); //body not accessible

int main()
{
    int ret = someFunc (5, true); //works normally
    int ret2 = Wrap (someFunc, 5, true); //same as above, but I'll get a message if there's an error
}

Это прекрасно работает. Тем не менее, есть одна из возможных проблем. Возьмем функцию

void WINAPI someFunc();

При вложении этой функции в функцию шаблона она выглядит следующим образом:

void Wrap(void(WINAPI *api)())
{
    void ret = api(); //<-- ahem! Can't declare a variable of type void...
    //check for errors
    return ret; //<-- Can't return a value for void either
}
Чтобы обойти это, я попытался создать версию шаблона, где я заменил TRet на void. К сожалению, это на самом деле просто вызывает двусмысленность, какой из них использовать.

Это в сторону, я попробовал использовать

if (strcmp (typeid (TRet).name(), "v") != 0) //typeid(void).name() == "v"
{
    //do stuff with variable to return
}

else
{
    //do stuff without returning anything
}

Однако typeid является сравнением времени выполнения, поэтому код все еще не компилируется из-за попытки объявить переменную void, даже если этого никогда не случится.

Затем я попытался использовать std::is_same <TRet, void>::value вместо typeid, но обнаружил, что это также сравнение во время выполнения.

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

Я использую Code:: Blocks с GNU G++ 4.6.1 и Windows XP, а также Windows 7. Спасибо за любую помощь, даже если это говорит мне, что мне придется просто не использовать Wrap для функций, которые возвращают void.

3 12

3 ответа:

Вы можете использовать вспомогательный класс для точной настройки специализаций:

template <typename F>
struct wrapper
{};

template <typename Res, typename... Args>
struct wrapper<Res(Args...)>
{
    static Res wrap(Res (WINAPI *f)(Args...), Args&& args...)
    {
        Res r = f(std::forward<Args>(args)...);
        // Blah blah
        return r;
    }
};

template <typename... Args>
struct wrapper<void(Args...)>
{
    static void wrap(void (WINAPI *f)(Args...), Args&& args...)
    {
        f(std::forward<Args>(args)...);
        // Blah blah
    }
};

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

template <typename Res, typename... Args>
Res Wrap(Res (WINAPI *f)(Args...), Args&& args...)
{
    return wrapper<Res(Args...)>::wrap(f, std::forward<Args>(args)...);
}

Обратите внимание, что он работает даже тогда, когда Res составляет void. Вам разрешено return выражение, возвращающее void в функции, возвращающей void.

Правильный тип выводится, как и в Wrap(someFunc, 5, true), даже для функций, возвращающих void.

Чтобы обойти это, я попытался создать версию шаблона, где я заменил TRet на void. К сожалению, это на самом деле просто вызывает двусмысленность, какой из них использовать.

Это должно работать, я полагаю, потому что void более специализировано, чем TRet, но, как вы указываете, это не так. я могу что-то упустить, но в любом случае, это не имеет значения, вы можете предотвратить перегрузку TRet от выбора.

template<typename TFun, typename... TArgs>
auto Wrap(TFun api, TArgs&&... args) ->
typename std::enable_if<
    !std::is_void<typename std::result_of<TFun(TArgs...)>::type>::value,
    typename std::result_of<TFun(TArgs...)>::type
>::type
{
    auto result = api(std::forward<TArgs&&>(args)...);
    return result;
}

template<typename TFun, typename... TArgs>
auto Wrap(TFun api, TArgs&&... args) ->
typename std::enable_if<
    std::is_void<typename std::result_of<TFun(TArgs...)>::type>::value,
    typename std::result_of<TFun(TArgs...)>::type
>::type
{
    api(std::forward<TArgs&&>(args)...);
}

void WINAPI f1()
{
}

void WINAPI f2(double)
{
}

int WINAPI f3()
{
    return 0;
}

int WINAPI f4(double)
{
    return 0;
}

int main() {
    Wrap(f1);
    Wrap(f2, 0);
    return Wrap(f3) * Wrap(f4, 0);
}

Обновление: скорректированы для преобразования типа аргумента в тип параметра.

продвигаясь от комментария к ответу я понимаю, почему специализация возвращаемого типа как void не сработала (не могу разобраться в типах возвращаемых данных) , но специализация с void и добавление дополнительного параметра должны работать, что произошло? Возможно, вам придется вызывать явные типы.