【问题标题】:How to find types of an object's ancestors?如何找到对象祖先的类型?
【发布时间】:2012-03-22 17:14:33
【问题描述】:

我正在创建一种机制,接收者可以通过该机制告诉发送者每个接收者都对某种类型的消息感兴趣。在下面我的示例实现中存在一个限制,即想要接收特定基本类型的所有消息的接收器只接收明确属于该类型的消息,而 不会接收派生的消息类型(例如参见 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)

【问题讨论】:

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


    【解决方案1】:

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

    这样你的发送者不知道你的接收者,你的观察者可以控制消息的分发。

    Sender -> 通知观察者有消息。

    observer -> 通知每个相关方有一个新的 msg。

    感兴趣的部分 -> 做有趣的事情。

    这将需要某种味精识别系统。也许所有的 msgs 都可以继承自一个有 type 成员和 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);
    };
    

    另一个更新:

    这实际上只是一个粗略的概念验证。一个人可以在不知道确切的祖先链的情况下做你想做的事。原谅 trigger 和 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&)
    德尔。打电话
    触发器:BaseMsg
    virtual void Subscriber1::newMessage(const BaseMsg&)
    触发器:消息
    virtual void 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;
    }
    

    【讨论】:

    • 我的帖子是关于需要的消息识别系统的。
    • 请注意:此代码未编译,仅供参考。
    • 这并没有解决我要解决的特定问题。请参考我的 main() 查看相关问题。
    • 我正在考虑通过静态变量提供祖先信息,而您将其构建到对象本身的构造中。您的方法需要创建一个实例才能注册。我需要考虑这对我的程序是否可以接受。您是否有理由不添加到构造函数中的 m_subClassChain 和 m_triggerEntries 向量?这将消除派生类忘记调用 BaseClass::init() 的可能性。
    • 将触发器放在初始化函数中,而不是构造函数中,您可以不调用父级初始化函数(如果您愿意,它允许您停止链。这可能会也可能不会很有用,这个设计并不是我真正会使用的东西,只是展示它可以完成的东西。另外......我在阅读您的评论后有了一个想法 MetaTemplates 可以用于在运行时执行此操作。跨度>
    猜你喜欢
    • 1970-01-01
    • 2021-09-05
    • 1970-01-01
    • 1970-01-01
    • 2020-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多