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

StackOverflow https://stackoverflow.com//questions/9625526

Вопрос

Я пытаюсь обернуть функции Windows API для проверки ошибок, когда захочу.Как я узнал в предыдущем вопросе 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.

Это было полезно?

Решение

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

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 и добавлением дополнительного параметра, должны работать, чтополучилось?Возможно, вам придется вызывать с явными типами.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top