Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
template_metaprogramming_ru.docx
Скачиваний:
12
Добавлен:
01.07.2025
Размер:
774 Кб
Скачать
☆

Пример:

Реализуем прототипирование алгоритма для работы некоторого изображения (заставки), поведение котрого необходимо настраивать динамически. Алгоритм для создания изображение на экране является довольно сложным, но он легок в настройке: изменяя простую числовую функцию, которая вызывается один раз за период (кадр) в ядре алгоритма, можно генерировать различные заготовки для изображения. Расточительно делать целую заставку (весь класс) изображения шаблоном для использования подобного просто механзима параметризации, вместо этого оптимальнее будет использовать указатель на функцию преобразования:

typedef float (*floatfunc)(float);

class screensaver

{

public:

explicit screensaver(floatfunc get_seed)

: get_seed(get_seed)

{}

pixel_map next_screen() // main algorithm

{

float center_pixel_brightness = ...;

float seed = this->get_seed(center_pixel_brightness);

//complex computation using seed...

}

private:

floatfunc get_seed;

//other members...

};

Если возникает необходимость добавления новых настроек. Например, новые настройки требуют создания вектора для 128 целочисленных параметров, которые изменяются при каждом вызове next_screen ().

Обобщение:

Добавим в класс screensaver элемент std::vector<int>, а также измененим список параметров next_screen, добавив дополнительного аргумент в функцию настройки:

class screensaver

{

pixel_map next_screen()

{

float center_pixel_brightness = ...;

float seed = this->get_seed(center_pixel_brightness, state);

//...

}

private:

std::vector<int> state;

float (*get_seed)(float, std::vector<int>& s);

//...

};

Такая модификация требует корректировки списка параметров и реализации самой функции преобразования, а в случае появления новых настроек, нужно будет вновь переписывать код реализаций.

Решение: дополнительный уровень абстракции. Если заменить механизм настройки посредством указателя на функцию – отдельным классом (вложенным/вынесенным – pimpl, что позволит даже не перекомпилировать тело класса), который сделает состояние настройки деталью внутренней реализации, тогда класс screensaver будет работать только с объектом класса настройки, изменения в котором не требуют модификации реализации основного алгоритма:

class screensaver

{

public:

struct customization // Интерфейс вложенной структуры настроек.

{

virtual ~customization() {}

virtual float operator()(float) const = 0;

};

explicit screensaver(std::auto_ptr<customization> c)

: get_seed(c)

{}

pixel_map next_screen()

{

float center_pixel_brightness = ...;

float seed = (*this->get_seed)(center_pixel_brightness);

//...

}

private:

std::auto_ptr<customization> get_seed;

//...

};

Динамический полиморфизм

Теперь можно написать класс наследник, который будет реализовывать интерфейс настроек, например содержит дополнительное состояние в качестве элемента, а также осуществляет сами настройки в теле оператора ():

struct hypnotic : screensaver::customization

{

float operator()(float) const

{

//...use this->state...

}

std::vector<int> state;

};

Если, теперь, понадобятся такие настройки, которые не нуждаются в векторе состояний, то можно выполнить просто другую реализацию посредством наследования (обертка для указателя на функцию настроек):

struct funwrapper : screensaver::customization

{

funwrapper(floatfunc pf)

: pf(pf) {}

float operator()(float x) const

{

return this->pf(x);

}

floatfunc pf; // stored function pointer

};

Так, достигается сокрытие информации о конкретном типе настроек в теле конкретной реализации единого интерфейса натсроек.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]