C++ 在具有继承类型参数的事件系统中使用std::bind?
这是一个隐晦的标题,但我想不出更好的方式来描述我的问题 我正在使用C++ 在具有继承类型参数的事件系统中使用std::bind?,c++,events,functor,stdbind,C++,Events,Functor,Stdbind,这是一个隐晦的标题,但我想不出更好的方式来描述我的问题 我正在使用std::function和std::bind编写一个小事件系统,到目前为止,一切都很顺利。然而,我现在遇到的问题是,我试图调用一个带有继承了函子中指定类型的参数的函数 下面,我尝试使用一个参数调用绑定函数,该参数的类型不是functor中指定的Event,而是SocketConnectedEvent。这导致编译器抛出无法专门化函数模板“未知类型std::invoke(_Callable&&,_Types&&&…),我假设我需要使
std::function
和std::bind
编写一个小事件系统,到目前为止,一切都很顺利。然而,我现在遇到的问题是,我试图调用一个带有继承了函子中指定类型的参数的函数
下面,我尝试使用一个参数调用绑定函数,该参数的类型不是functor中指定的Event
,而是SocketConnectedEvent
。这导致编译器抛出无法专门化函数模板“未知类型std::invoke(_Callable&&,_Types&&&…)
,我假设我需要使用模板。
但是,我希望能够以独立于实现的方式使用functor,可以将其用于从事件
派生的任何类。我希望避免硬编码所有可能的事件类型。我知道我可以使用模板来实现这一点,但我会被限制为尽可能多的值。绑定事件函数的类应该能够用从事件派生的类型绑定任意多个不同的参数
我有什么办法可以做到这一点吗
对于C++,我还是很新的,所以请原谅我的错误。
谢谢
编辑:
好的,这是我的原始代码,去掉了不相关的部分。我之所以没有发表这篇文章,是因为这是一个相当大的项目,我认为这可能会让问题变得太复杂。即使是这段代码也可能无法工作,因为我必须对其进行抽象。这并不是要迷惑任何人,但如果我不这样做,我就必须实际包括整个项目。但无论如何:
SystemHandler.h
#ifndef SYSTEMHANDLER_H
#define SYSTEMHANDLER_H
#include <QB/Console/Console.h>
#include <QB/Network/Socket.h>
#include <QB/Network/SocketConnectedEvent.h>
class SystemHandler {
public:
SystemHandler();
void onSocketConnected(const QB::Network::SocketConnectedEvent &);
};
#endif
EventSource.h
#ifndef QB_EVENTS_EVENTSOURCE_H
#define QB_EVENTS_EVENTSOURCE_H
#include <functional>
#include <map>
#include <vector.h>
#include <string>
#include "Event.h"
namespace QB {
namespace Events {
class Event;
class EventSource {
public:
typedef std::function<void(const Event &)> Handler;
EventSource();
void dispatch(const String &, const Event &);
void subscribe(const String &, const Handler &);
private:
std::map<String, std::vector<Handler>> subscribers;
};
}
}
#endif
Socket.h
#ifndef QB_NETWORK_SOCKET_H
#define QB_NETWORK_SOCKET_H
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <ws2tcpip.h>
#include <string>
#include "../Events/EventSource.h"
#include "Network.h"
#include "SocketConnectedEvent.h"
namespace QB {
namespace Events {
class EventSource;
}
namespace Network {
class SocketConnectedEvent;
class Socket : public Events::EventSource {
public:
Socket(const AddressFamily &, const SocketType &, const IPPROTO &);
void connect(const std::string &, const int &);
private:
SOCKET value;
};
}
}
#endif
#ifndef QB_EVENTS_EVENT_H
#define QB_EVENTS_EVENT_H
namespace QB {
namespace Events {
class Event {
public:
Event();
};
}
}
#endif
#ifndef QB_EVENTS_SOCKETCONNECTEDEVENT_H
#define QB_EVENTS_SOCKETCONNECTEDEVENT_H
#include <string>
#include "../Events/Event.h"
namespace QB {
namespace Events {
class Event;
}
namespace Network {
class SocketConnectedEvent : public Events::Event {
public:
SocketConnectedEvent(const std::string & host, const int & port);
const std::string getHost() const;
const int getPort() const;
private:
std::string host;
int port;
};
}
}
#endif
Event.h
#ifndef QB_NETWORK_SOCKET_H
#define QB_NETWORK_SOCKET_H
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <ws2tcpip.h>
#include <string>
#include "../Events/EventSource.h"
#include "Network.h"
#include "SocketConnectedEvent.h"
namespace QB {
namespace Events {
class EventSource;
}
namespace Network {
class SocketConnectedEvent;
class Socket : public Events::EventSource {
public:
Socket(const AddressFamily &, const SocketType &, const IPPROTO &);
void connect(const std::string &, const int &);
private:
SOCKET value;
};
}
}
#endif
#ifndef QB_EVENTS_EVENT_H
#define QB_EVENTS_EVENT_H
namespace QB {
namespace Events {
class Event {
public:
Event();
};
}
}
#endif
#ifndef QB_EVENTS_SOCKETCONNECTEDEVENT_H
#define QB_EVENTS_SOCKETCONNECTEDEVENT_H
#include <string>
#include "../Events/Event.h"
namespace QB {
namespace Events {
class Event;
}
namespace Network {
class SocketConnectedEvent : public Events::Event {
public:
SocketConnectedEvent(const std::string & host, const int & port);
const std::string getHost() const;
const int getPort() const;
private:
std::string host;
int port;
};
}
}
#endif
Event.cpp
#include "SystemHandler.h"
SystemHandler::SystemHandler() {
QB::Network::Socket socket = QB::Network::Socket(QB::Network::AddressFamily_InterNetwork, QB::Network::SocketType_Stream, IPPROTO_TCP);
socket.subscribe("SocketConnected", std::bind(&SystemHandler::onSocketConnected, this, std::placeholders::_1)); // This is where things go wrong
socket.connect("www.google.com", 80);
QB::Console::readLine();
}
void SystemHandler::onSocketConnected(const QB::Network::SocketConnectedEvent & event) {
QB::Console::writeLine("SUCCESS!");
QB::Console::writeLine(event.getHost());
}
#include "EventSource.h"
namespace QB {
namespace Events {
EventSource::EventSource() {
}
void EventSource::dispatch(const std::string & event, const Event & data) {
// Check if there are subscribers
if (subscribers.find(event) != subscribers.end()) {
// Get handlers
std::vector<Handler> handlers = subscribers[event];
// Loop event handlers and notify them
for (Handler handler : handlers) {
handler(data);
}
}
}
void EventSource::subscribe(const std::string & event, const Handler & handler) {
if (subscribers.find(event) != subscribers.end()) {
subscribers[event].add(handler);
} else {
std::vector<Handler> handlers = std::vector<Handler>();
handlers.push_back(handler);
subscribers.insert(std::make_pair(event, handlers));
}
}
}
}
#include "Socket.h"
namespace QB {
namespace Network {
Socket::Socket(const AddressFamily & addressFamily, const SocketType & socketType, const IPPROTO & protocolType) {
// Socket initialization code
}
void Socket::connect(const std::string & host, const int & port) {
// Irrelevant socket connect code
dispatch("SocketConnected", SocketConnectedEvent(host, port));
}
}
}
#include "Event.h"
namespace QB {
namespace Events {
Event::Event() {
}
}
}
#include "SocketConnectedEvent.h"
namespace QB {
namespace Network {
SocketConnectedEvent::SocketConnectedEvent(const std::string & host, const int & port) {
this->host = host;
this->port = port;
}
const std::string SocketConnectedEvent::getHost() const {
return host;
}
const int SocketConnectedEvent::getPort() const {
return port;
}
}
}
SocketConnectedEvent.h
#ifndef QB_NETWORK_SOCKET_H
#define QB_NETWORK_SOCKET_H
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <ws2tcpip.h>
#include <string>
#include "../Events/EventSource.h"
#include "Network.h"
#include "SocketConnectedEvent.h"
namespace QB {
namespace Events {
class EventSource;
}
namespace Network {
class SocketConnectedEvent;
class Socket : public Events::EventSource {
public:
Socket(const AddressFamily &, const SocketType &, const IPPROTO &);
void connect(const std::string &, const int &);
private:
SOCKET value;
};
}
}
#endif
#ifndef QB_EVENTS_EVENT_H
#define QB_EVENTS_EVENT_H
namespace QB {
namespace Events {
class Event {
public:
Event();
};
}
}
#endif
#ifndef QB_EVENTS_SOCKETCONNECTEDEVENT_H
#define QB_EVENTS_SOCKETCONNECTEDEVENT_H
#include <string>
#include "../Events/Event.h"
namespace QB {
namespace Events {
class Event;
}
namespace Network {
class SocketConnectedEvent : public Events::Event {
public:
SocketConnectedEvent(const std::string & host, const int & port);
const std::string getHost() const;
const int getPort() const;
private:
std::string host;
int port;
};
}
}
#endif
我希望这消除了混乱
然而,我现在遇到的问题是,我试图调用一个带有继承了函子中指定类型的参数的函数
问题不在于使用派生类型的参数调用。这样做没关系
除了一些语法错误之外,您的问题是,std::bind(&Test::onSpecializedEvent,this,std::placeholders::_1)
无法分配给std::function
类型的变量,因为参数不匹配。绑定this
后,SpecializedEvent上的需要一个常量SpecializedEvent&
,而可以存储在std::function
中的functor必须能够接受任何常量事件&
考虑一下如果您可以这样做并调用处理程序(而不是专门的事件)会发生什么
其中not\u事件
是事件
的另一个派生实例。调用handler
似乎可以,因为它是std::function
。但是调用将由onSpecializedEvent
处理,它需要一个常量SpecializedEvent&
。一场灾难!不会有任何类型的安全
参数类型不需要完全相同。协变参数也是可能的。例如,如果onSpecializedEvent
接受const事件&
,则该函子可以存储在std::function
中。这是因为可以处理任何常量事件&
的函子也可以处理它的导数。但是,在您尝试时,这根本无法反过来工作。不要在这里发布您头顶上的代码。发布您(试图)编译并观察到重现问题的代码。这些代码显然是不可编译的,我想知道真正的代码是什么。如果我的想法是正确的,这应该是双重分派的范围。这不是马蹄铁。你的问题是你的代码不起作用;发布因您的问题以外的原因而不起作用的代码需要我们了解您的想法,找出哪些错误是真实的,哪些错误是不相关的。通常,如果您从公共基派生一系列类,您可以通过虚拟函数访问它们的功能(并通过引用传递这些对象)。我已经添加了原始代码。“这样做很好。”如果您对对象切片没有意见,也就是说。@n.m.当函子引用对象时,将不会进行切片。好的,谢谢您解释为什么它不起作用,但是在保持代码灵活性的同时,有什么方法可以实现我想要做的吗?(我已将原始代码添加到原始问题)