Проверьте во время компиляции, является ли аргумент шаблона пустым
Я пытаюсь обернуть функции 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 ответа:
Вы можете использовать вспомогательный класс для точной настройки специализаций:
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); }
Обновление: скорректированы для преобразования типа аргумента в тип параметра.