Практическое применение шаблона с переменным количеством параметров для самых маленьких
Пролог
Недавно у меня появилась потребность применить на практике такую вещь, как variadic templates. Нашел я пару статей здесь, на Хабре. Вот к примеру достаточно хорошая, чтобы познакомиться з тем, что же это за технология и как это работает.
Только проблема была в том, что первый же пример у меня не компилился с помощью MSVS компилятора (компилятор, встроенный в Visual Studio), а остальные нуждались в дополнительных библиотеках, которые нужно было скачать и прикрутить к проекту. К тому же, даже первый пример покажется слишком сложным новичку (как, например, он сперва показался мне :) ), поэтому я решил написать эту статью...
От слов к делу
Проблема
Есть много различных методов и фунций, которые нуждаются в многократном повторении выполнения до тех пор, пока не выполнится какое либо условие ИЛИ не выйдет их время. Например, функция проверяет, появилось ли какое нибудь окно на рабочем столе не больше 5 секунд, и как только оно появилось, возвращает результат. Проще говоря, нужна функция-таймер, которая будет принимать другую функцию и выполнять ее на протяжении данного времени. Главная проблема здесь — универсальность.
Постановка задачи
Делаем функцию, которая будет принимать на вход указатель на другую функцию(с возвращаемым типом bool), время(тайм-аут), и выполнять ее, пока функция-аргумент не вернет true или не истечет время.
Реализация
Приведу самый простой (на мой взгляд) пример реализации, который работает у меня в VS 2015 с конфигом Platform toolset: Visual Studio 2015(v140).
/* Пример */
#include <Windows.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
// Магия тут
// Самое сложное здесь - правильно обьявить указатель на функцию
template <typename ... Args>
bool smartWaiter(DWORD timeout, bool(*func)(Args ...), Args ... args) // Таймаут, указатель, аргументы
{
DWORD start = ::GetTickCount(); //старт отчета
bool result = func(args ...);
while (!result && ::GetTickCount() - start < timeout) // условие: пока не получим true или жє пока время не выйдет
result = func(args ...);
return result; // если время закончится и будет false, мы вернем false, а если выйдем преждевременно, то получим true
}
// Дальше просто примеры функций
bool someFunc(int a, const char* str, char* x)
{
// some operations which depend on time/state
return true;
}
bool someAnotherFunc(const char* b)
{
// some operations which depend on time/state
return true;
}
bool someVoidFunc()
{
// some operations which depend on time/state
return true;
}
int main()
{
const DWORD DEFAULT_TIMEOUT = 1000; // 1000 милисекунд, что равно 1 секунде
bool result;
std::vector<bool> results;
// и немножно магии тут, главное здесь - правильно специализировать шаблон, а дальше уже
// через запятую передать тот же таймер, указатель, ну и затем параметры, типы которых мы указали
result = smartWaiter <int, const char*, char*>(DEFAULT_TIMEOUT, someFunc, 1, "Yeap", "x");
results.push_back(result);
result = smartWaiter <const char*>(DEFAULT_TIMEOUT, someAnotherFunc, "Yeap");
results.push_back(result);
// можно вызывать и функицю без параметров
result = smartWaiter<>(DEFAULT_TIMEOUT, someVoidFunc);
results.push_back(result);
// просто вывод возвращаемых результатов
// чтобы показать, что функция действительно была вызвана
std::copy(results.begin(), results.end(), std::ostream_iterator<bool>(std::cout, " \n"));
system("pause");
}
Эпилог
С этим можно еще много эксперементировать, в частности, сделать возвращаемый тип R вместо bool, но тогда следует предусмотреть реализацию сравнения результата, чтобы знать, когда мы должны возвратить его преждевременно.
Надеюсь, Вам было приятно это читать и, самое главное, полезно.