Как найти типы предков объекта? - PullRequest
2 голосов
/ 22 марта 2012

Я создаю механизм, с помощью которого Получатели могут сообщать Отправителю, что каждый Получатель заинтересован в Сообщениях определенного типа. В моем примере реализации, приведенном ниже, существует ограничение, при котором получатель, который хочет получать все сообщения определенного базового типа, получает только сообщения явно этого типа, а не будет получать сообщения производного типа. (см. Например, 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)

1 Ответ

0 голосов
/ 22 марта 2012

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

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

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

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

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

Для этого потребуется какая-то система идентификации сообщений. Возможно, все msgs могут наследоваться от типа msg, который имеет член типа и член id. Таким образом, вы можете зарегистрироваться на 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);
};

Еще одно обновление:

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

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

Запуск C: \ Users \ David \ Downloads \ asdf-build-desktop-Qt_4_8_0_for_Desktop _- MinGW _Qt_SDK__Release \ release \ asdf.exe ...
Базовый регистр
Регистрация: BaseMsg
Детский регистр
Регистрация: сообщение
Базовый звонок
Триггер: BaseMsg
виртуальный пустота Subscriber1 :: newMessage (const BaseMsg &)
Производная звоните
Триггер: BaseMsg
виртуальный пустота Subscriber1 :: newMessage (const BaseMsg &)
Триггер: сообщение
виртуальный пустота Subscriber2 :: newMessage (const BaseMsg &)
C: \ Users \ David \ Downloads \ asdf-build-desktop-Qt_4_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;
}
...