Определение конкретного типа методом научного тыка
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.159] |
|
|
| Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Определение конкретного типа методом научного тыка
|
Сообщ.
#1
,
|
|
|
|
Всем привет!
Проблема следующая: Есть N конкретных классов C1, C2, ....., CN Все они наследуются от абстрактного класса A Задача: Реализовать механизм сравнения обьектов любого из классов С с любым другим. Ограничения: Не юзать RTTI и поля типа. Множественную передачу не предлагать - слишком геморно. Вот взбрела такая идея в голову: Имея чисто виртуальную функцию f, достаточно ли сравнить адрес этой функции у двух обьектов, чтобы знать одного они типа или нет? Например: ![]() ![]() class A { virtual ~A () {} virtual void f () = 0; virtual bool compare (const A& a) = 0; }; class C1 : public A { virtual void f () {} virtual bool compare (const A& a) { if (/*адреса f для a и this разные*/) { return false; } // Bla bla bla return smth; } }; class C2 : public A { virtual void f () {} virtual bool compare (const A& a) { if (/*адреса f для a и this разные*/) { return false; } // Bla bla bla return smth; } }; Как думаете будет работать? И главный вопрос: как сделать это самое сравнение по безопаснее и по переносимее? |
|
Сообщ.
#2
,
|
|
|
|
Цитата Gunnar @ Вот взбрела такая идея в голову: Имея чисто виртуальную функцию f, достаточно ли сравнить адрес этой функции у двух обьектов, чтобы знать одного они типа или нет? Адрес метода можно взять только у класса - не у объекта. Так что не прокатит. |
|
Сообщ.
#3
,
|
|
|
|
Цитата Gunnar @ достаточно ли сравнить адрес этой функции у двух обьектов, чтобы знать одного они типа или нет? Должно, если все классы переопределяют эту ф-цию, только вот проблема: как ты узнаешь этот адрес ?Цитата Gunnar @ Не юзать RTTI dynamic_cast - это тоже rtti? |
|
Сообщ.
#4
,
|
|
|
|
Цитата archimed7592 @ dynamic_cast - это тоже rtti? Ну дык да. Вмысле он реализован, используя RTTI. Добавлено Цитата archimed7592 @ как ты узнаешь этот адрес Вот это как раз самое интересное. Можно сделать вот такой хак (первое, что пришло в голову) ![]() ![]() class A { virtual ~A () {} virtual void f () = 0; virtual bool compare (const A& a) = 0; }; struct A_vtbl { ptr_t dtor; ptr_t f; ptr_t compare; }; class C1 : public A { virtual void f () {} virtual bool compare (const A& a) { A_vtbl* pvtbl_a = 0; A_vtbl* pvtbl_this = 0; pvtbl_a = *((A_vtbl**)&a); pvtbl_this = *((A_vtbl**)this); if (pvtbl_a->f != pvtbl_this->f) { return false; } // Bla bla bla return smth; } }; ну как? |
|
Сообщ.
#5
,
|
|
|
|
Цитата Gunnar @ ну как? нуу.. работать будет Добавлено Цитата LuckLess @ нуу.. работать будет ![]() только если все классы C1, C2 ... CN гарантировано переопределяют эту функцию сами! А не в базовом классе.., скажем. |
|
Сообщ.
#6
,
|
|
|
|
Цитата LuckLess @ только если все классы C1, C2 ... CN гарантировано переопределяют эту функцию сами! А не в базовом классе.., скажем. Ну это понятно. Как бы это по красивее реализовать? есть идеи? |
|
Сообщ.
#7
,
|
|
|
|
Цитата Gunnar @ Можно сделать вот такой хак (первое, что пришло в голову) Если так, то можно и без хака: ![]() ![]() #define DEFINE_GET_SIGN_METHOD virtual void* GetSign() const { static Sign sign; return &sign; } class A { protected: struct Sign {}; public: static bool IsEqualClass(const A& a1, const A& a2) { return a1.GetSign() == a2.GetSign(); } protected: DEFINE_GET_SIGN_METHOD }; class C1 : public A { protected: DEFINE_GET_SIGN_METHOD }; class C2 : public A { protected: DEFINE_GET_SIGN_METHOD }; void main() { A* p1 = new C1; A* p2 = new C2; bool b1 = A::IsEqualClass(*p1, *p2); } |
|
Сообщ.
#8
,
|
|
|
|
Оооооооооо! Вот это идея. Спасибо!
|
|
Сообщ.
#9
,
|
|
|
|
писал библиотечку классов на голом С
там было примерно так: ![]() ![]() typedef struct _TObjectClass TObjectClass; struct TObject { TObjectClass *Class; ... } struct _TObjectClass { const char *ClassName(); TObjectClass *ParentClass(); } static TObjectClass *AObjectClass=NULL;//-один для всего класса инициализируется при вызове первого конструктора //наследники от TObject typedef struct _TObject1Class TObject1Class; struct TObject1 { TObject Parent; ... } struct _TObject1Class { TObjectClass ParentClass; ... } static TObject1Class *AObject1Class=NULL;//-один для всего класса инициализируется при вызове первого конструктора //наследники от TObject1 typedef struct _TObject2Class TObject2Class; struct TObject2 { TObject1 Parent; ... } struct _TObject1Class { TObject1Class ParentClass; ... } static TObject2Class *AObject2Class=NULL;//-один для всего класса инициализируется при вызове первого конструктора в таком случае вполне можно было узнать тот ли это клас или наследник (рекурсией с выходом FALSE по условию Class->ParentClass=NULL, либо TRUE если см ниже): ![]() ![]() ((TObject*) A)->Class==((TObject*) B)->Class; |
|
Сообщ.
#10
,
|
|
|
|
Ой
Случайно заглянул ... А как на счет множественного (виртуального и невиртуального) наследования? ... Эти ваши "уау-фишки" тоже заработают? |
|
Сообщ.
#11
,
|
|
|
|
Цитата Majestio @ Эти ваши "уау-фишки" тоже заработают? ![]() Вообще сама идея очень плохая. Самое простое и надёжное это задать для каждого типа уникальный идентификатор. Целочисленный. Если использовать указатель на процедуру, то определить тип невозможно. Можно только обнаружить разницу между типами объектов. А теперь представим себе крутой компилятор с удивительно продвинутой оптимизацией. Допустим, он обнаружит по-байтное совпадение процедур объектов разных типов и будет использовать одну процедуру на всех. Теперь про указатели. Вычислить указатель на процедуру класса можно. Для этого не обязательно использовать: 1. Процедуру f () 2. Указатель на начало процедуры (любой) Как то раз я написал функцию, которая получает адрес возврата. Она не сложная. Вызываем её из процедуры типа getAddr() и получаем адрес для идентификации обьекта. Вот, нашел: ![]() ![]() #ifdef _M_IX86 const size_t RET_ADDR_STACK_OFFSET = 2; #endif #ifdef _M_AMD64 const size_t RET_ADDR_STACK_OFFSET = 3; #endif // получим адрес возврата #pragma warning (push) #pragma warning (disable:4700) #pragma optimize ("", off) // .......................................................................... PVOID WINAPI meas_f(void) { volatile PVOID caller = (PVOID)(&caller)[RET_ADDR_STACK_OFFSET]; return caller; } // .......................................................................... #pragma optimize ("", on) #pragma warning (pop) Для вычисления идентификатора интерфейс вообще ни при чём. Можно вычислять адрес любой одинаковой для родственных классов функции. ![]() ![]() class I_A { protected: PVOID addr; public: virtual ~I_A() {} virtual void f () = 0; }; class B : virtual public I_A { public: B() {} virtual ~B(){} virtual void f (); virtual void GetAddr2 (); }; void B::f() { } void B::GetAddr2 () { addr = meas_f(); _tprintf (_T("%s addr=%p\n"),_T(__FUNCTION__),addr); } Если смастерить разных типов родственных классов то можно зафиксировать различие адресов. Не буду приводить все исходники. |
|
Сообщ.
#12
,
|
|
|
|
Народ. Давайте я перефразирую вопрос из первого поста:
Цитата Как мне получить динамический тип объекта, не используя динамический тип объекта? Ничего не смущает? |
|
Сообщ.
#13
,
|
|
|
|
Цитата Qraizer @ Ничего не смущает? Возможно он имел ввиду нечто: ![]() ![]() class Base { public: std::string type_name() const { return "Base"; } }; class Derived : public Base { public: std::string type_name() const { return "Derived"; } // скрывает Base::type_name }; Derived d; d.type_name(); // "Derived" Base& b = d; b.type_name(); // "Base" Base* p = &d; p->type_name(); // "Base" Для полиморфных классов, что-то типа: ![]() ![]() class Shape { public: virtual ~Shape() = default; std::string type_name() const { // туточки невиртуальный фасад return type_name_impl(); } private: virtual std::string_view type_name_impl() const { return "Shape"; } }; class Circle : public Shape { private: std::string_view type_name_impl() const override { return "Circle"; } }; class Square : public Shape { private: std::string_view type_name_impl() const override { return "Square"; } }; Ну я так понял его вопрос Хотя тут хрен уточнишь - чел на форуме не был с 2008 года. |
|
Сообщ.
#14
,
|
|
|
|
Цитата Majestio @ Неважно, чего он хочет. Он отключил фичу, которая наура решает его задачу и спрашивает, как теперь с этим жить. Возможно он имел ввиду нечто: ... |
|
Сообщ.
#15
,
|
|
|
|
|