在派生对象类型的重载虚函数中使用模板重用基对象类型上应该 运行 的代码

Reuse code that should be run on base object type with templates in overloaded virtual function for derived object types

上下文:

我得到了结构的下一个层次:

Event -> MouseEvent    -> MouseButtonEvent
                       -> MouseWheelEvent
                       -> ...

      -> KeyboardEvent -> KeyEvent
                       -> InputEvent
                       -> ...
      -> ...

可能会有很多不同的事件类型。还有一个组件层次结构。基数 class 是 Component:

Component.h: 我想要的样子


class Component
{
    ...
    /* These could be overriden in derived to handle this events. */
    virtual void handleEvent(const MouseButtonEvent&);
    virtual void handleEvent(const MouseWheelEvent&);
    virtual void handleEvent(const KeyEvent&);
    virtual void handleEvent(const InputEvent&);
    ...

    ...
    template<typename GMouseEvent>
    void handleMouseEvent(const GMouseEvent& e)
    {
        if (!children.empty())
        {
            // Pay attention: I need to cast to real Event type to be able to use event properties here.
            const auto& eMouse = static_cast<const MouseEvent&>(e);

            auto eCopy = eMouse;

            for (auto child : children)
            {
                 // point is a property of MouseEvent
                 eCopy.point = child->transformPointToLocal(eMouse.point);
                 if (child->predicateOnMouseEventPropery(eMouse.point))
                      return child->handleEvent(static_cast<GMouseEvent&>(eCopy)); // Need to save GMouseEvent type to call proper virtual handler
            }
        }
    }

    template<typename GNonMouseEvent>
    void handleNonMouseEvent(const GNonMouseEvent& e)
    {
        for (auto child : children)
            child->handleEvent(e);   // Also need to preserve original call type to call virtual method
    }

    ...

    std::vector<Component*> children;
}


...


void Component::handleEvent(const MouseButtonEvent& e)
{
    handleMouseEvent<MouseButtonEvent>(e);
}

void Component::handleEvent(const MouseWheelEvent& e)
{
    handleMouseEvent<MouseWheelEvent>(e);
}

void Component::handleEvent(const KeyEvent& e)
{
    handleNonMouseEvent<KeyEvent>(e);
}

void Component::handleEvent(const InputEvent& e)
{
    handleNonMouseEvent<InputEvent>(e);
}

Component.h: 实际情况如何


class Component
{
    ...
    /* These could be overriden in derived to handle this events. */
    virtual void handleEvent(const MouseButtonEvent&);
    virtual void handleEvent(const MouseWheelEvent&);
    virtual void handleEvent(const KeyEvent&);
    virtual void handleEvent(const InputEvent&);
    ...

    std::vector<Component*> children;
}


...


void Component::handleEvent(const MouseButtonEvent& e)
{
    if (!children.empty())
    {
         auto eCopy = e;

         for (auto child : children)
         {
             // point is a property of MouseEvent that is the base of MouseButtonEvent
             eCopy.point = child->transformPointToLocal(e.point);
             if (child->predicateOnMouseEventPropery(e.point))
                 return child->handleEvent(eCopy);
        }
    }
}

void Component::handleEvent(const MouseWheelEvent& e)
{
    if (!children.empty())
    {
         auto eCopy = e;

         for (auto child : children)
         {
             // point is a property of MouseEvent that is the base of MouseWheelEvent
             eCopy.point = child->transformPointToLocal(e.point);
             if (child->predicateOnMouseEventPropery(e.point))
                 return child->handleEvent(eCopy);
        }
    }
}

void Component::handleEvent(const KeyEvent& e)
{
    for (auto child : children)
        child->handleEvent(e);
}

void Component::handleEvent(const InputEvent& e)
{
    for (auto child : children)
        child->handleEvent(e);
}

MyComponent.h:


class MyComponent : public Component
{
    virtual void handleEvent(const MouseButtonEvent& e);
    virtual void handleEvent(const KeyEvent& e);
    ...
}


void MyComponent::handleEvent(const MouseButtonEvent& e)
{
    // Calling for virtual void Component::handleEvent(const MouseButtonEvent&)
    Component::handleEvent(e); 


    ... // doing other stuff
}


void MyComponent::handleEvent(const KeyEvent& e)
{
     // Calling for virtual void Component::handleEvent(const KeyEvent&)
     Component::handleEvent(e);

     ... // doing stuff
}

Somewhere else:


...

Component* c = myComponentPointer;

MouseButtonEvent e = {};
e.point = {10, 20};
e.foo = "bar";

// Just call virtual overloaded function here to be as simple from the side as possible
// Calling here virtual void MyComponent::handleEvent(const MouseButtonEvent&);
c->handleEvent(e);

...

问题

当我很好地调用为事件层次结构的端节点实现的虚函数时,我想保存这种调用模式。但是我想为每个既不需要端节点也不需要根节点事件的虚拟函数在 Component::handleEvent 中放置一些代码。如何重写 template<typename GMouseEvent> void handleMouseEvent(const GMouseEvent& e)template<typename GNonMouseEvent> void handleNonMouseEvent(const GMouseNonEvent& e)

我找到了一种方法来做我想做的事。

Component.h:


class Component
{
    ...

    template<typename GMouseEvent>
    ShouldParentHandleEvent handleMouseEvent(const GMouseEvent& e)
    {
        if (!children.empty())
        {
            /* 
               There is a trick: I cast unknown const reference to const MouseEvent&
               to be able then read it as MouseEvent. I also need a copy of the original
               event. So I copy it and cast it to a reference of MouseEvent&.
            */
            const auto& eMouseEvent = static_cast<const MouseEvent&>(e);
            auto eCopy = e;

            auto& eMouse = static_cast<MouseEvent&>(eCopy);

            for (const auto& child : children)
            {
                /* 
                    There magic happens: eMouse is a reference to eCopy (original event).
                    We need it just only to write. Then we need to preserve event type
                    when we will call child->handleEvent(eCopy) with modified event of the
                    original type. So the right overloaded virtual function is called.
                */
                eMouse.point = child->transformToLocalPoint(eMouseEvent.point);
                if (child->predicateOnMouseEventProperty(eMouseEvent.point))
                {
                    return child->handleEvent(eCopy);
                }
            }
        }
        return true;
    }

    template<typename GNonMouseEvent>
    ShouldParentHandleEvent handleNonMouseEvent(const GNonMouseEvent& e)
    {
        bool shouldComponentHandleEvent = true;

        for (const auto& child : children)
            shouldComponentHandleEvent = child->handleEvent(e) && shouldComponentHandleEvent;

        return shouldComponentHandleEvent;
    }

    ...
}