Инициализация класса
От: remark Россия http://www.1024cores.net/
Дата: 20.11.05 15:33
Оценка:
Предлагается решение следующей проблемы:

Имеется интерфейс обработчика запроса:

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.

Пока ничего подобного в литературе не встречал (ни у Саттера, ни у (как ни странно ) Александреску).

Если у кого есть какие замечания/предложения/вопросы/комментарии/ссылки — прошу сюда.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Инициализация класса
От: Pavel Chikulaev Россия  
Дата: 20.11.05 16:06
Оценка:
Здравствуйте, remark, Вы писали:

R>Основа решения — класс "инициализатор класса":

R>(версия под компилятор msvc 7.1 или выше, т.к. используются __if_exists и __declspec(selectany), на других компиляторах возможно имеются аналоги)
Легко заменяется на стандартный C++ с помощью SFINAE. См. http://www.rsdn.ru/Forum/?mid=382857
Автор: MaximE
Дата: 13.09.03
Re[2]: Инициализация класса
От: remark Россия http://www.1024cores.net/
Дата: 21.11.05 05:46
Оценка:
Здравствуйте, Pavel Chikulaev, Вы писали:

PC>Здравствуйте, remark, Вы писали:


R>>Основа решения — класс "инициализатор класса":

R>>(версия под компилятор msvc 7.1 или выше, т.к. используются __if_exists и __declspec(selectany), на других компиляторах возможно имеются аналоги)
PC>Легко заменяется на стандартный C++ с помощью SFINAE. См. http://www.rsdn.ru/Forum/?mid=382857
Автор: MaximE
Дата: 13.09.03



Да пожалуйста, сколько угодно, только я это делать не буду.

З.ы. как интересно __declspec(selectany) заменяется с помощью SFINAE?

1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Инициализация класса
От: Alex Alexandrov США  
Дата: 22.11.05 22:02
Оценка:
Здравствуйте, remark, Вы писали:

R>Предлагается решение следующей проблемы:


R>Имеется интерфейс обработчика запроса:


[...]

Можно обойтись без selectany:

#ifndef __CLASS_INITIALIZER_H__
#define __CLASS_INITIALIZER_H__

template<typename Class>
class ClassInitializer
{
public:
    ClassInitializer()
    {
        if (!m_count++)
        {
            // Execute this when the counter becomes 1 from 0.
            __if_exists(Class::initializeClass)
            {
                Class::initializeClass();
            }
        }
    }

    ~ClassInitializer()
    {
        if (!--m_count)
        {
            // Execute this when the counter becomes 0 from 1.
            __if_exists(Class::uninitializeClass)
            {
                Class::uninitializeClass();
            }
        }
    }

    static size_t m_count;

};

template<typename Class>
size_t ClassInitializer<Class>::m_count;

#endif
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
It's kind of fun to do the impossible (Walt Disney)
Re[2]: Инициализация класса
От: remark Россия http://www.1024cores.net/
Дата: 25.11.05 12:18
Оценка:
Здравствуйте, Alex Alexandrov, Вы писали:

AA>Можно обойтись без selectany:


AA>
AA>#ifndef __CLASS_INITIALIZER_H__
AA>#define __CLASS_INITIALIZER_H__

AA>template<typename Class>
AA>class ClassInitializer
AA>{
AA>public:
AA>    ClassInitializer()
AA>    {
AA>        if (!m_count++)
AA>        {
AA>            // Execute this when the counter becomes 1 from 0.
AA>            __if_exists(Class::initializeClass)
AA>            {
AA>                Class::initializeClass();
AA>            }
AA>        }
AA>    }

AA>    ~ClassInitializer()
AA>    {
AA>        if (!--m_count)
AA>        {
AA>            // Execute this when the counter becomes 0 from 1.
AA>            __if_exists(Class::uninitializeClass)
AA>            {
AA>                Class::uninitializeClass();
AA>            }
AA>        }
AA>    }

AA>    static size_t m_count;

AA>};

AA>template<typename Class>
AA>size_t ClassInitializer<Class>::m_count;

AA>#endif
AA>



Плохо, ибо:
1. Лексему __CLASS_INITIALIZER_H__ нельзя использовать в программах на С++

2. Статическую переменную size_t m_count использовать плохо, т.к.:
2.1. Инициализация будет происходить при создании первого экземпляра (!). В моём примере, например, экземпляры не будут создаваться пока не произойдёт инициализация класса. Это может происходить уже в многопоточном окружении!
2.2. Всосёт в многопоточной окружении.
2.3. Инициализация и деинициализация могут происходить несколько раз, что не то что нужно.
2.4. При каждом создании экземпляра будет происходить лишнаяя работа по проверке переменной (плюс критическая секция).

3. Без selectany работать не будет, т.к. если включить этот файл в несколько cpp файлов, то будет несколько экземпляров переменной — неслинкуется.





1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Инициализация класса
От: Alex Alexandrov США  
Дата: 25.11.05 16:12
Оценка: :)
Здравствуйте, remark, Вы писали:

Итак, по порядку.

R>Плохо, ибо:

R>1. Лексему __CLASS_INITIALIZER_H__ нельзя использовать в программах на С++

М-м-м. Это еще почему? И зачем ее использовать? Это же include guard просто.

R>2. Статическую переменную size_t m_count использовать плохо, т.к.:

R>2.1. Инициализация будет происходить при создании первого экземпляра (!). В моём примере, например, экземпляры не будут создаваться пока не произойдёт инициализация класса. Это может происходить уже в многопоточном окружении!
R>2.2. Всосёт в многопоточной окружении.

Проблема реализации, а никак не дизайна. Что мешает использовать Interlocked* функции для изменения/проверки значения счетчика?

R>2.3. Инициализация и деинициализация могут происходить несколько раз, что не то что нужно.


Они будут происходить один раз на каждый тип, и мне показалось, что это именно то, что нужно.

R>2.4. При каждом создании экземпляра будет происходить лишнаяя работа по проверке переменной (плюс критическая секция).


Без критической секции можно обойтись. Работа по проверке переменной — это да. Но ты уверен, что это будет узким местом в твоей программе? Ты профилировал и профилировщик сказал тебе это?

R>3. Без selectany работать не будет, т.к. если включить этот файл в несколько cpp файлов, то будет несколько экземпляров переменной — неслинкуется.


Слинкуется. Если бы статическая переменная была нешаблонной — не слинковалось бы. А так слинкуется. Или где бы тогда по-твоему должна была бы производиться инициализация статических членов шаблонного класса?

В общем, дело твое. Мое дело — предложить.
It's kind of fun to do the impossible (Walt Disney)
Re[4]: Инициализация класса
От: remark Россия http://www.1024cores.net/
Дата: 25.11.05 16:37
Оценка:
Здравствуйте, Alex Alexandrov, Вы писали:

AA>Здравствуйте, remark, Вы писали:


AA>Итак, по порядку.


R>>Плохо, ибо:

R>>1. Лексему __CLASS_INITIALIZER_H__ нельзя использовать в программах на С++

AA>М-м-м. Это еще почему? И зачем ее использовать? Это же include guard просто.


Лексемы с двойным подчёркиваем использовать нельзя, они зарезервированы.

R>>2. Статическую переменную size_t m_count использовать плохо, т.к.:

R>>2.1. Инициализация будет происходить при создании первого экземпляра (!). В моём примере, например, экземпляры не будут создаваться пока не произойдёт инициализация класса. Это может происходить уже в многопоточном окружении!
R>>2.2. Всосёт в многопоточной окружении.

AA>Проблема реализации, а никак не дизайна. Что мешает использовать Interlocked* функции для изменения/проверки значения счетчика?


R>>2.3. Инициализация и деинициализация могут происходить несколько раз, что не то что нужно.


AA>Они будут происходить один раз на каждый тип, и мне показалось, что это именно то, что нужно.


Интересно. А если создать один объект объект, потом разрушить его, потом создать второй объект и разрушить его. Сколько инициализаций вызоветься?


R>>2.4. При каждом создании экземпляра будет происходить лишнаяя работа по проверке переменной (плюс критическая секция).


AA>Без критической секции можно обойтись. Работа по проверке переменной — это да. Но ты уверен, что это будет узким местом в твоей программе? Ты профилировал и профилировщик сказал тебе это?


Если функция принимает std::vector только для чтения, то ты её как передаёшь? Как std::vector? Пока не увидишь, что в этом месте тормозит? Я лично сразу передаю как const std::vector&



R>>3. Без selectany работать не будет, т.к. если включить этот файл в несколько cpp файлов, то будет несколько экземпляров переменной — неслинкуется.


AA>Слинкуется. Если бы статическая переменная была нешаблонной — не слинковалось бы. А так слинкуется. Или где бы

тогда по-твоему должна была бы производиться инициализация статических членов шаблонного класса?

Согласен, это предусмотрено для шаблонов.



Концепция конструктор/деструктор — это специальная языковая конструкция для инициализации/деинициализации, корректная работа в данном случае обеспечивается компилятором.
Я таким образом смог переложить концепцию конструктор/деструктор с объекта на класс.

1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: Инициализация класса
От: Alex Alexandrov США  
Дата: 26.11.05 10:10
Оценка:
Здравствуйте, remark, Вы писали:

R>Лексемы с двойным подчёркиваем использовать нельзя, они зарезервированы.


А, ты про это. Да фиг с ними — к теме топика никакого отношения не имеет. Но ты прав здесь. С 6-ой студии привычка осталась — надо отучаться.

AA>>Они будут происходить один раз на каждый тип, и мне показалось, что это именно то, что нужно.


R>Интересно. А если создать один объект объект, потом разрушить его, потом создать второй объект и разрушить его. Сколько инициализаций вызоветься?


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

R>>>2.4. При каждом создании экземпляра будет происходить лишнаяя работа по проверке переменной (плюс критическая секция).


AA>>Без критической секции можно обойтись. Работа по проверке переменной — это да. Но ты уверен, что это будет узким местом в твоей программе? Ты профилировал и профилировщик сказал тебе это?


R>Если функция принимает std::vector только для чтения, то ты её как передаёшь? Как std::vector? Пока не увидишь, что в этом месте тормозит? Я лично сразу передаю как const std::vector&


Не надо передергивать. Передача по константной ссылке пишется автоматом, потому что она работает везде. Твой selectany не стандартен. Если бы передача по константной ссылке была, скажем, Microsoft-specific, я бы использовал ее только в случае доказанной профайлером необходимости.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
It's kind of fun to do the impossible (Walt Disney)
Re[6]: Инициализация класса
От: remark Россия http://www.1024cores.net/
Дата: 26.11.05 13:28
Оценка:
Здравствуйте, Alex Alexandrov, Вы писали:

Собственно, я не вижу у Вашего варианта никаких достоинств перед моим (если из моего убрать selectany). Поэтому не понимаю смысла.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.