Эволюция классов в C++. Часть 1
От: Гарбузенко Алексей Украина http://bast.kiev.ua
Дата: 10.02.04 14:45
Оценка:
Этой задачачей я занимаюсь несколько месяцев и сейчас уже готова альфа-версия фреймворка. Поэтому и решил систематизировать имеющиеся данные и предпосылки, чтобы выложить на Ваше усмотрение. Уверен, достаточно много будет возражений и дополнений по этой теме, что несомненно позволит проработать систему и привести ее в вид, пригодный для использования.

сначала, немного теории.

Эволюция классов в 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
Re: Эволюция классов в C++. Часть 1
От: nap2k Верблюд есть
Дата: 10.02.04 15:26
Оценка: +1
Здравствуйте, Гарбузенко Алексей, Вы писали:

ГА>Этой задачачей я занимаюсь несколько месяцев и сейчас уже готова альфа-версия фреймворка. Поэтому и решил систематизировать имеющиеся данные и предпосылки, чтобы выложить на Ваше усмотрение. Уверен, достаточно много будет возражений и дополнений по этой теме, что несомненно позволит проработать систему и привести ее в вид, пригодный для использования.


И к чему весь этот бред
... << RSDN@Home 1.1.0 stable >>
Re: Эволюция классов в C++. Часть 1
От: MaximE Великобритания  
Дата: 10.02.04 18:01
Оценка: +1
Гарбузенко Алексей wrote:

[]

Вопрос. Какую задачу ЭТО решает?

--
Maxim Egorushkin
MetaCommunications Engineering
http://www.meta-comm.com/engineering/
Posted via RSDN NNTP Server 1.8 beta
Re: Эволюция классов в C++. Часть 1
От: Аноним  
Дата: 10.02.04 20:27
Оценка: 15 (1)
Вообще-то идея (а не реализация) сильно напоминает
паттерн Bridge (возможно, совмещённый с Proxy).

Что-то вроде

class Base {
public:
    virtual ~Base() {};
    virtual void DoSomething()=0;
};

class Proxy : public Base {
public:
    Proxy(Base * a_pimpl) : pimpl_(a_pimpl) {};
    virtual ~Proxy() { delete(pimpl_); };
    virtual void DoSomething() { pimpl_->DoSomething(); };
private:
    Proxy(); 
    Base * pimpl_; // реализацию можно менять в рантайме. Как именно - непринципиально. 
};
Re: Эволюция классов в C++. Часть 1
От: Denwer Россия  
Дата: 11.02.04 06:51
Оценка: +1
Здравствуйте, Гарбузенко Алексей, Вы писали:

ГА>Этой задачачей я занимаюсь несколько месяцев и сейчас уже готова альфа-версия фреймворка.

Советую посидеть над ней еще несколько месяцев.
При написании программ я придерживаюсь следующих правил: программа должа быть очень понимаема другими программистами(для ее поддержки например), максимально стабильна(т.е. максимум придерживаться стандарта и без грязных хаков), и конечно при возможности алгоритм дожен быть как можно короче, но не теряя функционала и понимания(это правило также связана косвенно с двумя предыдущими). Все это также ускоряет процесс отладки.
Вот теперь подумай что из твоих слов удовлетворяет этим требованиям.
ЗЫ: Ты конечно можешь сказать что это лично мои предрассудки, тогда да, можешь писать проги со страшным кодом и которые постоянно виснут.
Re: Эволюция классов в C++. Часть 1
От: Павел Кузнецов  
Дата: 11.02.04 08:13
Оценка:
Здравствуйте, Алексей, Вы писали:

ГА>

ГА> 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"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[2]: Эволюция классов в C++. Часть 1
От: Гарбузенко Алексей Украина http://bast.kiev.ua
Дата: 11.02.04 09:37
Оценка:
Здравствуйте, MaximE, Вы писали:


ME>Вопрос. Какую задачу ЭТО решает?


Сначала простой ответ: это решает задачу эволюции классов.
Эволюция в чистом виде в современных языках, если не ошибаюсь, присутствует только в Java.

Теперь к вопросу, зачем именно нужна сама эволюция..
Вы никогда не задавали себе вопрос, почему необходимо перезапускать, например, Apache, при изменении какого-либо модуля? например, при обновлении mod_perl? А существуют ведь программные комплексы, полная остановка которых тоже не всегда желательна (например, системы снятия статистики, АРМ и т.п.).
Конечно, динамическая линковка дает нам возможность подгружать и выгружать модули не останавливая программ. Но, если модуль хранит какие-либо данные, то его замена представляет из себя последовательность следующих действий:

Если внимательно присмотреться, то это и есть "кустарный" случай конструктивной эволюции с сериализацией эволюционируемого объекта.

А задач, в которых может потребоваться "мягкая" замена одного класса другим, на самом деле очень много. Даже изменение состояния и режима работы объекта достаточно гибко реализуется с помощью эволюции.
Согласитесь, гораздо удобнее было бы на каждый режим (MODE_SUSPENDED, MODE_AUTHORIZING, MODE_TRANSMITTING, MODE_LISTENING и т.п.), например, того же модуля снятия статистики, создать разные экземпляры класса и эволюционировать между ними при смене режима, чем делать switch(mode) и т.п. в обработчике.

Конечно, в приведенном примере использование эволюции (а с ней и сериализации, полиморфизма и т.п.) немного скажется на производительности, но, в конце-концов, не отказываемся же мы от виртуальных методов, из-за того, что они не настолько быстро обрабатываются, как, например, статические
"..ты только посмотри на это деградирующее новое поколение, — даже "Момент", который они нюхают придумали до них!" (с) ZerG
Re[2]: Эволюция классов в C++. Часть 1
От: Гарбузенко Алексей Украина http://bast.kiev.ua
Дата: 11.02.04 09:55
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Здравствуйте, Алексей, Вы писали:


ПК>Еще есть требование выполнения alignment requirements, которые могут зависеть

ПК>не только от sizeof класса, но и от определений его членов. Соответственно в
ПК>общем случае одного ограничения по "объему" не достаточно.

1.4.1. Мутация классов в стеке возможна в том случае, если требования выравнивания и объем нового объекта соответствуют параметрам блока памяти, занимаемого старым объектом.


Так нормально?

ГА>>

ГА>> 1.4.5. Использование виртуальных методов, одинаковых по описанию и
ГА>> порядковому номеру в виртуальной таблице допустимо без приведений.


ПК>Таких гарантий стандарт не дает.


Тут сложнее. Это один из самых неустойчивых моментов.
Эксперименты на MSVC и BCB не опровергли это утверждение. Но в этих компиляторах положение vptr в объекте не изменялось, поэтому код для вызова виртуальных методов, одинаковых по синтаксису и положению в vtable, генерируется одинаковый. Как я понимаю, для компиляторов, записывающих vptr в конец объекта, требуется еще и соответствие его положения в мутирующих объектах.
В любом случае, в дальнейшем будет рассматриваться именно эволюция классов, т.е. замена наследников одного предка и в дальнейшем использование только вируальных методов предка..

Есть еще другое направление развития этой темы, — использование вместо placement new, прокси с заменяемым хранимымым объектом.. Но в таком случае взятие прямого указателя на хранимый объект потенциально опасно, но, практически не контролируемо.
"..ты только посмотри на это деградирующее новое поколение, — даже "Момент", который они нюхают придумали до них!" (с) ZerG
Re[3]: Эволюция классов в C++. Часть 1
От: Kluev  
Дата: 11.02.04 09:58
Оценка:
Здравствуйте, Гарбузенко Алексей, Вы писали:

ГА>- остановка старого модуля;

ГА>- сохранение данных;
ГА>- выгрузка;
ГА>- загрузка нового модуля;
ГА>- загрузка сохраненных данных;
ГА>- запуск нового модуля;

Это аналогично
-сохранение данных
-выход из программы
-обновление программы
-запуск программы
-загрузка данных

Даже если перегружаешь что-то частями то все равно в этот момент система не работоспособна. Если в твоем апаче интенствно используется mod_perl то презагруска mod_perl равнозначна перезагрузке апача. Зачем тогда забивать себе голову и обременять программу навороченным, не нужным и не стабильным функционалом.
Re[4]: Эволюция классов в C++. Часть 1
От: Гарбузенко Алексей Украина http://bast.kiev.ua
Дата: 11.02.04 10:15
Оценка:
Здравствуйте, Kluev, Вы писали:

K>Здравствуйте, Гарбузенко Алексей, Вы писали:


ГА>>- остановка старого модуля;

ГА>>- сохранение данных;
ГА>>- выгрузка;
ГА>>- загрузка нового модуля;
ГА>>- загрузка сохраненных данных;
ГА>>- запуск нового модуля;

пример приводился не к Апачу. он, к сожалению, не умеет делать выгрузку и загрузку модулей на ходу.

K>Это аналогично

K>-сохранение данных
K>-выход из программы
K>-обновление программы
K>-запуск программы
K>-загрузка данных

K>Даже если перегружаешь что-то частями то все равно в этот момент система не работоспособна. Если в твоем апаче интенствно используется mod_perl то презагруска mod_perl равнозначна перезагрузке апача. Зачем тогда забивать себе голову и обременять программу навороченным, не нужным и не стабильным функционалом.


А вот суть системы эволюции именно в максимальном облегчении такой и схожих задач. Если у тебя на апаче работает 10-20 виртуальных хостов, и, например, рядом еще висит resin, то в некоторых случаях его достаточно долгая перезагрузка приведет к:

В нормальных странах за такую функциональность сервера, фирма-предоставитель хостинга может влететь в немалую копеечку.

А для разрешения проблем "навороченности и нестабильности функционала" это все тут и обсуждается.
"..ты только посмотри на это деградирующее новое поколение, — даже "Момент", который они нюхают придумали до них!" (с) ZerG
Re[3]: Эволюция классов в C++. Часть 1
От: Павел Кузнецов  
Дата: 11.02.04 14:48
Оценка:
Здравствуйте, Гарбузенко, Вы писали:

ГА> 1.4.1. Мутация классов в стеке возможна в том случае, если требования

ГА> выравнивания и объем нового объекта соответствуют параметрам блока памяти,
ГА> занимаемого старым объектом.

ГА> Так нормально?


Да.

ГА>>> 1.4.5. Использование виртуальных методов, одинаковых по описанию и

ГА>>> порядковому номеру в виртуальной таблице допустимо без приведений.

ПК>> Таких гарантий стандарт не дает.


ГА> Тут сложнее. Это один из самых неустойчивых моментов.

ГА> Эксперименты на MSVC и BCB не опровергли это утверждение. <...>

Скорее всего, работать будет, но стандарт никаких подобных гарантий
не дает.
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[4]: Эволюция классов в C++. Часть 1
От: Шахтер Интернет  
Дата: 12.02.04 03:41
Оценка: +1
Здравствуйте, Павел Кузнецов, Вы писали:

ГА>>>> 1.4.5. Использование виртуальных методов, одинаковых по описанию и

ГА>>>> порядковому номеру в виртуальной таблице допустимо без приведений.

ПК>>> Таких гарантий стандарт не дает.


ГА>> Тут сложнее. Это один из самых неустойчивых моментов.

ГА>> Эксперименты на MSVC и BCB не опровергли это утверждение. <...>

ПК>Скорее всего, работать будет, но стандарт никаких подобных гарантий

ПК>не дает.

Если хочется гарантий, то можно поработать ручками. Делают же COM компоненты на C.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.