这是一个神秘的标题,但我想不出更好地描述我的问题的方法。
我正在使用std::function
和std::bind
编写一个小型事件系统,到目前为止,事情进展得非常好。然而,我现在遇到的问题是,我正在尝试使用参数继承函数中指定的类型来调用函数。
下面我试图使用一个参数来调用绑定函数,该参数不是类型Event
,而是类型SocketConnectedEvent
。这导致编译器抛出Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...)'
,我假设我需要使用模板。
但是,我希望能够以独立于实现的方式使用仿函数,在这种情况下可以将它与从Event
派生的任何类一起使用。我想避免硬编码所有可能的事件类型。我知道我可以使用模板来做到这一点,但后来我会被限制为我愿意模板化的许多值。绑定事件函数的类应该能够根据需要使用从Event派生的类型绑定尽可能多的不同参数。
有什么方法可以实现这个目标吗?
我仍然是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
SystemHandler.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());
}
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
EventSource.cpp
#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));
}
}
}
}
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
Socket.cpp
#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));
}
}
}
Event.h
#ifndef QB_EVENTS_EVENT_H
#define QB_EVENTS_EVENT_H
namespace QB {
namespace Events {
class Event {
public:
Event();
};
}
}
#endif
Event.cpp
#include "Event.h"
namespace QB {
namespace Events {
Event::Event() {
}
}
}
SocketConnectedEvent.h
#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
SocketConnectedEvent.cpp
#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;
}
}
}
我希望能够消除这种困惑。
答案 0 :(得分:0)
我现在遇到的问题是,我试图使用参数继承函数中指定的类型来调用函数。
您的问题不在于使用派生类型的参数调用。这样做很好。
除了一些语法错误之外,您的问题是std::bind(&Test::onSpecializedEvent, this, std::placeholders::_1)
无法分配给std::function<void(const Event &)>
类型的变量,因为参数不匹配。绑定this
后,onSpecializedEvent
需要const SpecializedEvent &
,而可以存储在std::function<void(const Event &)>
中的仿函数必须能够接受任何 const Event &
}。
考虑如果你能做到这一点会发生什么,并调用handler(not_the_specialized_event);
,其中not_the_specialized_event
是Event
的另一个衍生物的实例。调用handler
似乎没问题,因为它是std::function<void(const Event &)>
。但是调用将由onSpecializedEvent
处理,期望const SpecializedEvent&
。灾难!没有任何类型安全。
参数类型不需要完全相同。协变论证也是可能的。例如,如果onSpecializedEvent
接受了const Event&
,那么仿函数可以存储在std::function<void(const SpecializedEvent &)>
中。这是因为可以处理任何const Event&
的仿函数也可以处理它的衍生物。但是,当你正在尝试时,这根本无法正常工作。