Re[13]: [ANN] ObjESSty - еще один проект сериализации
От: adontz Грузия http://adontz.wordpress.com/
Дата: 25.01.05 18:14
Оценка:
Здравствуйте, eao197, Вы писали:

E>Не реальная. Но вечный двигатель до сих пор изобретают.


В рамках языка да, а в рамках языка+компилятора не знаю.

A>>дело не в том собственное оно или нет. Если вместе с проектом будут идти vbs и pl файл то всё ок. А так качать много чего надо.

E>pl -- это Perl? А если у меня на машине Perl-а нет? Вот принципиально не люблю Perl-а и не ставлю себе, пока сильно не прижмет.

VBScript для Windows и Perl для Unix. Делать они должны одно и то же.

A>>Я бы сказал шире — плохая расширяемость. Насколько легко подключить новое хранилище или формат хранения данных?

E>А насколько просто подключить к SQLite или MetaKit, или BerkeleyDB новое хранилище или формат хранения данных?

А к MySQL? Вот кстати хороший пример. Как БД ничего особенного (даже хранимок нет), зато очень удобное расширение SQL и несколько хранилищь на выбор с разными возможностями.
A journey of a thousand miles must begin with a single step © Lau Tsu
[ANN] ObjESSty - там еще простенькая объектная БД есть :)
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 26.01.05 10:27
Оценка: 36 (1)
Добрый день.

Спасибо adontz
Автор: adontz
Дата: 25.01.05
и alsemm
Автор: alsemm
Дата: 24.01.05
за высказанные ими замечения в соседней ветви. Резюмировать их можно следующим образом:
— плохо, что для сериализации требуется делать сериализуемый тип производным от специального базового типа;
— плохо, что исходя из вышеозначенного пункта органичивается количество типов, объекты которых могут быть сериализованны;
— плохо, что нет поддержки сериализации в XML и других форматов, отличных от собственного формата ObjESSty.

Замечания ожидаемые. Но, надеюсь, что из рассмотрения было упущено то, что ObjESSty это не только и не столько инструемент для сериализации данных. Начинался проект ObjESSty как проект для создания простой объектной БД и сериализация явилась в ObjESSty, можно сказать, побочным продуктом. Если ее рассматривать в отрыве от БД ObjESSty, то указанные выше замечания кажутся мне вескими. Но дело в том, что основная часть ObjESSty -- это объектная БД.

Вот сделал описание этой БД. Мне было бы интересно мнение участников форума и об этой стороне ObjESSty. Сразу хочу сказать, что сейчас oess_1::db далека от того, что даже я хотел бы в ней видеть. В планах есть внедрение в oess_1::db:
— поддержки индексов (точнее: поддержки возможности индексации содержимого слайса несколькими разными ключами);
— поддержки именованных (корневых) объектов (это понятие из области объектных БД, там можно было дать некоторым объктам уникальные имена и использовать их в качестве "точек входа" в БД);
— предоставления возможности работы с БД нескольким процессам (для чего активно собираюсь использовать возможности ACE).

Буду рад также любым конструктивным предложениям и идеям.


Еще одним из недостатков было указано использование "нетрадиционной" системы компиляции проекта, для использования которой нужно устанавливать язык Ruby. Так же ожидаемое замечание. Но пока я не нашел инструмента, который бы позволял мне поддерживать сразу несколько платформ с помощью одного и того же проектного файла. И при этом бы без проблем поддерживал используемую мной систему каталогов в проекте. Так же буду рад любым конструктивным предложениям по этому поводу.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Нужен совет
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 07.03.05 12:06
Оценка:
Добрый день.

Нужен совет по возможным способам реализации индексов. В БД обычно применяют индексы на основе B+ деревьев. Но, как я понимаю, в B+ дереве должны храниться только уникальные ключи. В ObjESSty зачастую хранятся объекты, у которых ключи не уникальны. Например, если в качестве ключа используется время создания объекта, в виде time_t (в одну секунду может быть создано несколько объектов). Сейчас при работе с такими объектами в памяти строится std::multimap по времени создания. Затем с помощью lower_bound выбираются все объекты, время создания которых больше нужного значения.

Есть необходимость поддержки таких multimap прямо на уровне ObjESSty, чтобы не приходилось вручную строить std::multimap при открытии БД. Если бы можно было использовать обычный map, то я бы просто взял B+ дерево. Но вот по поводу multimap у меня заминка.

Может я не прав, по поводу B+ дерева?

Может существуют еще какие-то механизмы организации индексов во внешней памяти (кроме B+ деревьев)?

Буду признателен за любые предложения и ссылки на соответствующие материалы.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: [ANN] ObjESSty - еще один проект сериализации
От: hth  
Дата: 07.03.05 17:24
Оценка:
Здравствуйте, eao197, Вы писали:

E>Добрый день!


E>В течении последних двух лет я занимался собственным проектом для сериализации и долговременного хранения сложных C++ объектов: Object Entity Serialization & Stability (ObjESSty). Наконец сейчас он достиг уровня, при котором его не стыдно показать общественности. Что я и делаю при помощи этого сообщения.


E>Страничка проекта: http://eao197.narod.ru/objessty

E>Страничка, как и последняя версия проекта находится в состоянии работоспособной beta-версии. Могут наблюдаться различные кривости/ошибки/описки/опечатки, но при наличии интереса и предложений все может быть доведено до ума.

E>Буду признателен за любые конструктивные предложения и замечания, в том числе и за разгромную (но объективную) критику.


E>С наилучшими пожеланиями

E>Евгений Охотников.

хотелось бы узнать у автора проeкта —
"как на счет" scheema evolution?

И посоветовать, "в качестве обмена опытом", посмотреть на
то, как сериализация делается автоматически на основе
C++ reflection information discussed today at
http://rsdn.ru/Forum/Message.aspx?mid=1059505
Автор: yxiie
Дата: 07.03.05

в ROOTe http://root.cern.ch/root/HowtoWrite.html
Re[2]: [ANN] ObjESSty - еще один проект сериализации
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 07.03.05 18:43
Оценка:
Здравствуйте, hth, Вы писали:

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


E>>Добрый день!


hth>хотелось бы узнать у автора проeкта —

hth>"как на счет" scheema evolution?

Спасибо за интересный вопрос.

Прежде всего, давайте я опишу, как я понимаю термин scheme evolution. Под эволюцией схемы данных я понимаю такое изменение схемы данных, которое приводит к изменению сериализованного образа объекта. Под такие изменения попадают:
— изменение иерархии наследования: добавление базового типа, изъятие базового типа, изменение типа наследования (например, обычный базовый тип становится виртуальным базовым типом или наоборот, виртуальный базовый тип становится обычным базовым типом);
— изменение типов данных атрибутов;
— изменение списка атрибутов: добавление атрибутов или удаление атрибутов.

Сразу оговорюсь, что scheme evolution в чистом виде в ObjESSty сейчас нет. В моей предыдущей попытке создать ООСУБД такая поддержка была и я хотел бы со временем добавить ее и в ObjESSty, но здесь сказывается недостаток времени.

Так же, по опыту предыдущей работы, обработку изменения схемы данных очень удобно делать на уровне списка атрибутов: для конкретного типа строится полный список атрибутов, как собственных, так и унаследованных. Таких списков получается два -- один для старого варианта схемы данных, второй -- для нового варианта. Пересечение таких списков как раз и показывает изменение схемы данных.

В ObjESSty отсутствие полноценной поддержки схемы данных не сказывалось критическим образом по следующим причинам:

1. Сериализация с помощью ObjESSty используется для организации обмена сообщения между процессами. Поскольку каждый процесс является самостоятельным проектом, который самостоятельно развивается, то спецификация (схема данных) сообщений не может быть просто так изменена. Т.е. схема данных определяет интерфейс. Который, будучи однажды зафиксированным, уже не может быть изменен. Такая ситуация, насколько я знаю, существует с интерфейсами и в CORBA, и в COM. Поэтому в ObjESSty особо и не требовалось изменять структуру существующих сообщений так, чтобы на другой стороне новая структура была адекватно понята.

2. ObjESSty поддерживает два механизма для расширения существующей схемы данных. Т.е. уже описанные атрибуты и базовые типы не изменяются, но можно добавить новые атрибуты, которые будут проигнорированы на стороне, которая использует старую схему данных. Один механизм -- это механизм "расширяющихся типов", т.е. список их атрибутов может быть просто расширен. Второй механизм -- т.н. subclassing by extension, позволяет передавать в сообщениях объекты полиморфных типов, про которые принимающая сторона просто ничего не знает. В двух словах эти механизмы не опишешь, а подробнее можно прочитать здесь, здесь и здесь.

Так же я думаю, что если говорить о сериализации данных, то здесь изменение схемы данных логичнее поддерживать так, как это делается в ASN1: через extension points. В ObjESSty такую роль играют расширяемые типы. Если же говорить об изменении схемы данных БД, то здесь нужно поддерживать и модификацию списка атрибутов, и изменения типов атрибутов. А так же либо миграцию существующих данных на новую схему, либо преобразование данных на лету.

hth>И посоветовать, "в качестве обмена опытом", посмотреть на

hth>то, как сериализация делается автоматически на основе
hth>C++ reflection information discussed today at
hth>http://rsdn.ru/Forum/Message.aspx?mid=1059505
Автор: yxiie
Дата: 07.03.05


Я так же принимаю участие в этой ветке

hth>в ROOTe http://root.cern.ch/root/HowtoWrite.html


Здесь я не нашел ничего принципиально нового для себя. Все же я сторонник того, чтобы сериализующий/десериализующий код генерировался автоматически. И автоматически отслеживались изменения схемы данных. В очень большом числе задач это возможно.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[3]: [ANN] ObjESSty - еще один проект сериализации
От: hth  
Дата: 07.03.05 19:05
Оценка:
Здравствуйте, eao197, Вы писали:

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


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


E>>>Добрый день!


hth>>хотелось бы узнать у автора проeкта —

hth>>"как на счет" scheema evolution?

E>Спасибо за интересный вопрос.


E>Прежде всего, давайте я опишу, как я понимаю термин scheme evolution. Под эволюцией схемы данных я понимаю такое изменение схемы данных, которое приводит к изменению сериализованного образа объекта. Под такие изменения попадают:

E>- изменение иерархии наследования: добавление базового типа, изъятие базового типа, изменение типа наследования (например, обычный базовый тип становится виртуальным базовым типом или наоборот, виртуальный базовый тип становится обычным базовым типом);
E>- изменение типов данных атрибутов;
E>- изменение списка атрибутов: добавление атрибутов или удаление атрибутов.

E>Сразу оговорюсь, что scheme evolution в чистом виде в ObjESSty сейчас нет. В моей предыдущей попытке создать ООСУБД такая поддержка была и я хотел бы со временем добавить ее и в ObjESSty, но здесь сказывается недостаток времени.


E>Так же, по опыту предыдущей работы, обработку изменения схемы данных очень удобно делать на уровне списка атрибутов: для конкретного типа строится полный список атрибутов, как собственных, так и унаследованных. Таких списков получается два -- один для старого варианта схемы данных, второй -- для нового варианта. Пересечение таких списков как раз и показывает изменение схемы данных.


E>В ObjESSty отсутствие полноценной поддержки схемы данных не сказывалось критическим образом по следующим причинам:


E>1. Сериализация с помощью ObjESSty используется для организации обмена сообщения между процессами. Поскольку каждый процесс является самостоятельным проектом, который самостоятельно развивается, то спецификация (схема данных) сообщений не может быть просто так изменена. Т.е. схема данных определяет интерфейс. Который, будучи однажды зафиксированным, уже не может быть изменен. Такая ситуация, насколько я знаю, существует с интерфейсами и в CORBA, и в COM. Поэтому в ObjESSty особо и не требовалось изменять структуру существующих сообщений так, чтобы на другой стороне новая структура была адекватно понята.


E>2. ObjESSty поддерживает два механизма для расширения существующей схемы данных. Т.е. уже описанные атрибуты и базовые типы не изменяются, но можно добавить новые атрибуты, которые будут проигнорированы на стороне, которая использует старую схему данных. Один механизм -- это механизм "расширяющихся типов", т.е. список их атрибутов может быть просто расширен. Второй механизм -- т.н. subclassing by extension, позволяет передавать в сообщениях объекты полиморфных типов, про которые принимающая сторона просто ничего не знает. В двух словах эти механизмы не опишешь, а подробнее можно прочитать здесь, здесь и здесь.


E>Так же я думаю, что если говорить о сериализации данных, то здесь изменение схемы данных логичнее поддерживать так, как это делается в ASN1: через extension points. В ObjESSty такую роль играют расширяемые типы. Если же говорить об изменении схемы данных БД, то здесь нужно поддерживать и модификацию списка атрибутов, и изменения типов атрибутов. А так же либо миграцию существующих данных на новую схему, либо преобразование данных на лету.


hth>>И посоветовать, "в качестве обмена опытом", посмотреть на

hth>>то, как сериализация делается автоматически на основе
hth>>C++ reflection information discussed today at
hth>>http://rsdn.ru/Forum/Message.aspx?mid=1059505
Автор: yxiie
Дата: 07.03.05


E>Я так же принимаю участие в этой ветке


hth>>в ROOTe http://root.cern.ch/root/HowtoWrite.html


E>Здесь я не нашел ничего принципиально нового для себя. Все же я сторонник того, чтобы сериализующий/десериализующий код генерировался автоматически. И автоматически отслеживались изменения схемы данных. В очень большом числе задач это возможно.


я хотел бы отметить, что в ROOTe serialisation делается автоматически,
т.е. Streamer method генерится на основе C++ metadata information.
C++ metadata information также спасается в файл вместе с обьектом.
Это позволяет реализовать поддержку scheema evolution —
даже если схема класса изменилась, все равно возможно прочитать
файлы обьектами имеющими "старую" схему.
Re[4]: [ANN] ObjESSty - еще один проект сериализации
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 07.03.05 21:13
Оценка:
Здравствуйте, hth, Вы писали:

E>>Здесь я не нашел ничего принципиально нового для себя. Все же я сторонник того, чтобы сериализующий/десериализующий код генерировался автоматически. И автоматически отслеживались изменения схемы данных. В очень большом числе задач это возможно.


hth>я хотел бы отметить, что в ROOTe serialisation делается автоматически,

hth>т.е. Streamer method генерится на основе C++ metadata information.
hth>C++ metadata information также спасается в файл вместе с обьектом.
hth>Это позволяет реализовать поддержку scheema evolution -
hth>даже если схема класса изменилась, все равно возможно прочитать
hth>файлы обьектами имеющими "старую" схему.

А ROOT работает еще на чем-нибудь, кроме CInt?

А ROOT позволяет сериализовать только часть атрибутов класса?
А сериализовать только те атрибуты, значение которых отличается от значения по-умолчанию?
... << RSDN@Home 1.1.4 beta 3 rev. 185>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: Нужен совет
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 08.03.05 07:46
Оценка:
Здравствуйте, eao197, Вы писали:

E>Добрый день.


E>Нужен совет по возможным способам реализации индексов. В БД обычно применяют индексы на основе B+ деревьев. Но, как я понимаю, в B+ дереве должны храниться только уникальные ключи. В ObjESSty зачастую хранятся объекты, у которых ключи не уникальны. Например, если в качестве ключа используется время создания объекта, в виде time_t (в одну секунду может быть создано несколько объектов). Сейчас при работе с такими объектами в памяти строится std::multimap по времени создания. Затем с помощью lower_bound выбираются все объекты, время создания которых больше нужного значения.


E>Есть необходимость поддержки таких multimap прямо на уровне ObjESSty, чтобы не приходилось вручную строить std::multimap при открытии БД. Если бы можно было использовать обычный map, то я бы просто взял B+ дерево. Но вот по поводу multimap у меня заминка.


Пока удалось придумать два решения:



1. Использовать обычное B+ дерево, но в листовых страницах хранить элементы такого псевдотипа:

struct    leaf_node_item_t
    {
        // Значение ключа.
        key_t    m_key;
        // Ссылка на элемент с таким значением ключа.
        // Эта ссылка является пустой, если таких элементов несколько.
        item_t *    m_item;
        
        // Общее количество элементов с таким значением ключа.
        unsigned int    m_items_count;
        // Первая страница списка указателей на элементы с одинаковым значением ключа.
        ref_list_page_t< item_t * > *    m_first_list_page;
        // Последняя страница списка указателей на элементы с одинаковым значением ключа.
        ref_list_page_t< item_t * > * m_last_list_page;
    };


Т.е. идея в том, что когда элемент с указанным значением ключа один, то на него ссылается leaf_node_item_t::m_item. Если же элементов с таким значением ключа несколько, то ссылки на них находятся в односвязном списке страниц ref_list_page_t:

template< class Reference >
struct    ref_list_page_t
    {
        Reference    m_refs[ max_ref_list_page_size ];
        ref_list_page_t< Reference > *    m_next;
    };


В таком варианте для извлечения всех элементов с одинаковым значением ключа нужно просто найти этот ключ в B+ дереве, а затем пройти по списку страниц с ссылками.



2. Использовать обычное B+ дерево, но на уровне индекса подменять тип ключа. Например, на вот такой тип:

template< class Key >
struct    index_key_t
    {
        // Оригинальный ключ.
        Key    m_key;
        
        // А вот в этом поле весь фокус и состоит.
        // Это автоинкрементное поле, которое автоматически выставляется
        // самим индексом при обработке нового элемента.
        unsigned int    m_uid;
        
        // Нужен специальный оператор сравнения.
        // Приоритет при сравнении отдается оригинальному ключу, но если
        // ключи совпадают, то сравниваются m_uid-ы.
        bool
        operator<( const index_key_t< Key > & o ) const
            {
                if( m_key < o.m_key )
                    return true;
                else if( o.m_key < m_key )
                    return false;
                else
                    return ( m_uid < o.m_uid );
            }
    };


Тогда, операцию lower_bound(k) можно выразить через lower_bound(index_key_t(k, 0)). А операцию upper_bound(k) -- через upper_bound(index_key_t(k, MAX_UINT)).

А все элементы, которые имеют одинаковый ключ берутся из диапазона [lower_bound(index_key_t(k,0)), upper_bound(index_key_t(k,MAX_UINT))).




Мне больше нравится второе решение.

Может я где-то ошибся?
... << RSDN@Home 1.1.4 beta 3 rev. 185>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[5]: [ANN] ObjESSty - еще один проект сериализации
От: hth  
Дата: 08.03.05 10:33
Оценка: 7 (1)
Здравствуйте, eao197, Вы писали:

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


E>>>Здесь я не нашел ничего принципиально нового для себя. Все же я сторонник того, чтобы сериализующий/десериализующий код генерировался автоматически. И автоматически отслеживались изменения схемы данных. В очень большом числе задач это возможно.


hth>>я хотел бы отметить, что в ROOTe serialisation делается автоматически,

hth>>т.е. Streamer method генерится на основе C++ metadata information.
hth>>C++ metadata information также спасается в файл вместе с обьектом.
hth>>Это позволяет реализовать поддержку scheema evolution -
hth>>даже если схема класса изменилась, все равно возможно прочитать
hth>>файлы обьектами имеющими "старую" схему.

E>А ROOT работает еще на чем-нибудь, кроме CInt?


CINT — это и есть сам корень ROOTa

E>А ROOT позволяет сериализовать только часть атрибутов класса?


позволяет сериализовать все атрибуты,
при этом если атрибут, является pointer сериализуется и обьект, на который
указывает pointer. Это позволяет иметь directory structure inside file
Пример тут http://carrot.cern.ch/CarrotExamples/hsimple.root
Конечно есть опции (специальные комментарии "напротив" атрибута)
позволяюшие не писать этот атрибут в файл

E>А сериализовать только те атрибуты, значение которых отличается от значения по-умолчанию?


сериализуются значения атрибутов на момент записи
Реализация паттерна Async Completion Token
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 20.05.05 14:40
Оценка: 20 (3)
Вот здесь профессор Дуглас Шмидт описывает паттерн Asynchronous Completion Token. Суть патерна в том, что клиент, выполняя запрос к серверу, передает на сервер специальный объект (Asynchronous Completion Token -- ACT). Сервер возвращает этот объект клиенту в ответе. Благодоря ACT клиент понимает, на какой именно из его запросов ответил сервер. Особенно это удобно при асинхронном взаимодействии клиента и сервера, когда по одному каналу связи идут потоки запросов и ответов. Ключевым моментом в паттерне Asynchronous Completion Token является то, что объект ACT, в общем случае, является непрозрачным для сервера (т.е. сервер совершенно не знает, что именно находится в ACT).

Забавно, что я наткнулся на описание этого паттерна вскоре после того, как реализовал с помощью ObjESSty подобный механизм. В моем случае требовалось организовать цепочку процессов (которые, возможно, работают на разных узлах сети) и которые в асинхронном режиме обслуживают запросы (транзакции) клиента, подключенного к одному из концов этой цепочки (эта цепочка выглядит для клиента как один сервер). Причем каждая транзакция состоит из нескольких сообщений. Например, одна из транзакций состоит из трех сообщений:
1. send -- инициируется клиентом и содержит описание необходимых действий. Клиент повторяет его до тех пор, пока не получит send_result.
2. send_result -- отсылается клиенту в ответ на send. Повторяется до тех пор, пока клиент не пришлет send_finish. Сделано это для того, чтобы в случае потери единичного send_result клиент не инициировал запрос send еще раз.
3. send_finish -- отсылается клиентом в ответ на send_result. Это сообщение говорит серверу, что клиент полностью завершил транзакцию и что сервер может удалить у себя информацию о данной транзакции.
Такая трехфазная схема позволяет организовать асинхронное взаимодействие между клиентом и сервером через один канал. Клиент может инициировать несколько send не дожидаясь ответа на предудущие send. Однако, эта схема требует, чтобы каждая транзакция имела уникальный идентификатор.

Вопрос был в том, как представить идентификаторы транзакций. Особенность еще и в том, что к серверу могли подключаться несколько клиентов, каждый из которых использует собственные идентификаторы. А внутри сервера сообщения от разных клиентов могли смешиваться. В типовом случае сервер состоял из трех процессов:
— первый являлся шлюзом между клиентами и остальной частью сервера (gate). Этих процессов может быть несколько, в зависимости от того, какой транспорт предпочитает конкретный клиент;
— второй процесс (router) занимался выбором конкретного процесса для выполнения запроса клиента. В простейшем случае сервер содержал только один router, к которому подключаются все gate-ы;
— третий процесс (service) занимался выполнением запросов клиентов. Этих процессов может быть несколько.
Таким образом, в один процесс router стекаются запросы от всех клиентов. И router должен получать вместе с каждым запросом уникальный идентификатор запроса. И последующие в цепочке запросы так же должны получать уникальные идентификаторы.

Если бы в качестве идентификаторов транзакций использовались какие-либо простые значения, например уникальные целые числа (UID), то каждый процесс в цепочке, который мог обрабатывать запросы нескольких клиентов, был бы вынужден сохранять полученный от клиента UID, назначать транзакции собственный UID и передавать дальше именно собственный UID. Получив ответ процессу небходимо было по своему UID восстановить исходный UID и ретранслировать ответ клиенту уже с исходным UID. А это означало бы, что все процессы в цепочке должны были бы быть state-ful. Но для некоторых процессов это было слишком накладно. Для того же router-а например.

Поэтому здесь хорошо подходит идея ACT в качестве идентификатора транзакции. Каждый процесс в цепочке получает от предыдущего узла ACT, строит на его основе собственный ACT, сохраняя прямо в нем исходное значение, и передает запрос дальше со своим ACT. Получив ответ, процесс из своего ACT извлекает исходное значение и уже с ним ретранслирует ответ.

Вопрос здесь лишь в том, как удобно сделать работу с непрозрачными ACT. В случае с ObjESSty он разрешился посредством использования механизма subclassing by extension. Для идентификатора транзакции был заведен базовый класс:

namespace mbsms_2
{

class    MBSMS_2_TYPE    trx_id_t
    :    public oess_1::stdsn::serializable_t
    {
        OESS_SERIALIZER_EX( trx_id_t, MBSMS_2_TYPE )
    public :
        virtual ~trx_id_t();

        /*!
            \brief Клонировать объект.
        */
        virtual std::auto_ptr< trx_id_t >
        clone() const = 0;
    };

}


Который описывался на DDL следующим образом:
{type    mbsms_2::trx_id_t
    {abstract}
    {extensible}
    {subclassing_by_extension}
}


Реальные идентификаторы транзакций должны были создаваться путем наследования от trx_id_t. Например, вот так:
// В качестве реального идентификатора используется уникальное целое число.
class trx_id_t
    :    public mbsms_2::trx_id_t
    {
    OESS_SERIALIZER( trx_id_t )
    private :
        unsigned int    m_uid;

    public :
        trx_id_t()
            :    m_uid( 0 )
            {}
        trx_id_t(
            unsigned int uid )
            :    m_uid( uid )
            {}

        virtual std::auto_ptr< mbsms_2::trx_id_t >
        clone() const
            {
                return std::auto_ptr< mbsms_2::trx_id_t >(
                        new trx_id_t( *this ) );
            }

        unsigned int
        uid() const
            {
                return m_uid;
            }
    };

{type    trx_id_t
    {extensible}
    {subclassing_by_extension {extension_of mbsms_2::trx_id_t}}

    {attr m_uid {of oess_1::uint_t}}
}


Для того, чтобы передавать производные от mbsms_2::trx_id_t идентификаторы транзакций в сообщениях send, send_result, send_finish нужно использовать указатели на mbsms_2::trx_id_t и динамически созданные объекты конкретных типов идентификаторов. Для упрощения работы с арибутами-указателями был создан вспомогательный сериализуемый умный указатель (на самом деле он не такой уже и умный -- для копирования объектов используется их клонирование):
class    MBSMS_2_TYPE    trx_id_wrapper_t
    :    public oess_1::stdsn::shptr_skeleton_t<
                trx_id_t,
                oess_1::stdsn::cloneable_policy_t< trx_id_t > >
    {
        //! Псевдоним для базового типа.
        typedef oess_1::stdsn::shptr_skeleton_t<
                trx_id_t,
                oess_1::stdsn::cloneable_policy_t< trx_id_t > >
            base_type_t;
        OESS_SERIALIZER_EX( trx_id_wrapper_t, MBSMS_2_TYPE )
        OESS_1_SHPTR_IFACE( trx_id_wrapper_t,
                mbsms_2::trx_id_t,
                base_type_t )
    };

{type    mbsms_2::trx_id_wrapper_t
    {attr    m_ptr {of {extension_of} mbsms_2::trx_id_t}}
}


Далее в сообщениях хранятся именно mbsms_2::trx_id_wrapper_t:
class    MBSMS_2_TYPE send_t :
    public mbapi_3::msg_t
{
    OESS_SERIALIZER_EX( send_t, MBSMS_2_TYPE )
    public :
        //! Конструктор по умолчанию.
        /*!
            Присваивает всем полям пустые значения.
        */
        send_t();
        //! Инициализирующий конструктор.
        send_t(
            //! Идентификатор данной транзакции на стороне отправителя
            //! сообщения send_t.
            const mbsms_2::trx_id_wrapper_t & originator_trx,
            ....... );
        /*!
            \since 2.0.0

            Конструктор для случая перемаршрутизации сообщения.
            Явно задается новый originator_trx, остальные атрибуты берутся
            из исходного объекта.
        */
        send_t(
            //! Идентификатор данной транзакции на стороне отправителя сообщения.
            const mbsms_2::trx_id_wrapper_t & originator_trx,
            //! Исходный объект.
            const send_t & original );
        ...
    private :
        //! Идентификатор данной транзакции на стороне отправителя
        //! сообщения send_t.
        /*! \since v.2.0.0 */
        mbsms_2::trx_id_wrapper_t m_originator_trx;
        ...
};

{type    mbsms_2::send_t
    {extensible}
    {super mbapi_3::msg_t}

    {attr    m_originator_trx    {of    mbsms_2::trx_id_wrapper_t}}
    ...
}


Процесс router использует для идентификации проходящих через него транзакций собственный тип идентификатора:
class    trx_id_t
    :    public mbsms_2::trx_id_t
    { 
        OESS_SERIALIZER( trx_id_t )
    private :
        //! Идентификатор клиента, от которого был получен исходный
        //! идентификатор транзакции.
        /*!
            Может быть пустым, если исходного идентификатора транзакции
            не было.
        */
        mbapi_3::client_dest_t    m_client;

        //! Исходный идентификатор транзакции, который был получен от
        //! клиента.
        /*!
            Может быть нулевым, если исходного идентификатора транзакции
            не было.
        */
        mbsms_2::trx_id_wrapper_t    m_original;

    public :
        /*!
            Устанавливает все значения пустыми значениями.
        */
        trx_id_t();
        /*!
            Полностью инициализирующий конструктор.
        */
        trx_id_t(
            const mbapi_3::client_dest_t & client,
            const mbsms_2::trx_id_wrapper_t & original );

        //! Клонирование объекта.
        std::auto_ptr< mbsms_2::trx_id_t >
        clone() const;

        //! Является ли объект пустым.
        /*!
            \return true, если m_client является пустым.
        */
        bool
        is_empty() const;

        //! Получить доступ к идентификатору клиента.
        const mbapi_3::client_dest_t &
        client() const;

        //! Получить доступ к идентификатор транзакции клиента.
        const mbsms_2::trx_id_wrapper_t &
        original() const;
    };

{type    aag_3::smsc_map::trx_id_t
    {extensible}
    {subclassing_by_extension {extension_of mbsms_2::trx_id_t}}

    {attr m_client {of mbapi_3::client_dest_t}}
    {attr m_original {of mbsms_2::trx_id_wrapper_t}}
}


И вот как router маршрутизирует сообщение send_t на нужный процесс-service:
void
route_send_to_smsc(
    const so_4::rt::agent_t & router_agent,
    const mbsms_2::send_t & original,
    const mbapi_3::client_dest_t & cprov_id,
    const mbapi_3::client_dest_t & smsc_id,
    //! Будет пустой строкой, если номер отправителя SMS заменять не нужно.
    const std::string & actual_sms_sender,
    const mbapi_3::server_dest_t & router_addr )
    {
        // Сообщение должно уйти sms-центру с нашим идентификатором транзакции.
        mbsms_2::send_t outgoing(
                mbsms_2::trx_id_wrapper_t(
                        new trx_id_t(
                                cprov_id,
                                original.query_originator_trx() ) ),
                original );

        if( actual_sms_sender.size() )
            // В процессе маршрутизации сообщению был назначен
            // другой номер отправителя.
            outgoing.set_sms_sender(
                    mbsms_2::phone_dest_t(
                            mbsms_2::unknown_smsc_id,
                            actual_sms_sender ) );

        so_log_1::logic[ router_agent ][ so_log_1::low ]
                [ send_routing_log_tag ]
                [ so_log_1::d() << "cprov: " << cprov_id
                        << ", sms_receiver: " << original.query_sms_receiver()
                        << ", smsc_id: " << smsc_id ]();

        mbapi_3::router::route( smsc_id, outgoing, router_addr );
    }


А вот так router маршрутизирует ответное сообщение от service к нужному клиенту:
void
route_send_result(
    const so_4::rt::agent_t & router_agent,
    const mbsms_2::send_result_t & original,
    const mbapi_3::client_dest_t & smsc_id,
    const mbapi_3::server_dest_t & router_addr )
    {
        // Извлекаем исходный trx_id.
        const trx_id_t * trx = original.query_originator_trx().cast_to(
                oess_1::stdsn::shptr_type_tag< trx_id_t >() );
        if( !trx )
            throw std::domain_error(
                    "pointer to originator trx_id is 0!" );

        mbsms_2::send_result_t outgoing(
                trx->original(),
                mbsms_2::trx_id_wrapper_t(
                        new trx_id_t( smsc_id,
                                original.query_recipient_trx() ) ),
                router_addr,
                original );

        so_log_1::logic[ router_agent ][ so_log_1::lowest ]
                [ send_result_routing_log_tag ]
                [ so_log_1::d() << "smsc_id: " << smsc_id
                        << ", cprov: " << trx->client()
                        << ", result: "
                        << original.query_result() ]();

        mbapi_3::router::route( trx->client(), outgoing, router_addr );
    }


При такой схеме идентификаторы транзакций (ACT) получаются непрозрачными, но с ними удобно работать, т.к. они являются производными от одного базового типа.

Конечно же, такую схему можно было бы реализовать и без ObjESSty. Например, можно было использовать в качестве идентификаторов транзакций строки. И затем процесс router формировал свою строку, в которую включал бы идентификатор клиента и исходный ACT. А затем парсил бы ее. Так, собственно, все и происходит на самом нижнем уровне в ObjESSty, просто это скрыто от программиста.




Подобный механизм "непрозрачных" ACT можно реализовать с использованием любой системы сериализации, это не только ObjESSty. И даже без системы сериализации, вручную. Просто здесь я показал, как это получилось сделать посредством ObjESSty.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: Реализация паттерна Async Completion Token
От: Аноним  
Дата: 20.05.05 16:21
Оценка:
Здравствуйте, eao197, Вы писали:

E>Вот здесь профессор Дуглас Шмидт описывает паттерн Asynchronous Completion Token. Суть патерна в том, что клиент, выполняя запрос к серверу, передает на сервер специальный объект (Asynchronous Completion Token -- ACT). Сервер возвращает этот объект клиенту в ответе. Благодоря ACT клиент понимает, на какой именно из его запросов ответил сервер. Особенно это удобно при асинхронном взаимодействии клиента и сервера, когда по одному каналу связи идут потоки запросов и ответов. Ключевым моментом в паттерне Asynchronous Completion Token является то, что объект ACT, в общем случае, является непрозрачным для сервера (т.е. сервер совершенно не знает, что именно находится в ACT).


E>Забавно, что я наткнулся на описание этого паттерна вскоре после того, как реализовал с помощью ObjESSty подобный механизм. В моем случае требовалось организовать цепочку процессов (которые, возможно, работают на разных узлах сети) и которые в асинхронном режиме обслуживают запросы (транзакции) клиента, подключенного к одному из концов этой цепочки (эта цепочка выглядит для клиента как один сервер). Причем каждая транзакция состоит из нескольких сообщений. Например, одна из транзакций состоит из трех сообщений:

E>1. send -- инициируется клиентом и содержит описание необходимых действий. Клиент повторяет его до тех пор, пока не получит send_result.
E>2. send_result -- отсылается клиенту в ответ на send. Повторяется до тех пор, пока клиент не пришлет send_finish. Сделано это для того, чтобы в случае потери единичного send_result клиент не инициировал запрос send еще раз.
E>3. send_finish -- отсылается клиентом в ответ на send_result. Это сообщение говорит серверу, что клиент полностью завершил транзакцию и что сервер может удалить у себя информацию о данной транзакции.
E>Такая трехфазная схема позволяет организовать асинхронное взаимодействие между клиентом и сервером через один канал. Клиент может инициировать несколько send не дожидаясь ответа на предудущие send. Однако, эта схема требует, чтобы каждая транзакция имела уникальный идентификатор.

По-моему это называется "тройное рукопожатие" подобные вещи были уже реализованы еще в прошлом веке например в TCP-IP.
Re[2]: Реализация паттерна Async Completion Token
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 20.05.05 16:31
Оценка: +1
Здравствуйте, <Аноним>, Вы писали:

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


E>>Вот здесь профессор Дуглас Шмидт описывает паттерн Asynchronous Completion Token. Суть патерна в том, что клиент, выполняя запрос к серверу, передает на сервер специальный объект (Asynchronous Completion Token -- ACT). Сервер возвращает этот объект клиенту в ответе. Благодоря ACT клиент понимает, на какой именно из его запросов ответил сервер. Особенно это удобно при асинхронном взаимодействии клиента и сервера, когда по одному каналу связи идут потоки запросов и ответов. Ключевым моментом в паттерне Asynchronous Completion Token является то, что объект ACT, в общем случае, является непрозрачным для сервера (т.е. сервер совершенно не знает, что именно находится в ACT).


E>>Забавно, что я наткнулся на описание этого паттерна вскоре после того, как реализовал с помощью ObjESSty подобный механизм. В моем случае требовалось организовать цепочку процессов (которые, возможно, работают на разных узлах сети) и которые в асинхронном режиме обслуживают запросы (транзакции) клиента, подключенного к одному из концов этой цепочки (эта цепочка выглядит для клиента как один сервер). Причем каждая транзакция состоит из нескольких сообщений. Например, одна из транзакций состоит из трех сообщений:

E>>1. send -- инициируется клиентом и содержит описание необходимых действий. Клиент повторяет его до тех пор, пока не получит send_result.
E>>2. send_result -- отсылается клиенту в ответ на send. Повторяется до тех пор, пока клиент не пришлет send_finish. Сделано это для того, чтобы в случае потери единичного send_result клиент не инициировал запрос send еще раз.
E>>3. send_finish -- отсылается клиентом в ответ на send_result. Это сообщение говорит серверу, что клиент полностью завершил транзакцию и что сервер может удалить у себя информацию о данной транзакции.
E>>Такая трехфазная схема позволяет организовать асинхронное взаимодействие между клиентом и сервером через один канал. Клиент может инициировать несколько send не дожидаясь ответа на предудущие send. Однако, эта схема требует, чтобы каждая транзакция имела уникальный идентификатор.

А>По-моему это называется "тройное рукопожатие" подобные вещи были уже реализованы еще в прошлом веке например в TCP-IP.


И что? TCP/IP -- это транспортный протокол, а не прикладной. Я же говорил о прикладном протоколе.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[3]: Реализация паттерна Async Completion Token
От: Аноним  
Дата: 20.05.05 21:22
Оценка: +1
Здравствуйте, eao197, Вы писали:


А>>По-моему это называется "тройное рукопожатие" подобные вещи были уже реализованы еще в прошлом веке например в TCP-IP.


E>И что? TCP/IP -- это транспортный протокол, а не прикладной. Я же говорил о прикладном протоколе.


Я просто к тому, что такая схема взаимных подтверждений используется во многих местах. Не рассматривайте как наезд
Re[4]: Реализация паттерна Async Completion Token
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.05.05 03:34
Оценка:
Здравствуйте, <Аноним>, Вы писали:

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



А>>>По-моему это называется "тройное рукопожатие" подобные вещи были уже реализованы еще в прошлом веке например в TCP-IP.


E>>И что? TCP/IP -- это транспортный протокол, а не прикладной. Я же говорил о прикладном протоколе.


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


Я и не расматриваю, просто речь шла не о том, что я придумал трехфазный протокол. Главная цель была -- показать реализацию ACT через средства ObjESSty. А трехфазный протокол -- это пример того, где подобные ACT удобно применять.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[2]: Нужен совет
От: GlebZ Россия  
Дата: 21.06.05 11:02
Оценка:
Здравствуйте, eao197, Вы писали:


E>>Но, как я понимаю, в B+ дереве должны храниться только уникальные ключи.

Откуда такие сведения?
E>Может я где-то ошибся?
Почти нет. Если проблема еще не решена, то могу рассказать.

С уважением, Gleb.
Re[3]: Нужен совет
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.06.05 11:07
Оценка:
Здравствуйте, GlebZ, Вы писали:

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



E>>>Но, как я понимаю, в B+ дереве должны храниться только уникальные ключи.

GZ>Откуда такие сведения?

По незнанию и недопониманию

E>>Может я где-то ошибся?

GZ>Почти нет. Если проблема еще не решена, то могу рассказать.

Да я разобрался, вроде. Просто не могу найти время на реализацию

Но спасибо за участие. И если есть возможность рассказать, то разскажи. Моя благодарность не будет знать границ в рамках доступных мне оценок
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[4]: Нужен совет
От: GlebZ Россия  
Дата: 21.06.05 11:36
Оценка: 14 (1)
Здравствуйте, eao197, Вы писали:


E>И если есть возможность рассказать, то разскажи. Моя благодарность не будет знать границ в рамках доступных мне оценок

Хе-хе. Это я люблю.

Вобщем система такая. Три главных свойства Б+ индекса за которые его любят базоведы:
1. Логарифмический поиск, добавление и удаление
2. Доступ к листьям как к сортированному списку
3. Хранение в сегментах.
Мне как-то приходилось делать Б+ дерево. Но одной из задач мне нужно было находить количество элементов в заданном диапазоне. Поэтому ключи были неуникальными. В результате, я мог не доходя до листьев(а это дисковые операции) оценивать количество объектов в заданном диапазоне.
В System/R была несколько другая система. Там правда для ихнего оптимизатора эта задача и не нужна была. В ихнем Б+ дереве были действительно уникальные значения. В листьях хранился список PID с указателями на кортежи. Ессно, при этом был организован и доступ к сортированному списку как по списку листьев со спуском на внутренние списки PID.

Поэтому я и сказал, что почти прав. То есть можно, и так, и так.

С уважением, Gleb.
Re[5]: Нужен совет
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.06.05 11:57
Оценка:
Здравствуйте, GlebZ, Вы писали:

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



E>>И если есть возможность рассказать, то разскажи. Моя благодарность не будет знать границ в рамках доступных мне оценок

GZ>Хе-хе. Это я люблю.

GZ>Вобщем система такая. Три главных свойства Б+ индекса за которые его любят базоведы:

GZ>1. Логарифмический поиск, добавление и удаление
GZ>2. Доступ к листьям как к сортированному списку
GZ>3. Хранение в сегментах.

Вот здесь еще один повод для сомнений у меня есть. B+ деревья хорошо подходят для данных фиксированной длины (или имеющих разумные органичения по размеру). Если же ключи могут быть разной длины (строки, например), то нужно, чтобы ключ помещался на страницу. А вот если нет?
Я просто хотел бы в ObjESSty добавить индексы и использовать для задачи, в которой как раз ключами будут строки разной длины (она описана здесь: Реализация паттерна Async Completion Token
Автор: eao197
Дата: 20.05.05
).

GZ>Мне как-то приходилось делать Б+ дерево. Но одной из задач мне нужно было находить количество элементов в заданном диапазоне. Поэтому ключи были неуникальными. В результате, я мог не доходя до листьев(а это дисковые операции) оценивать количество объектов в заданном диапазоне.


А как ты это делал? Что-то сразу не вьезжаю.



Вообще-то я сейчас курю по поводу libgist (GiST). Ее в PostgreSQL используют. Смущает то, что в GiST применяются непереносимые (платформно-зависимые) файлы для хранения индексов. И не понятно, как мне к своей транзакционности изменение этих индексных файлов пристегнуть.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[6]: Нужен совет
От: GlebZ Россия  
Дата: 21.06.05 12:29
Оценка:
Здравствуйте, eao197, Вы писали:

E>Вот здесь еще один повод для сомнений у меня есть. B+ деревья хорошо подходят для данных фиксированной длины (или имеющих разумные органичения по размеру). Если же ключи могут быть разной длины (строки, например), то нужно, чтобы ключ помещался на страницу. А вот если нет?

А если нет, но это совсем...
Я просто хотел бы в ObjESSty добавить индексы и использовать для задачи, в которой как раз ключами будут строки разной длины (она описана здесь: Реализация паттерна Async Completion Token
Автор: eao197
Дата: 20.05.05
).
Немного не понял зачем тебе там Б+. Ты хочешь получать по UID другой UID?

GZ>>Мне как-то приходилось делать Б+ дерево. Но одной из задач мне нужно было находить количество элементов в заданном диапазоне. Поэтому ключи были неуникальными. В результате, я мог не доходя до листьев(а это дисковые операции) оценивать количество объектов в заданном диапазоне.


E>А как ты это делал? Что-то сразу не вьезжаю.

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

E>

E>Вообще-то я сейчас курю по поводу libgist (GiST). Ее в PostgreSQL используют. Смущает то, что в GiST применяются непереносимые (платформно-зависимые) файлы для хранения индексов. И не понятно, как мне к своей транзакционности изменение этих индексных файлов пристегнуть.
Это мои любимые велосипеды. Для меня такое сделать самый цимес.

С уважением, Gleb.
Re[7]: Нужен совет
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.06.05 12:40
Оценка:
Здравствуйте, GlebZ, Вы писали:

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


E>>Вот здесь еще один повод для сомнений у меня есть. B+ деревья хорошо подходят для данных фиксированной длины (или имеющих разумные органичения по размеру). Если же ключи могут быть разной длины (строки, например), то нужно, чтобы ключ помещался на страницу. А вот если нет?

GZ>А если нет, но это совсем...

Вот и я о том же сожалею. И думаю, а не использовать ли мне что-нибудь другое. Например AVL-деревья. Ведь все равно у меня объекты нарезаются на маленькие блоки и размазываются по хранилищу. Точно так же будут и элементы AVL-дерева по хранилищу размазываться, вне зависимости от длины ключа.

E>>Я просто хотел бы в ObjESSty добавить индексы и использовать для задачи, в которой как раз ключами будут строки разной длины (она описана здесь: Реализация паттерна Async Completion Token
Автор: eao197
Дата: 20.05.05
).


GZ>Немного не понял зачем тебе там Б+. Ты хочешь получать по UID другой UID?


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

В принципе, в качестве ключа можно будет использовать хеш двоичного образа. Но на хешах нельзя строить упорядоченные множества и коллизии как-то разрешать нужно. Хотя нужно ли в этом случае упорядочение -- еще большой вопрос.
Я просто еще не выбрал окончательного решения и обдумываю разные варианты.

GZ>>>Мне как-то приходилось делать Б+ дерево. Но одной из задач мне нужно было находить количество элементов в заданном диапазоне. Поэтому ключи были неуникальными. В результате, я мог не доходя до листьев(а это дисковые операции) оценивать количество объектов в заданном диапазоне.


E>>А как ты это делал? Что-то сразу не вьезжаю.

GZ>Немного соврал. До листьев я добирался. Давно делал, только сейчас понял. Хотя в принципе можно было так и не делать. Абсолютная точность была не нужна (нужно было процентное отношение), и можно было просчитывать по средней балансировке. Балансировка была по золотому сечению, поэтому можно было предствить средний коэффициент заполнености, и по нему подсчитывать достаточно близкие значения к реальным.

Ба! Прочитал с открытым ртом
Если серьезно, то я не большой спец в алгоритмах.

E>>

E>>Вообще-то я сейчас курю по поводу libgist (GiST). Ее в PostgreSQL используют. Смущает то, что в GiST применяются непереносимые (платформно-зависимые) файлы для хранения индексов. И не понятно, как мне к своей транзакционности изменение этих индексных файлов пристегнуть.
GZ>Это мои любимые велосипеды. Для меня такое сделать самый цимес.

Любимые велосипеды -- это libgist что-ли?
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.