如何找到对象祖先的类型?

时间:2012-03-22 17:14:34

标签: c++ inheritance rtti base-class ancestor

我正在创建一种机制,通过该机制,Receiver可以告诉发件人每个Receiver都对某种类型的消息感兴趣。通过下面的示例实现,存在一个限制,即希望接收所有某个基本类型的消息的Receiver只接收明确属于该类型的消息,而将不会接收派生的消息输入(例如参见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 :(得分:0)

或许考虑使用观察者模式(http://en.wikipedia.org/wiki/Observer_pattern)。

这样,您的发送者不了解您的接收者,您的观察者可以控制消息的分布。

发件人 - &gt;通知观察者有一条消息。

观察者 - &gt;通知每个感兴趣的一方有一个新的消息。

感兴趣的部分 - &gt;做有趣的东西。

这将需要某种msg识别系统。也许所有msgs都可以从具有类型成员和id成员的msg类型继承。这样你就可以使用它们注册msgs。

更新:

快速消息结构:

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;

};

观察者应该有一个方法来注册msgs:

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

另一个更新:

这实际上只是一个粗略的概念验证。在不知道确切的祖先链的情况下,你可以做你想做的事。原谅触发器和registrationEntry函数的切换(我最初做错了,这是最简单的修正,再次证明了概念)。这个草图的另一个缺点是至少需要构造一个msg来注册。如果您正在寻找一个真正的长期解决方案,我建议您找到一个已经有反射的库或框架(例如QT具有元对象),这些可用于查看超类。或者,您可以使用那里的信号/插槽。

以下代码的输出:

启动C:\ Users \ David \ Downloads \ asdf-build-desktop-Qt_4_8_0_for_Desktop _- MinGW _Qt_SDK__Release \ release \ asdf.exe ...
基地登记册
注册:BaseMsg
儿童登记册 注册:留言
基本呼叫
触发:BaseMsg
virtual void Subscriber1 :: newMessage(const BaseMsg&amp;)
明镜。调用
触发:BaseMsg
virtual void Subscriber1 :: newMessage(const BaseMsg&amp;)
触发:消息
virtual void Subscriber2 :: newMessage(const BaseMsg&amp;)
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;
}