在派生对象类型的重载虚函数中使用模板重用基对象类型上应该 运行 的代码
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;
}
...
}
上下文:
我得到了结构的下一个层次:
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;
}
...
}