Повторно использовать код, который должен выполняться на базовом типе объекта с шаблонами в перегруженной виртуальной функции для производных типов объектов - PullRequest
0 голосов
/ 09 апреля 2020

Контекст:

Я получил следующую иерархию структур:

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

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

Потенциально может быть много разных типов событий. Также есть компоненты heirarchy. Базовый класс 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 для каждой из этих виртуальных функций, которым просто не нужны ни конечные узлы, ни событие root. Как мне переписать template<typename GMouseEvent> void handleMouseEvent(const GMouseEvent& e) и template<typename GNonMouseEvent> void handleNonMouseEvent(const GMouseNonEvent& e)?

1 Ответ

0 голосов
/ 09 апреля 2020

Я нашел способ сделать то, что я хочу.

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;
    }

    ...
}
...