gpt4 book ai didi

c++ - std::tr1::function::target 和协变/逆变

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:13:33 24 4
gpt4 key购买 nike

因为我喜欢用 C# 和 C++ 编程,所以我打算实现一个类似 C# 的事件系统,作为我计划的 C++ SFML-GUI 的坚实基础。

这只是我的代码的摘录,我希望这能澄清我的概念:

// Event.h
// STL headers:
#include <functional>
#include <type_traits>
#include <iostream>
// boost headers:
#include <boost/signals/trackable.hpp>
#include <boost/signal.hpp>

namespace Utils
{
namespace Gui
{
#define IMPLEMENTS_EVENT(EVENTNAME, EVENTARGS) public: \
Utils::Gui::IEvent<EVENTARGS>& EVENTNAME() { return m_on##EVENTNAME; } \
protected: \
virtual void On##EVENTNAME(EVENTARGS& e) { m_on##EVENTNAME(this, e); } \
private: \
Utils::Gui::Event<EVENTARGS> m_on##EVENTNAME;


#define MAKE_EVENTFIRING_CLASS(EVENTNAME, EVENTARGS) class Fires##EVENTNAME##Event \
{ \
IMPLEMENTS_EVENT(EVENTNAME, EVENTARGS); \
};


class EventArgs
{
public:
static EventArgs Empty;
};

EventArgs EventArgs::Empty = EventArgs();

template<class TEventArgs>
class EventHandler : public std::function<void (void*, TEventArgs&)>
{
static_assert(std::is_base_of<EventArgs, TEventArgs>::value,
"EventHandler must be instantiated with a TEventArgs template paramater type deriving from EventArgs.");
public:
typedef void Signature(void*, TEventArgs&);
typedef void (*HandlerPtr)(void*, TEventArgs&);

EventHandler() : std::function<Signature>() { }

template<class TContravariantEventArgs>
EventHandler(const EventHandler<TContravariantEventArgs>& rhs)
: std::function<Signature>(reinterpret_cast<HandlerPtr>(*rhs.target<EventHandler<TContravariantEventArgs>::HandlerPtr>()))
{
static_assert(std::is_base_of<TContravariantEventArgs, TEventArgs>::value,
"The eventHandler instance to copy does not suffice the rules of contravariance.");
}

template<class F>
EventHandler(F f) : std::function<Signature>(f) { }

template<class F, class Allocator>
EventHandler(F f, Allocator alloc) : std::function<Signature>(f, alloc) { }
};

template<class TEventArgs>
class IEvent
{
public:
typedef boost::signal<void (void*, TEventArgs&)> SignalType;

void operator+= (const EventHandler<TEventArgs>& eventHandler)
{
Subscribe(eventHandler);
}

void operator-= (const EventHandler<TEventArgs>& eventHandler)
{
Unsubscribe(eventHandler);
}

virtual void Subscribe(const EventHandler<TEventArgs>& eventHandler) = 0;

virtual void Subscribe(const EventHandler<TEventArgs>& eventHandler, int group) = 0;

virtual void Unsubscribe(const EventHandler<TEventArgs>& eventHandler) = 0;
};

template<class TEventArgs>
class Event : public IEvent<TEventArgs>
{
public:
virtual void Subscribe(const EventHandler<TEventArgs>& eventHandler)
{
m_signal.connect(*eventHandler.target<EventHandler<TEventArgs>::HandlerPtr>());
}

virtual void Subscribe(const EventHandler<TEventArgs>& eventHandler, int group)
{
m_signal.connect(group, *eventHandler.target<EventHandler<TEventArgs>::HandlerPtr>());
}

virtual void Unsubscribe(const EventHandler<TEventArgs>& eventHandler)
{
m_signal.disconnect(*eventHandler.target<EventHandler<TEventArgs>::HandlerPtr>());
}

void operator() (void* sender, TEventArgs& e)
{
m_signal(sender, e);
}

private:
SignalType m_signal;
};

class IEventListener : public boost::signals::trackable
{
};
};
};

如您所见,我使用 boost::signal 作为我的实际事件系统,但我用 IEvent 接口(interface)(它实际上是一个抽象类)封装它以防止事件监听器通过 operator() 触发事件.

为了方便,我重载了加赋值和减赋值运算符。如果我现在确实从 IEventListener 派生我的事件监听类,我就能够编写代码而无需担心信号中的悬挂函数指针。

到目前为止,我正在测试我的结果,但我遇到了 std::tr1::function::target<TFuncPtr>() 的问题:

class BaseEventArgs : public Utils::Gui::EventArgs
{
};

class DerivedEventArgs : public BaseEventArgs
{
};

void Event_BaseEventRaised(void* sender, BaseEventArgs& e)
{
std::cout << "Event_BaseEventRaised called";
}

void Event_DerivedEventRaised(void* sender, DerivedEventArgs& e)
{
std::cout << "Event_DerivedEventRaised called";
}

int main()
{
using namespace Utils::Gui;
typedef EventHandler<BaseEventArgs>::HandlerPtr pfnBaseEventHandler;
typedef EventHandler<DerivedEventArgs>::HandlerPtr pfnNewEventHandler;

// BaseEventHandler with a function taking a BaseEventArgs
EventHandler<BaseEventArgs> baseEventHandler(Event_BaseEventRaised);
// DerivedEventHandler with a function taking a DerivedEventArgs
EventHandler<DerivedEventArgs> newEventHandler(Event_DerivedEventRaised);
// DerivedEventHandler with a function taking a BaseEventArgs -> Covariance
EventHandler<DerivedEventArgs> covariantBaseEventHandler(Event_BaseEventRaised);

const pfnBaseEventHandler* pBaseFunc = baseEventHandler.target<pfnBaseEventHandler>();
std::cout << "baseEventHandler function pointer is " << ((pBaseFunc != nullptr) ? "valid" : "invalid") << std::endl;

const pfnNewEventHandler* pNewFunc = newEventHandler.target<pfnNewEventHandler>();
std::cout << "baseEventHandler function pointer is " << ((pNewFunc != nullptr) ? "valid" : "invalid") << std::endl;

// Here is the error, covariantBaseEventHandler actually stores a pfnBaseEventHandler:
pNewFunc = covariantBaseEventHandler.target<pfnNewEventHandler>();
std::cout << "covariantBaseEventHandler as pfnNewEventHandler function pointer is " << ((pNewFunc != nullptr) ? "valid" : "invalid") << std::endl;

// This works as expected, but template forces compile-time knowledge of the function pointer type
pBaseFunc = covariantBaseEventHandler.target<pfnBaseEventHandler>();
std::cout << "covariantBaseEventHandler as pfnBaseEventHandler function pointer is " << ((pBaseFunc != nullptr) ? "valid" : "invalid") << std::endl;

return EXIT_SUCCESS;
}

EventHandler<TEventArgs>::target<TFuncPtr>()如果 TFuncPtr 与 Functor 中存储的类型完全相同,则方法将仅返回一个有效指针,而不管协方差如何。由于 RTTI 检查,它禁止将指针作为标准弱类型 C 函数指针访问,这在这种情况下有点烦人。

EventHandler 是 DerivedEventArgs 类型,但仍然指向 pfnBaseEventHandler 函数,即使该函数通过构造函数运行。

这意味着,std::tr1::function 本身“支持”逆变,但如果我不这样做的话,我无法找到一种简单地从 std::tr1::funcion 对象中获取函数指针的方法在编译时知道它的类型,这是模板参数所必需的。

在这种情况下,我很感激他们添加了一个简单的 get() 方法,就像他们为 RAII 指针类型所做的那样。

由于我是编程新手,我想知道是否有办法解决这个问题,最好是在编译时通过模板(我认为这是唯一的方法)。

最佳答案

刚刚找到了问题的解决方案。看来我只是错过了在不同位置的类型转换:

template<class TEventArgs>
class EventHandler : public std::function<void (void*, TEventArgs&)>
{
public:
typedef void Signature(void*, TEventArgs&);
typedef void (*HandlerPtr)(void*, TEventArgs&);

// ...

template<class TContravariantEventArgs>
EventHandler(const EventHandler<TContravariantEventArgs>& rhs)
: std::function<Signature>(reinterpret_cast<HandlerPtr>(*rhs.target<EventHandler<TContravariantEventArgs>::HandlerPtr>()))
{
static_assert(std::is_base_of<TContravariantEventArgs, TEventArgs>::value,
"The eventHandler instance to copy does not suffice the rules of contravariance.");
}

// ...
}

这是按预期方式工作的。尽管如此,还是感谢您顺利地向我介绍了这个非常棒的社区!

关于c++ - std::tr1::function::target<TFuncPtr> 和协变/逆变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3227926/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com