在具有继承类型参数的事件系统中使用std :: bind?

时间:2015-10-27 12:48:39

标签: c++ events functor stdbind

这是一个神秘的标题,但我想不出更好地描述我的问题的方法。

我正在使用std::functionstd::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;
        }
    }
}

我希望能够消除这种困惑。

1 个答案:

答案 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_eventEvent的另一个衍生物的实例。调用handler似乎没问题,因为它是std::function<void(const Event &)>。但是调用将由onSpecializedEvent处理,期望const SpecializedEvent&。灾难!没有任何类型安全。

参数类型不需要完全相同。协变论证也是可能的。例如,如果onSpecializedEvent接受了const Event&,那么仿函数可以存储在std::function<void(const SpecializedEvent &)>中。这是因为可以处理任何const Event&的仿函数也可以处理它的衍生物。但是,当你正在尝试时,这根本无法正常工作。