That's one cryptic title, but I couldn't think of a way to better describe my problem.
I'm writing up a small event system using std::function
and std::bind
and so far things are working pretty nicely. The problem I'm experiencing now, however, is that I'm trying to call a function with a parameter that is inherited of the type specified in the functor.
Below I'm trying to call the bound function using a parameter that is not of the type Event
as specified in the functor but of the type SocketConnectedEvent
. This results in the compiler throwing Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...)'
, and I assume I need to use templates.
However, I would like to be able to use the functor in an implementation-independent manner, where it is possible to use it with any class derived from Event
. I'd like to avoid having to hard-code all possible event types. I know I could do that using templates, but then I'd be restricted to as many values I'd be willing to template. The class binding the event functions should be able to bind as many different parameters with types derived from Event as it wants.
Is there any way I can achieve this?
I'm still pretty new to C++ so forgive me for any errors. Thanks!
EDIT:
Okay so here's my original code, with the irrelevant parts left out. The reason I did not post it was because it's a rather big project and I thought it might make the question too confusing. Even this code might not work because I had to abstract it. This is not to confuse anyone, but if I didn't I'd have to practically include the entire project. But anyway:
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;
}
}
}
I hope that cleared up the confusion.