C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Это не Excel, самодельный RTTI
Георгий
Отправлено: 14.08.2003, 00:01


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



По просьбе скучающего Jean
есть ОСРВ QNX 4.25 и компилятор к ней Watcom C++ 10.6 (последняя официальная версия, датированная 1994 годом — потом фирму перекупила SyBase и компиляторы Watcom C загнулись, но это уже другая история). В те времена только-только исключения придумали, но пользуюсь я этим компилятором сейчас и хочу RTTI, а его нету...
Помогите придумать удобную самодельную реализацию RTTI.
На данный момент у меня есть кое какая идея, но, что бы не мешать мыслительному процессу добровольных помошников, я её ближе к вечеру пятницы напишу, да и то если она хоть чем то будет превосходить другие предложения...
Jean
Отправлено: 15.08.2003, 12:02


Дежурный стрелочник

Группа: Участник
Сообщений: 34



Спасибо, конечно, за заботу biggrin.gif Ну ты вопрос задал. Я вот несколько не понял зачем нужны такие типы. В каких случаях бех них нельзя обойтись, дай какой-нибудь пример простенький (словами объясни, не программу). Вот, тогда может что-нибудь посоветую smile.gif
Георгий
Отправлено: 20.08.2003, 23:19


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



Ну есть какой-нибудь контейнер (пусть будет массив указателей на базовый класс) и, до некоторого момента времени, тех возможностей, которые предоставляет базовый класс, хватало на полноценную работу с элементами контейнера. А потом приспичело сделать "нечто", что работает далеко не со всеми потомками базового класса и, как следствие, не может быть реализовано, как метод базового класса. Вот тут и надо вовремя исполнения программы выделить некоторых представителей базового класса, что и позволяет легко и просто сделать RTTI.

Очевидно, что это "болезнь роста" т.е. необходимость писать код ради кода (что бы сохранить ранее написанный и отлаженный код без изменений), но кроме RTTI ничего лучше нет.

Да кстати (как пример из реальной жизни) — когда работаешь с БД через BDE и надо установить формат отображения в DBGrid полей, содержащих дробные числа, то TField приходится преобразовывать в TFloatField и устанавливать нужный формат, а вот это без RTTI сложно сделать...
Jean
Отправлено: 21.08.2003, 20:59


Дежурный стрелочник

Группа: Участник
Сообщений: 34



По поводу кода для кода: лучше бы ты переделал smile.gif Я бы постарался. По-моему ты слишком усложняешь свою программу.
А по поводу БДЕ: никогда такой проблемы не возникало и ни разу формат поля я не менял...
Asher
Отправлено: 04.09.2003, 14:14


Мастер участка

Группа: Модератор
Сообщений: 550



Меня эта тем интересует.
Хочу свою RTTI, не связанную с VCL, для того, чтобы не городить всю иерархию от TObject.
Хотел бы поучаствовать в этой теме. Пока копаю инфу, и кое-что нашел...

Но боюсь в случае Watcom все гораздо сложнее — насколько я понял в компиляторе должна быть поддержка этого дела, об этом говорит хотя бы пункт Enable RTTI на закладке С++ опций проекта.

P.S. Буду благодарен за любую информацию на данную тему
Asher
Отправлено: 04.09.2003, 18:23


Мастер участка

Группа: Модератор
Сообщений: 550



кстати, вот наткнулся:
QUOTE
Среди нововведений, появившихся в Watcom C++ 11.0, — пространства имен, соответствующие стандарту ANSI/ISO C++, и динамическая идентификация типов (RTTI).

PC Magazine RE, February 4, 1997, p. 189 Си++: в поисках RADости
Дуглас Боулинг
Георгий
Отправлено: 04.09.2003, 19:30


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



Watcom C++ 11.0 — не вышел из beta версий и набор бинарников для работы в qnx я нигде не видел ( а для M$ у меня есть диск с WC 11.0 )
не первый раз замечаю, что ты часто вырезки из старых статей пишешь — как тебе удаётся это раскапывать?
LeeMouse
Отправлено: 05.09.2003, 12:58


Дежурный стрелочник

Группа: Участник
Сообщений: 79



есть большое количество учебных статей по построению искусственного RTTI. Хотя бы у того же Гради Буча. В инете оно есть, искать по ключевым словам. Если полиморфизм суппортится нормально, то свою RTTI можно создать без проблем, но вот только мне представляется это нерентабельным: надо реальные проекты создавать (чтоб денежки зарабатывать), поэтому я бы в этой ситуации сначала озаботился адекватной инструменталкой со всеми необходимыми функциями (RTTI в том числе)
Asher
Отправлено: 05.09.2003, 14:19


Мастер участка

Группа: Модератор
Сообщений: 550



LeeMouse писал:
QUOTE
В инете оно есть, искать по ключевым словам

А сам пробовал? Если нет, то попробубуй поищи, и внимательно прочти те 15 ссылок с просто упоминанием ключевых слов.
QUOTE
Хотя бы у того же Гради Буча
А сам его читал? Я читал, кроме словоблудия на пальцах ничего путного по данному вопросу не нашел. Хочешь реально помочь — давай конкретную ссылку на литературу. Хотя бы название книги и примерное название раздела.
QUOTE
сначала озаботился адекватной инструменталкой со всеми необходимыми функциями (RTTI в том числе)

Правда? а а это читал?
QUOTE
есть ОСРВ QNX 4.25 и компилятор к ней Watcom C++ 10.6 (последняя официальная версия, датированная 1994 годом... В те времена только-только исключения придумали, но пользуюсь я этим компилятором сейчас и хочу RTTI, а его нету...

Порекомендуй человеку средство разработки под его QNX, а советы типа возьми правильное средство не прокатят.
Теперь по поводу
QUOTE
Если полиморфизм суппортится нормально, то свою RTTI можно создать без проблем
Пример в студию. Если аналог некотрых вещей из PTypeInfo можно сделать ручками через структуру в базовом классе(заполняемую вручную в потомках), то очень бы хотелось посмотреть на созданную без проблем реализацию dynamic_cast, или хотя-бы static_cast. И нахрена это было в стандарт языка закладывать, а потом новые компиляторы писать с поддержкой RTTI?

маленькое отступление для LeeMouse:меня ответы типа читайте API или Help, которые ты расплодил по форуму не прут конкретно. Если реально знаешь где, то нормальную наводку давай или хоть ключевые слова. Нетрудно знать ответы на все вопросы, трудно знать какой ответ какому вопросу соответствует. А если на все вопросы отвечать RTFM то нахрена вообще утруждаться? или рейтинг нужен? тогда сходи на iXBT или Borland X Portal, а еще лучше на RSDN, там рейтинги любят. Только за такие ответы ты свой любимый рейтинг не повысишь...
Ко всем кого мой пост, возможно, задел — извините за резкость.
LeeMouse
Отправлено: 05.09.2003, 16:07


Дежурный стрелочник

Группа: Участник
Сообщений: 79



за резкозть извиняю... хотя не задело smile.gif)) книжка называется "Гради Буч. Объектно-ориентированный анализ и проектирование", сам читал. За свой 14-летний стаж разработчика имею 9 больших проектов. Писать развёрнутые примеры не намерен (я лучше потрачу время на проект биллинга крупного оператора сотовой связи, которым сейчас занимаюсь). Если хочешь пользоваться устаревшим продуктом — пользуйся. Про динамик каст я не говорил, разумеется речь шла о том, что можно реализовать в условиях без поддержки RTTI. Рейтинг мне не нужен, ни здесь, ни где либо ещё wink.gif)) Мне значительно более важен мой рейтинг у моих заказчиков, выражаемый портретами Франклина smile.gif))))
Георгий
Отправлено: 05.09.2003, 19:26


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



Пришёл LeeMouse и всё опошлил... smile.gif

А если серьёзно, то:
1. мне показалась задача интересной сама по себе, а о её практическом применении хорошо сказал Jean в своём последнем посте — типа не дёргайся надо будет переделать — переделаешь (я так даже больше денег заработаю мол "в структуру проекта были внесены принципиальные изменения, которые позволят ... и дадут ... ").
2. как вы помните Jean`у надоели вопросы по Excel — вот я и попытался помочь дав задачку, которая (с моей точки зрения) решается тривиально, но сложно "переключиться" на не прикладной образ мышления.
3. обещал идею выложить, но то забывал (пока ждал пятницу, уже успевал забыть что же хотел написать), то Jean отговаривал от RTTI

И так! сегодня пятница и я выкладываю своё видение идеи самодельного RTTI:

идея:
сделать один базовый класс для всех других классов, методы которого вызывали бы потомки для своей "регистрации"

реализация (т.к. Wc мало у кого есть, то код для BCB т.е. нужна форма, кнопка и мемо):
CODE
class RTTIBase
       {
protected:
       TStringList *ptrClasses;

       RTTIBase(void);
       RTTIBase(const RTTIBase&);
       RTTIBase& operator=(const RTTIBase&);
       virtual ~RTTIBase(void);

       void AddClass( const AnsiString& );
public:
       AnsiString virtual GetClassName(void)const;
       virtual bool    MayCastTo( const AnsiString& CastClassName )const;
       };

class A:public RTTIBase
       {
public:
       A(void):RTTIBase(){ this->AddClass( this->GetClassName() ); };
       ~A(void){};
       AnsiString GetClassName(void)const{ return "A"; };
       };

class B:public A
       {
public:
       B(void):A(){ this->AddClass( this->GetClassName() ); };
       ~B(void){};
       AnsiString GetClassName(void)const{ return "B"; };
       };


RTTIBase::RTTIBase( void )
       {
       this->ptrClasses=new TStringList;
       if ( !this->ptrClasses )
               throw Exception("Íå äîñòàòî÷íî ïàìÿòè");
       };

RTTIBase::RTTIBase(const RTTIBase& a)
       {
       RTTIBase();
       *this=a;
       };

RTTIBase& RTTIBase::operator=(const RTTIBase& a)
       {
       this->ptrClasses->Text=a.ptrClasses->Text;
       return *this;
       };

RTTIBase::~RTTIBase(void)
       {
       delete this->ptrClasses;
       };

void RTTIBase::AddClass( const AnsiString& c)
       {
       this->ptrClasses->Add( c );
       };

AnsiString RTTIBase::GetClassName(void)const
       {
       return "RTTIBase";
       };

bool    RTTIBase::MayCastTo( const AnsiString& CastClassName )const
       {
       return (this->ptrClasses->IndexOf( CastClassName ) != -1);
       };


void __fastcall TForm1::Button1Click(TObject *Sender)
{
A a;
B b;
AnsiString str;

RTTIBase* From[]={&a,&b};
RTTIBase* To[]={&a,&b};

int i,j;

str="имя класса A=";str+=a.GetClassName();
this->Memo1->Lines->Add( str );
str="имя класса B=";str+=b.GetClassName();
this->Memo1->Lines->Add( str );

for ( i=0; i< (sizeof (From) / sizeof (From[0])); i++ )
       for ( j=0; j< (sizeof (To) / sizeof (To[0])); j++ )
       {
       str="попытка преобразования";
       str+=From[i]->GetClassName(); str+=" в "; str+=To[j]->GetClassName(); str+=" =";
       str+=AnsiString( (int) From[i]->MayCastTo( To[j]->GetClassName() ) );
       this->Memo1->Lines->Add( str );
       };
}


Минусы такого подхода:
1. каждый экземпляр класса хранит свою родословную, что несколько не рационально
2. не так красиво, как настоящий RTTI (приходится явно в коде класса указывать его имя как текстовую строку)

Решения минусов:
1. каждому экземпляру дать указатель на таблицу родословных, из которых он и будет выбирать свою.
2. ничего не поделаешь.

Плюсы:
1. как факт теперь можно во времени выполнения идентифицировать типы

Отредактировано Георгий — 05/09/2003, 21:19
Asher
Отправлено: 06.09.2003, 15:05


Мастер участка

Группа: Модератор
Сообщений: 550



QUOTE
приходится явно в коде класса указывать его имя как текстовую строку

если в Wc есть макрос __FILE__, то приняв:
1. Один модуль == один класс
2. Имя модуля == имя класса
можно сделать так:
Добавить в класс поле CName;
Изменить метод AnsiString GetClassName(void){return CName;};
Добавить в каждый конструктор строку
CODE
CName = ExtractFileName(__FILE__).SubString(1, ExtractFileName(__FILE__).Pos(".") — 1);//


Минусы: 1. Необходимость во всех модулях потомках писать CName = ExtractFileName(... sad.gif
Это конечно более громоздко, чем простая строковая константа, но зато единообразно. biggrin.gif biggrin.gif biggrin.gif
P.S. Можнл оформить всю конструкцию справа от = как макрос в базовом классе, но это не осовобождает от необходимости инициализить CName в потомках. Надо дальше думать. wink.gif

Отредактировано Asher — 06/09/2003, 17:10
Георгий
Отправлено: 05.12.2004, 15:48


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



почему никто не сказал "сам то понимаешь что спросил?"

вернулся снова к преобразованию, но в этот раз с иллюзией понимания.

есть устоявшаяся структура в виде класса Test умеющего выполнять некую обработку (TestBase) с использованием методов baseInterface (baseMethod)

хочется расширить функциональность Test введя новые методы обработки (TestSecond) использующие в свою очередь работу с объектом обработки через дополнительный интерфейс secondInterface.

казалось бы ничего сложного:
1. используем в новых методах dynamic_cast для получения интерфейса secondInterface и всё в порядке, но не тут то было — нету у меня dynamic_cast (вынужден использовать компилятор не поддерживающий RTTI)
2. вводим в базовый класс виртуальный метод CastToSecondInterface. в потомках его поддерживающих перегружаем и возвращаем указатель на себя. но есть и минус — почему базовый должен заботиться о своих наследниках?

чутьё подсказывает "тут что то не то..."
идеи есть?

первый вариант реализован как метод Test1? второй — Test2, а Test3 — демонстрация того, что бывает если использовать явное приведеник к типу. (то, к чему сводилась предыдущая идея)

CODE
class secondInterface
{
public:
virtual void secontInterface(void)=0;
};
class baseInterface
{
public:
virtual void baseMethod(void)=0;
virtual secondInterface* ToSecond(void){return 0;};
};

class implementInterface:public baseInterface
{
public:
void baseMethod(void)
{
Form1->Memo1->Lines->Add("implementInterface::baseMethod");
};
};
class secondImpelent:public implementInterface, public secondInterface
{
public:
void secontInterface(void)
{
Form1->Memo1->Lines->Add("secondImpelent::secontInterface");
};
secondInterface* secondImpelent::ToSecond(void){return this;};
};

class Test
{
public:
baseInterface *base;
void TestBase ( void )
{
base->baseMethod();
};
static void TestSecond( secondInterface * second )
{
AnsiString s;
s.printf("ptr=%p",second);
Form1->Memo1->Lines->Add(s);
second->secontInterface();
};
void Test1( void )
{
secondInterface *second = dynamic_cast<secondInterface*>( this->base );
this->TestSecond(second);
};
void Test2( void )
{
secondInterface *second = this->base->ToSecond();
this->TestSecond(second);
};
void Test3( void )
{
secondInterface *second = (secondInterface*)( this->base );
this->TestSecond(second);
};
};

void TestAll( Test &tst )
{
Form1->Memo1->Lines->Add(" --- call TestBase");
try {
tst.TestBase();
}
catch(...)
{
Form1->Memo1->Lines->Add(" test fauled...");
};
Form1->Memo1->Lines->Add(" --- call Test1");
try {
tst.Test1();
}
catch(...)
{
Form1->Memo1->Lines->Add(" test fauled...");
};
Form1->Memo1->Lines->Add(" --- call Test2");
try {
tst.Test2();
}
catch(...)
{
Form1->Memo1->Lines->Add(" test fauled...");
};
Form1->Memo1->Lines->Add(" --- call Test3");
try {
tst.Test3();
}
catch(...)
{
Form1->Memo1->Lines->Add(" test fauled...");
};
};
void __fastcall TForm1::Button1Click(TObject *Sender)
{
implementInterface base;
this->Memo1->Lines->Add(" +++ test base ");
Test tst;
tst.base=&base;
TestAll( tst );
this->Memo1->Lines->Add(" +++ test second ");
secondImpelent second;
tst.base=&second;
TestAll( tst );
}


Отредактировано Георгий — 05/12/2004, 16:51

Присоединить изображение

Присоединить изображение


Вернуться в Вопросы программирования в C++Builder