Этой задачачей я занимаюсь несколько месяцев и сейчас уже готова альфа-версия фреймворка. Поэтому и решил систематизировать имеющиеся данные и предпосылки, чтобы выложить на Ваше усмотрение. Уверен, достаточно много будет возражений и дополнений по этой теме, что несомненно позволит проработать систему и привести ее в вид, пригодный для использования.
сначала, немного теории.
Эволюция классов в C++
1. Мутация (Mutation)
1.1. Мутацией будем называть операцию замену объекта оного типа в памяти другим. Мутация в частичном виде существует в скриптовых языках высокого уровня (PHP, Perl) для простых типов в виде неявного преобразования строк, целых чисел, чисел с плавающей запятой и т.п.
1.2. Реализация мутации простых типов в C++ не реализуется без потери соответствия стандарту, поэтому рассматриваться здесь не будет.
1.3. Мутация классов в C++ реализуема при помощи оператора placement new.
1.4. Правила и ограничения мутации
1.4.1. Мутация классов в стеке возможна в том случае, если объем нового объекта не превышает объем исходного.
1.4.2. Мутация классов в динамической памяти возможна в том случае, если объем нового объекта не превышает объем памяти, выделенной для старого объекта.
1.4.3. Использование регулярных и статических методов нового типа, так же как и обращение к публичным переменным результата мутации правомочно лишь в случае явного приведения этого объекта к новому типу.
1.4.4. Использование регулярных и статических методов старого типа, так же как и обращение к его переменным по отношению к результату мутации не допустимо и приводит к неопределенному поведению.
1.4.5. Использование виртуальных методов, одинаковых по описанию и порядковому номеру в виртуальной таблице допустимо без приведений.
1.5. Разрешенной будем считать мутацию, условия проведения и использования которой не противоречат пунктам 1.5.1. — 1.5.5. 2. Эволюция (Evolution)
2.1. Частный случай разрешенной мутации, операндами которой являются объекты, имеющие общий предок или наследуемые один от другого, будем называть эволюцией.
2.2. Эволюцию, в ходе которой данные, содержащиеся в исходном объекте, теряются, будем считать деструктивной.
Пример:
#include <new.h>
#include <iostream>
class Base{
public:
virtual ~Base () {};
virtual void Ident()=0;
};
class Derived1: public Base
{
public:
Derived1():data(1) {};
virtual ~Derived1 () {};
virtual void Ident() { cout<<"Derived1"<<endl; }
int data;
};
class Derived2: public Base
{
public:
Derived2():dat(2){};
virtual ~Derived2 () {};
virtual void Ident() { cout<<"Derived2"<<endl; }
int dat;
};
int main ()
{
Base * object=new Derived1();
object->Ident();
object->~Base();
object=new(object) Derived2();
object->Ident();
delete object;
}
2.3. Эволюцию, в ходе которой все данные исходного объекта или их часть сохраняется в новом, будем считать конструктивной.
Пример:
#include <new.h>
#include <iostream>
class Base{
public:
virtual ~Base () {};
virtual void Ident()=0;
virtual int getData()=0;
virtual void setData(int)=0;
};
class Derived1: public Base
{
public:
Derived1():data(1) {};
virtual ~Derived1 () {};
virtual void Ident() { cout<<"Derived1, data: "<<data<<endl; }
virtual int getData() { return data; }
virtual void setData(int Data) { data=Data; }
private:
int data;
};
class Derived2: public Base
{
public:
Derived2():dat(2){};
virtual ~Derived2 () {};
virtual void Ident() { cout<<"Derived2, dat: "<<dat<<endl; }
virtual int getData() { return dat; }
virtual void setData(int Data) { dat=Data; }
private:
int dat;
};
int main ()
{
Base * object=new Derived1();
object->Ident();
int temp=object->getData();
object->~Base();
object=new(object) Derived2();
object->setData(temp);
object->Ident();
delete object;
}
....
В дальнейшем есть смысл рассматривать только конструктивную эволюцию.Ее реализация напрямую связана с задачей сериализации, но это уже будем обсуждать в следующей теме.
Для этой части пока все.
Это, — теоретическая база, и если окажется, что какая-то ее часть требует детального пересмотрения и переработки, — дальнейшие дальнейшие части соответственно изменятся.
"..ты только посмотри на это деградирующее новое поколение, — даже "Момент", который они нюхают придумали до них!" (с) ZerG
Здравствуйте, Гарбузенко Алексей, Вы писали:
ГА>Этой задачачей я занимаюсь несколько месяцев и сейчас уже готова альфа-версия фреймворка. Поэтому и решил систематизировать имеющиеся данные и предпосылки, чтобы выложить на Ваше усмотрение. Уверен, достаточно много будет возражений и дополнений по этой теме, что несомненно позволит проработать систему и привести ее в вид, пригодный для использования.
Здравствуйте, Гарбузенко Алексей, Вы писали:
ГА>Этой задачачей я занимаюсь несколько месяцев и сейчас уже готова альфа-версия фреймворка.
Советую посидеть над ней еще несколько месяцев.
При написании программ я придерживаюсь следующих правил: программа должа быть очень понимаема другими программистами(для ее поддержки например), максимально стабильна(т.е. максимум придерживаться стандарта и без грязных хаков), и конечно при возможности алгоритм дожен быть как можно короче, но не теряя функционала и понимания(это правило также связана косвенно с двумя предыдущими). Все это также ускоряет процесс отладки.
Вот теперь подумай что из твоих слов удовлетворяет этим требованиям.
ЗЫ: Ты конечно можешь сказать что это лично мои предрассудки, тогда да, можешь писать проги со страшным кодом и которые постоянно виснут.
ГА> 1.3. Мутация классов в C++ реализуема при помощи оператора placement new.
ГА>
ГА> 1.4.1. Мутация классов в стеке возможна в том случае, если объем нового
ГА> объекта не превышает объем исходного.
Еще есть требование выполнения alignment requirements, которые могут зависеть
не только от sizeof класса, но и от определений его членов. Соответственно в
общем случае одного ограничения по "объему" не достаточно.
ГА>
ГА> 1.4.2. Мутация классов в динамической памяти возможна в том случае, если
ГА> объем нового объекта не превышает объем памяти, выделенной для старого
ГА> объекта.
Здесь все в порядке: при динамическом распределении памяти allocation function
должна возвращать блоки, удовлетворяющие alignment requirements любых типов,
для объектов которых хватит места в выделенном блоке.
ГА>
ГА> 1.4.5. Использование виртуальных методов, одинаковых по описанию и
ГА> порядковому номеру в виртуальной таблице допустимо без приведений.
Таких гарантий стандарт не дает.
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Сначала простой ответ: это решает задачу эволюции классов.
Эволюция в чистом виде в современных языках, если не ошибаюсь, присутствует только в Java.
Теперь к вопросу, зачем именно нужна сама эволюция..
Вы никогда не задавали себе вопрос, почему необходимо перезапускать, например, Apache, при изменении какого-либо модуля? например, при обновлении mod_perl? А существуют ведь программные комплексы, полная остановка которых тоже не всегда желательна (например, системы снятия статистики, АРМ и т.п.).
Конечно, динамическая линковка дает нам возможность подгружать и выгружать модули не останавливая программ. Но, если модуль хранит какие-либо данные, то его замена представляет из себя последовательность следующих действий:
— остановка старого модуля;
— сохранение данных;
— выгрузка;
— загрузка нового модуля;
— загрузка сохраненных данных;
— запуск нового модуля;
Если внимательно присмотреться, то это и есть "кустарный" случай конструктивной эволюции с сериализацией эволюционируемого объекта.
А задач, в которых может потребоваться "мягкая" замена одного класса другим, на самом деле очень много. Даже изменение состояния и режима работы объекта достаточно гибко реализуется с помощью эволюции.
Согласитесь, гораздо удобнее было бы на каждый режим (MODE_SUSPENDED, MODE_AUTHORIZING, MODE_TRANSMITTING, MODE_LISTENING и т.п.), например, того же модуля снятия статистики, создать разные экземпляры класса и эволюционировать между ними при смене режима, чем делать switch(mode) и т.п. в обработчике.
Конечно, в приведенном примере использование эволюции (а с ней и сериализации, полиморфизма и т.п.) немного скажется на производительности, но, в конце-концов, не отказываемся же мы от виртуальных методов, из-за того, что они не настолько быстро обрабатываются, как, например, статические
"..ты только посмотри на это деградирующее новое поколение, — даже "Момент", который они нюхают придумали до них!" (с) ZerG
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Здравствуйте, Алексей, Вы писали:
ПК>Еще есть требование выполнения alignment requirements, которые могут зависеть ПК>не только от sizeof класса, но и от определений его членов. Соответственно в ПК>общем случае одного ограничения по "объему" не достаточно.
1.4.1. Мутация классов в стеке возможна в том случае, если требования выравнивания и объем нового объекта соответствуют параметрам блока памяти, занимаемого старым объектом.
Так нормально?
ГА>>
ГА>> 1.4.5. Использование виртуальных методов, одинаковых по описанию и
ГА>> порядковому номеру в виртуальной таблице допустимо без приведений.
ПК>Таких гарантий стандарт не дает.
Тут сложнее. Это один из самых неустойчивых моментов.
Эксперименты на MSVC и BCB не опровергли это утверждение. Но в этих компиляторах положение vptr в объекте не изменялось, поэтому код для вызова виртуальных методов, одинаковых по синтаксису и положению в vtable, генерируется одинаковый. Как я понимаю, для компиляторов, записывающих vptr в конец объекта, требуется еще и соответствие его положения в мутирующих объектах.
В любом случае, в дальнейшем будет рассматриваться именно эволюция классов, т.е. замена наследников одного предка и в дальнейшем использование только вируальных методов предка..
Есть еще другое направление развития этой темы, — использование вместо placement new, прокси с заменяемым хранимымым объектом.. Но в таком случае взятие прямого указателя на хранимый объект потенциально опасно, но, практически не контролируемо.
"..ты только посмотри на это деградирующее новое поколение, — даже "Момент", который они нюхают придумали до них!" (с) ZerG
Здравствуйте, Гарбузенко Алексей, Вы писали:
ГА>- остановка старого модуля; ГА>- сохранение данных; ГА>- выгрузка; ГА>- загрузка нового модуля; ГА>- загрузка сохраненных данных; ГА>- запуск нового модуля;
Это аналогично
-сохранение данных
-выход из программы
-обновление программы
-запуск программы
-загрузка данных
Даже если перегружаешь что-то частями то все равно в этот момент система не работоспособна. Если в твоем апаче интенствно используется mod_perl то презагруска mod_perl равнозначна перезагрузке апача. Зачем тогда забивать себе голову и обременять программу навороченным, не нужным и не стабильным функционалом.
Здравствуйте, Kluev, Вы писали:
K>Здравствуйте, Гарбузенко Алексей, Вы писали:
ГА>>- остановка старого модуля; ГА>>- сохранение данных; ГА>>- выгрузка; ГА>>- загрузка нового модуля; ГА>>- загрузка сохраненных данных; ГА>>- запуск нового модуля;
пример приводился не к Апачу. он, к сожалению, не умеет делать выгрузку и загрузку модулей на ходу.
K>Это аналогично K>-сохранение данных K>-выход из программы K>-обновление программы K>-запуск программы K>-загрузка данных
K>Даже если перегружаешь что-то частями то все равно в этот момент система не работоспособна. Если в твоем апаче интенствно используется mod_perl то презагруска mod_perl равнозначна перезагрузке апача. Зачем тогда забивать себе голову и обременять программу навороченным, не нужным и не стабильным функционалом.
А вот суть системы эволюции именно в максимальном облегчении такой и схожих задач. Если у тебя на апаче работает 10-20 виртуальных хостов, и, например, рядом еще висит resin, то в некоторых случаях его достаточно долгая перезагрузка приведет к:
— ошибкам у пользователя;
— сбоям выполняющихся скриптов
— обрыву текущих download'ов (в т.ч. и больших страниц);
— потере данных в открывающихся post-формах;
— проблемам server-side веб-приложений;
— ...
В нормальных странах за такую функциональность сервера, фирма-предоставитель хостинга может влететь в немалую копеечку.
А для разрешения проблем "навороченности и нестабильности функционала" это все тут и обсуждается.
"..ты только посмотри на это деградирующее новое поколение, — даже "Момент", который они нюхают придумали до них!" (с) ZerG
Здравствуйте, Гарбузенко, Вы писали:
ГА> 1.4.1. Мутация классов в стеке возможна в том случае, если требования ГА> выравнивания и объем нового объекта соответствуют параметрам блока памяти, ГА> занимаемого старым объектом.
ГА> Так нормально?
Да.
ГА>>> 1.4.5. Использование виртуальных методов, одинаковых по описанию и ГА>>> порядковому номеру в виртуальной таблице допустимо без приведений.
ПК>> Таких гарантий стандарт не дает.
ГА> Тут сложнее. Это один из самых неустойчивых моментов. ГА> Эксперименты на MSVC и BCB не опровергли это утверждение. <...>
Скорее всего, работать будет, но стандарт никаких подобных гарантий
не дает.
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ГА>>>> 1.4.5. Использование виртуальных методов, одинаковых по описанию и ГА>>>> порядковому номеру в виртуальной таблице допустимо без приведений.
ПК>>> Таких гарантий стандарт не дает.
ГА>> Тут сложнее. Это один из самых неустойчивых моментов. ГА>> Эксперименты на MSVC и BCB не опровергли это утверждение. <...>
ПК>Скорее всего, работать будет, но стандарт никаких подобных гарантий ПК>не дает.
Если хочется гарантий, то можно поработать ручками. Делают же COM компоненты на C.