Проверьте во время компиляции, является ли аргумент шаблона недействительным - PullRequest
12 голосов
/ 09 марта 2012

Я пытаюсь обернуть функции 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.

Ответы [ 3 ]

8 голосов
/ 09 марта 2012

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

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)...);
}

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

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

2 голосов
/ 09 марта 2012

Чтобы обойти это, я попытался создать версию шаблона, где я заменил TRet на void.К сожалению, это на самом деле просто вызывает двусмысленность того, какой использовать.Вы указываете, что это не так.Возможно, я что-то упускаю, но, во всяком случае, это не имеет значения, вы можете предотвратить выбор перегрузки 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);
}

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

1 голос
/ 09 марта 2012

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

...