Предлагается решение следующей проблемы:
Имеется интерфейс обработчика запроса:
struct IProcessor
{
virtual void process() = 0;
};
Далее в программе имеется множество реализаций интерфейса для различных типов запросов. Проблема заключается в том как автоматически и масштабируемо поддерживать множество всех реализаций обработчиков запросов, т.е. автоматически иметь фабрику для всех обработчиков, иметь возможность вывести в интерфейс пользователя список имеющихся обработчиков и т.д.
Т.е. для реализации обработчика запроса необходима некая
инициализация класса, в которой он мог бы добавить свой класс в список всех обработчиков запросов.
Основа решения — класс "инициализатор класса":
(версия под компилятор msvc 7.1 или выше, т.к. используются __if_exists и __declspec(selectany), на других компиляторах возможно имеются аналоги)
#pragma once
// ClassInitializer.h
#pragma once
/** Инициализатор класса */
template<typename Class>
class ClassInitializer
{
private:
/** Вспомогательная структара */
struct Initializer
{
Initializer()
{
// необходимо, т.к. не каждый класс хочет явно определять
// и инициализацию и деинициализацию класса
__if_exists(Class::initializeClass)
{
Class::initializeClass();
}
}
~Initializer()
{
// необходимо, т.к. не каждый класс хочет явно определять
// и инициализацию и деинициализацию класса
__if_exists(Class::uninitializeClass)
{
Class::uninitializeClass();
}
}
void fake()
{}
};
static Initializer m_initializer;
protected:
ClassInitializer()
{
// Необходимо для борьбы с ленивой генерацией кода для шаблонов
m_initializer.fake();
}
};
template<typename Class>
__declspec(selectany)
typename ClassInitializer<Class>::Initializer
ClassInitializer<Class>::m_initializer;
Необходимо отнаследоваться от класса ClassInitializer и инстанциировать его собственным классом. Далее необходимо определить публичные статические функции initializeClass() и uninitializeClass(), которые будут вызываться для класса один раз вначале и один раз в конце работы программы (т.е. конструктор и деструктор для метакласса).
Полностью весь код приводить не буду (он достаточно тривиален). Вот определение базового класса для всех обработчиков (этот класс отвечает за всю работу по поддержке списка всех обработчиков):
template<typename Derived>
class Processor : public IProcessor, private ClassInitializer<Derived>
{
public:
static void initializeClass()
{
ProcessorFactory::addWrapper(new CProcessorWrapper<Derived>);
}
static void uninitializeClass()
{
ProcessorFactory::removeWrapper(Derived::CODE);
}
};
Сам конкретный обработчик выглядит примерно так:
class Processor1 : public Processor<Processor1>
{
public:
static const int REQUEST_TYPE = 1;
Processor1()
{}
virtual void process()
{}
};
Использование всего этого дела выглядит примерно так:
void processRequest(int RequestType)
{
IProcessorWrapper* wrapper = ProcessorFactory::getWrapper(RequestType);
log << "Request: type = " << wrapper->getTypeName();
IProcessor* processor = wrapper->create();
processor->process();
}
void displayProcessorsList()
{
ProcessorList list = ProcessorFactory::getList();
display(list);
}
Всё что нужно для добавления в программу нового обработчика — определить его класс — далее вся его поддержка при обработке запросов и в интерфейсе пользователя происходит автоматически.
Обработка запросов приведена в качестве примера использования, область применения этого приёма, конечно, гораздо шире. Его можно применять не только для поддержки списка типов, но и для выполнения некой разовой инициализации для одного класса.
Пока, правда, имеется нерешённая проблема — у производного от ClassInitializer (листового) класса обязательно должен быть констуктор. Иначе весь этот механизм не вступит в силу. В данном примере наличие коструктора можно форсировать наличием не-дефаултного конструктора у класса Processor.
Пока ничего подобного в литературе не встречал (ни у Саттера, ни у (как ни странно
) Александреску).
Если у кого есть какие замечания/предложения/вопросы/комментарии/ссылки — прошу сюда.