Всего за 0.01 руб. Купить полную версию
void callbackHandler(int eventID) {}
void operator() (int eventID) {}
};
void ExternalHandler(int eventID, void* somePointer)
{
Executor* ptrClass = (Executor*)somePointer;
}
int main()
{
Executor executor;
int capturedValue = 0;
// (2) External function
using FunctionPointer = void(*)(int, void*);
using FunctionConverter = CallbackConverter<FunctionPointer, void*>;
run(FunctionConverter(ExternalHandler, &executor));
// (3) Static method
using StaticPointer = void(*)(int, Executor*);
using StaticConverter = CallbackConverter<StaticPointer, Executor*>;
run(StaticConverter(Executor::staticCallbackHandler, &executor));
// (4) Member merthod
using MethodPointer = void(Executor::*)(int);
using MethodConverter = CallbackConverter<MethodPointer, Executor>;
run(MethodConverter(&Executor::callbackHandler, &executor));
// (5) Functional object
run(executor);
// (6) lambda-expression
auto lambda = [capturedValue](int eventID) {/*it will be called by initiator*/};
run(lambda);
}
В строке 1 объявлен класс исполнителя, в котором определены все необходимые типы вызовов: статический метод, метод-член, перегруженный оператор. Для вызовов 2, 3 и 4 в качестве аргумента передается функциональный объект для преобразования, который инстанциируется соответствующими типами. В остальных случаях нужный аргумент передается непосредственно, преобразования вызовов там не нужно. При использовании лямбда-выражения (строка 6) компилятор неявно определит его тип и подставит его в функцию шаблона-инициатора как аргумент.
При использовании преобразования вызовов можно использовать сокращенную запись без дополнительного объявления промежуточных типов, в этом случае код получается более компактным, но более запутанным (см. Листинг 30)
Листинг 30. Преобразование вызовов без объявления промежуточныхтипов// (2) External function
run(CallbackConverter<void(*)(int, void*), void*>(ExternalHandler, &executor));
// (3) Static method
run(CallbackConverter<void(*)(int, Executor*), Executor*>(Executor::staticCallbackHandler, &executor));
// (4) Member merthod
run(CallbackConverter<void(Executor::*)(int), Executor>(&Executor::callbackHandler , &executor));
// (6) lambda-expression
run([capturedValue](int eventID) {/*it will be called by initiator*/});
4.3. Вызовы в алгоритмах
4.3.1. Описание проблемы
Алгоритмы краеугольный камень информатики, они встречаются практически во всех ее разделах. Таким образом, проектирование и разработка алгоритмов одна из важнейших задач как в теоретической науке, так и в инженерной практике.
В реализации алгоритмов одной из трудностей, встающей перед разработчиком, является адаптация для конкретной структуры данных. Это связано с тем, что алгоритмы задают последовательность операций, но не определяют данные, с которыми работают. Предполагается, что алгоритм работает с любой структурой данных.
Например, предположим, что мы написали код для алгоритма сортировки. Естественно предположить, что он будет сортировать числа. Но вот появилась новая задача: отсортировать строки. По сравнению с исходной реализацией у нас теперь другая структура данных (строки) и новые правила сравнения (строки сравниваются совсем не так, как числа). А ведь в будущем, возможно, появятся более сложные случаи например, сортировка структур по отдельным полям Как написать универсальный код, работающий с любыми типами данных?
4.3.2. Параметризация типов
Обозначенная выше проблема в рамках параметрического полиморфизма решается просто: код оформляется в виде шаблона, параметрами шаблона выступают типы данных. При инстанциировании шаблона генерируется код, в который подставляются соответствующие типы.
Поясним сказанное на примере. Предположим, мы реализовали алгоритм сортировки пузырьком (Листинг 31).
Листинг 31. Сортировка массива методом пузырькаvoid sort_bubble(int* data, size_t size)
{
for (size_t i = 0; i < size 1; i++)
{
for (size_t j = 0; j < size i 1; j++)
{
if (data[j + 1] < data[j])
{
int temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
}
Описанный код работает с числами. Параметризуем типы (Листинг 32):