Как найти типы предков объекта?

Я создаю механизм, с помощью которого получатели могут сообщить отправителю, что каждый получатель заинтересован в сообщениях определенного типа. В приведенном ниже примере реализации существует ограничение, при котором Получатель, который хочет получать все Сообщения определенного базового типа, получает только Сообщения, явно относящиеся к этому типу, и не будет получать Сообщения производного типа. type (см., например, main()).

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

Какие другие решения существуют?

Примечание. На самом деле я бы сохранил RTTI, чтобы не требовался поиск RTTI каждый раз. Есть и другие вещи, которые я пропустил / пропустил здесь. Я собираюсь для краткости с этим примером...

Пример кода ниже:

class Sender
{
  typdef std::vector<Receiver const & > Receivers;
public:
  void register(Receiver const & i_recv, typeinfo const & i_type)
  {
    m_routingMap[i_type].push_back(i_recv);
  }


  void send(BaseMsg const & i_msg)
  {
    Receivers receivers = m_routingMap.find(typeid(i_msg));
    for (Receivers::iterator receiver = receivers.begin(); receiver != receivers.end(); ++receiver) {
      receiver.receive(i_msg);
    }
  }

private:
  std::map<typeinfo const &, Receivers> m_routingMap;
};


class Receiver
{
public:
  void receiver(BaseMsg const & i_msg)
  {
    // React to expected messages here
  }
};


class BaseMsg {};

class ChildMsg : public BaseMsg {};

int main()
{
  Sender sndr;

  Receiver recv1;
  sndr.register(recv1, typeid(BaseMsg));

  Receiver recv2;
  sndr.register(recv2, typeid(ChildMsg));

  BaseMsg baseMsg;
  sndr.send(baseMsg); // I want only recv1 to receive this message

  ChildMsg childMsg;
  sndr.send(childMsg); // I want both Receivers to receive this message, but only recv2 will receive it
}

Обновление: вот решение, которое у меня есть:

// Note: implementation is based in gleaning from
// http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14

class BaseMsg
{
public:

  typedef std::vector<TypeInfo const & > Types;

  static TypeInfo const * getType()
  {
    TypeInfo static * ms_type = new TypeInfo(typeid(BaseMsg));
    return ms_type;
  }

  static Types const * getAncestorTypes()
  {
    // The base class does not have an ancestor
    // Static varible, will only be constructed once!
    Types * ms_ancestorTypes = new Types();
    return ms_ancestorTypes;
  }
};


class ChildMsg
{
public:
  static TypeInfo const * getType()
  {
    TypeInfo static * ms_type = new TypeInfo(typeid(ChildMsg));
    return ms_type;
  }

  static Types const * getAncestorTypes()
  {
    // Add the parent type and all the parent's ancestor's types
    Types const * ancestorTypes = BaseMsg::getAncestorTypes();

    // Static variable, so it will only be constructed once!
    Types * static ms_ancestorTypes = new Types(ancestorTypes->begin(), ancestorTypes->end());

    // This push_back() will occur every time, but it's only one operation,
    // so hopefully it's not a big deal!
    ms_ancestorTypes->push_back(BaseMsg::getType());

    return ms_ancestorTypes;
  }
};

И Отправитель:

# Python pseudo code
class Sender:
  def send(self, i_msg):
    types_to_check_for = [i_msg.getType()].extend(i_msg.getAncestorTypes())

    for type_ in types_to_check_for:
      for receiver in _routing_list[type_]:
        receiver.receive(i_msg)

person Chris Morris    schedule 22.03.2012    source источник


Ответы (1)


Возможно, рассмотрите возможность использования шаблона наблюдателя (http://en.wikipedia.org/wiki/Observer_pattern).

Таким образом, ваш отправитель не знает вашего получателя, а ваш наблюдатель может контролировать распространение сообщений.

Отправитель -> информирует наблюдателя о наличии сообщения.

наблюдатель -> информирует каждую заинтересованную сторону о новом сообщении.

заинтересованная часть -> делает забавные вещи.

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

Обновлять:

Краткая структура сообщения:

class Message
{
public:
    size_t m_Type;
    size_t m_Id;

protected:
    Message(size_t type, size_t id) : m_Type(type), m_Id(id) {}
};

class Type1 : public Message
{
public:
    static const size_t type = 1;
    Type1(size_t id) : Message(type, id) {}
};

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

Class subscriber
{
    virtual void receiveType(size_t type, char * data) = 0;
    virtual void receiveMsg(size_t type, size_t id, char * data) = 0;

};

У наблюдателя должен быть метод регистрации для сообщений:

Class Observer
{
void registerForType(type, subscriber);
void registerForMsg(type, id, subscriber);
};

Другое обновление:

На самом деле это просто грубое доказательство концепции. Можно делать то, что вы хотите, не зная точной цепочки предков. Простите переключение функций триггера и регистрации входа (сначала сделал неправильно, и это было самое простое исправление, опять же proof-of-concept). Другим недостатком этого скетча является то, что для регистрации необходимо создать как минимум сообщение. Если вы ищете действительно долгосрочное решение, я предлагаю вам найти библиотеку или фреймворк, в котором уже есть отражение (например, QT имеет метаобъекты), их можно использовать для просмотра суперклассов. Или вы можете использовать уже имеющиеся сигналы/слоты.

Вывод из приведенного ниже кода:

Начиная с C:\Users\David\Downloads\asdf-build-desktop-Qt

#include <string>
#include <vector>
#include <map>
#include <stdio.h>
using namespace std;

class BaseMsg
{
public:
    BaseMsg()
    {
        theRealInit();
    }

    //incase you don't want to go all the way down the rabbit hole.
    //At the bottom they are the same
    virtual vector<string> const & registrationEntries() const  {return m_SubClassChain;}
    virtual vector<string> const & triggerEntries() const       {return m_SubClassChain;}

    protected:
    virtual void init() { printf("Should NOT CALL THIS HERE!");}
    vector<string> m_SubClassChain;

private:

    void theRealInit()
    {
        m_SubClassChain.push_back("BaseMsg");
    }


};

class Message : public BaseMsg
{
    public:
    Message() : BaseMsg()
    {
        init(); //MUST BE CALLED from child
    }

    virtual vector<string> const & triggerEntries() const       {return m_TriggerEntries;}

protected:
    virtual void init()
    {
        //BaseMsg::init();
        m_SubClassChain.push_back("Message");
        m_TriggerEntries.push_back("Message");
    }

private:
    vector<string> m_TriggerEntries;
};

class Subscriber
{
public:
    virtual void newMessage(BaseMsg const & i_msg)
    {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};

class Subscriber2 : public Subscriber
{
public:
    virtual void newMessage(BaseMsg const & i_msg)
    {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};

class Subscriber1 : public Subscriber
{
public:
    virtual void newMessage(BaseMsg const & i_msg)
    {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};



class Sender
{
  //typdef vector<Receiver const & > Receivers;
public:

    void registerForMsg(Subscriber * someoneThatCares, BaseMsg const & msg)
    {
        vector<string> const & triggers = msg.triggerEntries();

        vector<string>::const_iterator it = triggers.begin();
        for(; it != triggers.end(); it++)
        {
            printf("Registration: %s\n", it->c_str());

            m_routingMap.insert(pair<string, Subscriber *>(*it, someoneThatCares));
        }
    }


  void send(BaseMsg const & msg)
  {
      vector<string> const & triggers = msg.registrationEntries();
      vector<string>::const_iterator it = triggers.begin();
      for(; it != triggers.end(); it++)
      {

          printf("Trigger: %s\n", it->c_str());
          pair<multimap<string, Subscriber *>::iterator, multimap<string, Subscriber *>::iterator> ret;

          //borrowed from: http://www.cplusplus.com/reference/stl/multimap/equal_range/
          ret = m_routingMap.equal_range(*it);

          multimap<string, Subscriber *>::iterator it1;
          for (it1 = ret.first; it1 != ret.second; ++it1)
          {

              it1->second->newMessage(msg);
          }
      }
  }

private:
  multimap<string, Subscriber *> m_routingMap;
};

int main(int argc, char *argv[])
{
    Sender sndr;

    BaseMsg baseMsg;
    Message message;

    printf("Base Register\n");
    Subscriber1 recv1;
    sndr.registerForMsg(&recv1, baseMsg);

    printf("Child Register\n");
    Subscriber2 recv2;
    sndr.registerForMsg(&recv2, message);


    printf("Base call\n");
    sndr.send(baseMsg); // I want only recv1 to receive this message

    printf("Der. call\n");
    sndr.send(message); // I want both Receivers to receive this message, but only recv2 will receive it

    return 0;
}
8_0_for_Desktop_-MinGW_Qt_SDK__Release\release\asdf.exe...
Базовый регистр
Регистрация: BaseMsg
Дочерний регистр
Регистрация: Сообщение
Базовый вызов
Триггер: BaseMsg
virtual void Subscriber1::newMessage(const BaseMsg&)
Der. call
Триггер: BaseMsg
virtual void Subscriber1::newMessage(const BaseMsg&)
Триггер: Message
virtual void Subscriber2::newMessage(const BaseMsg&)
C:\Users\David\Downloads \asdf-build-desktop-Qt
#include <string>
#include <vector>
#include <map>
#include <stdio.h>
using namespace std;

class BaseMsg
{
public:
    BaseMsg()
    {
        theRealInit();
    }

    //incase you don't want to go all the way down the rabbit hole.
    //At the bottom they are the same
    virtual vector<string> const & registrationEntries() const  {return m_SubClassChain;}
    virtual vector<string> const & triggerEntries() const       {return m_SubClassChain;}

    protected:
    virtual void init() { printf("Should NOT CALL THIS HERE!");}
    vector<string> m_SubClassChain;

private:

    void theRealInit()
    {
        m_SubClassChain.push_back("BaseMsg");
    }


};

class Message : public BaseMsg
{
    public:
    Message() : BaseMsg()
    {
        init(); //MUST BE CALLED from child
    }

    virtual vector<string> const & triggerEntries() const       {return m_TriggerEntries;}

protected:
    virtual void init()
    {
        //BaseMsg::init();
        m_SubClassChain.push_back("Message");
        m_TriggerEntries.push_back("Message");
    }

private:
    vector<string> m_TriggerEntries;
};

class Subscriber
{
public:
    virtual void newMessage(BaseMsg const & i_msg)
    {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};

class Subscriber2 : public Subscriber
{
public:
    virtual void newMessage(BaseMsg const & i_msg)
    {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};

class Subscriber1 : public Subscriber
{
public:
    virtual void newMessage(BaseMsg const & i_msg)
    {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};



class Sender
{
  //typdef vector<Receiver const & > Receivers;
public:

    void registerForMsg(Subscriber * someoneThatCares, BaseMsg const & msg)
    {
        vector<string> const & triggers = msg.triggerEntries();

        vector<string>::const_iterator it = triggers.begin();
        for(; it != triggers.end(); it++)
        {
            printf("Registration: %s\n", it->c_str());

            m_routingMap.insert(pair<string, Subscriber *>(*it, someoneThatCares));
        }
    }


  void send(BaseMsg const & msg)
  {
      vector<string> const & triggers = msg.registrationEntries();
      vector<string>::const_iterator it = triggers.begin();
      for(; it != triggers.end(); it++)
      {

          printf("Trigger: %s\n", it->c_str());
          pair<multimap<string, Subscriber *>::iterator, multimap<string, Subscriber *>::iterator> ret;

          //borrowed from: http://www.cplusplus.com/reference/stl/multimap/equal_range/
          ret = m_routingMap.equal_range(*it);

          multimap<string, Subscriber *>::iterator it1;
          for (it1 = ret.first; it1 != ret.second; ++it1)
          {

              it1->second->newMessage(msg);
          }
      }
  }

private:
  multimap<string, Subscriber *> m_routingMap;
};

int main(int argc, char *argv[])
{
    Sender sndr;

    BaseMsg baseMsg;
    Message message;

    printf("Base Register\n");
    Subscriber1 recv1;
    sndr.registerForMsg(&recv1, baseMsg);

    printf("Child Register\n");
    Subscriber2 recv2;
    sndr.registerForMsg(&recv2, message);


    printf("Base call\n");
    sndr.send(baseMsg); // I want only recv1 to receive this message

    printf("Der. call\n");
    sndr.send(message); // I want both Receivers to receive this message, but only recv2 will receive it

    return 0;
}
8_0_for_Desktop_-MinGW_Qt_SDK__Release\release\asdf.exe завершился с кодом 0

#include <string>
#include <vector>
#include <map>
#include <stdio.h>
using namespace std;

class BaseMsg
{
public:
    BaseMsg()
    {
        theRealInit();
    }

    //incase you don't want to go all the way down the rabbit hole.
    //At the bottom they are the same
    virtual vector<string> const & registrationEntries() const  {return m_SubClassChain;}
    virtual vector<string> const & triggerEntries() const       {return m_SubClassChain;}

    protected:
    virtual void init() { printf("Should NOT CALL THIS HERE!");}
    vector<string> m_SubClassChain;

private:

    void theRealInit()
    {
        m_SubClassChain.push_back("BaseMsg");
    }


};

class Message : public BaseMsg
{
    public:
    Message() : BaseMsg()
    {
        init(); //MUST BE CALLED from child
    }

    virtual vector<string> const & triggerEntries() const       {return m_TriggerEntries;}

protected:
    virtual void init()
    {
        //BaseMsg::init();
        m_SubClassChain.push_back("Message");
        m_TriggerEntries.push_back("Message");
    }

private:
    vector<string> m_TriggerEntries;
};

class Subscriber
{
public:
    virtual void newMessage(BaseMsg const & i_msg)
    {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};

class Subscriber2 : public Subscriber
{
public:
    virtual void newMessage(BaseMsg const & i_msg)
    {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};

class Subscriber1 : public Subscriber
{
public:
    virtual void newMessage(BaseMsg const & i_msg)
    {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};



class Sender
{
  //typdef vector<Receiver const & > Receivers;
public:

    void registerForMsg(Subscriber * someoneThatCares, BaseMsg const & msg)
    {
        vector<string> const & triggers = msg.triggerEntries();

        vector<string>::const_iterator it = triggers.begin();
        for(; it != triggers.end(); it++)
        {
            printf("Registration: %s\n", it->c_str());

            m_routingMap.insert(pair<string, Subscriber *>(*it, someoneThatCares));
        }
    }


  void send(BaseMsg const & msg)
  {
      vector<string> const & triggers = msg.registrationEntries();
      vector<string>::const_iterator it = triggers.begin();
      for(; it != triggers.end(); it++)
      {

          printf("Trigger: %s\n", it->c_str());
          pair<multimap<string, Subscriber *>::iterator, multimap<string, Subscriber *>::iterator> ret;

          //borrowed from: http://www.cplusplus.com/reference/stl/multimap/equal_range/
          ret = m_routingMap.equal_range(*it);

          multimap<string, Subscriber *>::iterator it1;
          for (it1 = ret.first; it1 != ret.second; ++it1)
          {

              it1->second->newMessage(msg);
          }
      }
  }

private:
  multimap<string, Subscriber *> m_routingMap;
};

int main(int argc, char *argv[])
{
    Sender sndr;

    BaseMsg baseMsg;
    Message message;

    printf("Base Register\n");
    Subscriber1 recv1;
    sndr.registerForMsg(&recv1, baseMsg);

    printf("Child Register\n");
    Subscriber2 recv2;
    sndr.registerForMsg(&recv2, message);


    printf("Base call\n");
    sndr.send(baseMsg); // I want only recv1 to receive this message

    printf("Der. call\n");
    sndr.send(message); // I want both Receivers to receive this message, but only recv2 will receive it

    return 0;
}
person David D    schedule 22.03.2012
comment
Мой пост о необходимой системе идентификации сообщений. - person Chris Morris; 23.03.2012
comment
Обратите внимание: этот код не компилировался, это только для примера. - person David D; 23.03.2012
comment
Это не решает конкретную проблему, которую я пытаюсь решить. Пожалуйста, обратитесь к моему main(), чтобы увидеть соответствующую проблему. - person Chris Morris; 23.03.2012
comment
Я думал предоставить информацию о предке через статические переменные, тогда как вы встроили ее в конструкцию самих объектов. Ваш подход требует создания экземпляра для регистрации. Мне нужно подумать, приемлемо ли это для моей программы. И есть ли причина, по которой вы не добавляете векторы m_subClassChain и m_triggerEntries в конструкторы? Это устранило бы возможность того, что производный класс мог забыть вызвать BaseClass::init(). - person Chris Morris; 23.03.2012
comment
Помещение триггеров в функцию инициализации вместо конструктора дает вам возможность НЕ вызывать родительскую функцию инициализации (что позволяет вам остановить цепочку, если вы хотите. Это может быть или не быть полезным, дизайн не действительно то, что я бы использовал, просто что-то, чтобы показать, что это можно сделать.Кроме того... У меня появилась идея после прочтения вашего комментария MetaTemplates можно использовать для этого во время выполнения. - person David D; 24.03.2012