На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> Определение конкретного типа методом научного тыка
    Всем привет!

    Проблема следующая:
    Есть N конкретных классов C1, C2, ....., CN
    Все они наследуются от абстрактного класса A

    Задача:
    Реализовать механизм сравнения обьектов любого из классов С с любым другим.

    Ограничения:
    Не юзать RTTI и поля типа.

    Множественную передачу не предлагать - слишком геморно.

    Вот взбрела такая идея в голову:
    Имея чисто виртуальную функцию f, достаточно ли сравнить адрес этой функции у двух обьектов, чтобы знать одного они типа или нет?

    Например:
    ExpandedWrap disabled
      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;
        }
       
      };


    Как думаете будет работать?
    И главный вопрос: как сделать это самое сравнение по безопаснее и по переносимее?
    Сообщение отредактировано: Gunnar -
      Цитата Gunnar @
      Вот взбрела такая идея в голову:
      Имея чисто виртуальную функцию f, достаточно ли сравнить адрес этой функции у двух обьектов, чтобы знать одного они типа или нет?

      Адрес метода можно взять только у класса - не у объекта. Так что не прокатит.
        Цитата Gunnar @
        достаточно ли сравнить адрес этой функции у двух обьектов, чтобы знать одного они типа или нет?

        Должно, если все классы переопределяют эту ф-цию, только вот проблема: как ты узнаешь этот адрес :)?


        Цитата Gunnar @
        Не юзать RTTI

        dynamic_cast - это тоже rtti? :)
          Цитата archimed7592 @
          dynamic_cast - это тоже rtti?

          Ну дык да. Вмысле он реализован, используя RTTI.

          Добавлено
          Цитата archimed7592 @
          как ты узнаешь этот адрес

          Вот это как раз самое интересное.

          Можно сделать вот такой хак (первое, что пришло в голову)

          ExpandedWrap disabled
            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;
              }
            };


          ну как?
            Цитата Gunnar @
            ну как?

            нуу.. работать будет :rolleyes:

            Добавлено
            Цитата LuckLess @
            нуу.. работать будет :rolleyes:

            только если все классы C1, C2 ... CN гарантировано переопределяют эту функцию сами! А не в базовом классе.., скажем.
              Цитата LuckLess @
              только если все классы C1, C2 ... CN гарантировано переопределяют эту функцию сами! А не в базовом классе.., скажем.

              Ну это понятно.

              Как бы это по красивее реализовать? есть идеи?
                Цитата Gunnar @
                Можно сделать вот такой хак (первое, что пришло в голову)

                Если так, то можно и без хака:
                ExpandedWrap disabled
                  #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);
                  }
                Сообщение отредактировано: Hryak -
                  Оооооооооо! Вот это идея. Спасибо!
                    писал библиотечку классов на голом С
                    там было примерно так:
                    ExpandedWrap disabled
                      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 если см ниже):
                    ExpandedWrap disabled
                      ((TObject*) A)->Class==((TObject*) B)->Class;
                      Ой :blink: Случайно заглянул ... А как на счет множественного (виртуального и невиртуального) наследования? ... Эти ваши "уау-фишки" тоже заработают? :rolleyes:
                        Цитата Majestio @
                        Эти ваши "уау-фишки" тоже заработают? :rolleyes:

                        Вообще сама идея очень плохая.
                        Самое простое и надёжное это задать для
                        каждого типа уникальный идентификатор. Целочисленный.

                        Если использовать указатель на процедуру, то определить тип невозможно.
                        Можно только обнаружить разницу между типами объектов.
                        А теперь представим себе крутой компилятор с удивительно продвинутой
                        оптимизацией. Допустим, он обнаружит по-байтное совпадение процедур
                        объектов разных типов и будет использовать одну процедуру на всех.

                        Теперь про указатели.
                        Вычислить указатель на процедуру класса можно.
                        Для этого не обязательно использовать:
                        1. Процедуру f ()
                        2. Указатель на начало процедуры (любой)

                        Как то раз я написал функцию, которая получает адрес возврата.
                        Она не сложная.
                        Вызываем её из процедуры типа getAddr() и получаем адрес
                        для идентификации обьекта.

                        Вот, нашел:
                        ExpandedWrap disabled
                          #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)


                        Для вычисления идентификатора интерфейс вообще ни при чём.
                        Можно вычислять адрес любой одинаковой для родственных классов функции.

                        ExpandedWrap disabled
                          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);
                          }


                        Если смастерить разных типов родственных классов то можно зафиксировать
                        различие адресов. Не буду приводить все исходники.
                        Сообщение отредактировано: ЫукпШ -
                          Народ. Давайте я перефразирую вопрос из первого поста:
                          Цитата
                          Как мне получить динамический тип объекта, не используя динамический тип объекта?

                          Ничего не смущает?
                            Цитата Qraizer @
                            Ничего не смущает?

                            Возможно он имел ввиду нечто:

                            ExpandedWrap disabled
                              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"

                            Для полиморфных классов, что-то типа:

                            ExpandedWrap disabled
                              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 года.
                              Цитата Majestio @
                              Возможно он имел ввиду нечто:
                              ...
                              Неважно, чего он хочет. Он отключил фичу, которая наура решает его задачу и спрашивает, как теперь с этим жить. :-?
                                :lol:
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0.1033 ]   [ 16 queries used ]   [ Generated: 19.09.26, 18:06 GMT ]