/* * Copyright (C) 2023 Christopher J. Howard * * This file is part of Antkeeper source code. * * Antkeeper source code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper source code. If not, see . */ #ifndef ANTKEEPER_EVENT_SIGNAL_HPP #define ANTKEEPER_EVENT_SIGNAL_HPP #include #include #include #include #include #include //namespace event { template class signal; template class connector; /** * Manages a connection between a signal and handler. A signal will be disconnected from a handler when the connection is destructed or is disconnected manually via connection::disconnect(). */ class connection { public: /// Signal handler disconnect function type. typedef std::function)> disconnector_type; /** * Constructs a connection between a signal and a handler. * * @param handler Weak pointer to a signal handler. * @param disconnector Signal handler disconnect function. */ connection(std::weak_ptr handler, disconnector_type disconnector); /** * Destructs a connection between a signal and a handler. */ ~connection(); /** * Returns `true` if the signal and handler are connected, `false` otherwise. */ bool connected() const noexcept; /** * Disconnects the signal from the handler. */ void disconnect(); private: template friend class signal; std::weak_ptr handler; disconnector_type disconnector; }; /** * Creates connections between a signal and signal handlers. * * @tparam T Signal response type. * @tparam Args Signal argument types. */ template class connector { public: /// Signal response type. typedef T response_type; /// Signal type. typedef signal signal_type; /** * Constructs a signal connector. * * @param signal Signal to which handlers may be connected. */ connector(signal_type& signal): signal(&signal) {} /// @copydoc signal::connect(handler_type) std::shared_ptr connect(typename signal_type::handler_type handler) { return signal->connect(handler); } private: signal_type* signal; }; /** * Emits signals to signal handlers. * * @tparam T Signal response type. * @tparam Args Signal argument types. */ template class signal { public: /// Signal response type. typedef T response_type; /// Signal handler type. typedef std::function handler_type; /// Signal connector type. typedef connector connector_type; /** * Constructs a signal. */ signal(): signal_connector(*this) {} /** * Returns the connector for this signal. */ connector_type& connector() noexcept { return signal_connector; } /** * Connects the signal to a handler. * * @param handler Signal handler to connect. * * @return Connection between the signal and handler. */ std::shared_ptr connect(handler_type handler) { // Allocate shared pointer to handler std::shared_ptr shared_handler = std::make_shared(handler); // Add handler to list of connected handlers connections.push_back(shared_handler); // Return a shared pointer to the connection between the signal and handler return std::make_shared ( std::static_pointer_cast(shared_handler), [this](std::weak_ptr handler) { this->connections.remove(std::static_pointer_cast(handler.lock())); } ); } /** * Disconnects the signal from all connected handlers. */ void disconnect() { connections.clear(); } /** * Emits a signal to all connected handlers. * * @tparam ExecutionPolicy Execution policy type. * * @param policy Execution policy to use. * @param args Signal arguments. */ /// @{ template void emit(ExecutionPolicy&& policy, Args... args) const { std::for_each ( policy, std::begin(connections), std::end(connections), [&](const auto& handler) { (*handler)(args...); } ); } void emit(Args... args) const { emit(std::execution::seq, args...); } /// @} /** * Emits a signal to all connected handlers and relays their responses to a listener. * * @tparam ExecutionPolicy Execution policy type. * @tparam UnaryFunction Listener function object type. * * @param policy Execution policy to use. * @param listener Listener function object. * @param args Signal arguments. */ /// @{ template void ping(ExecutionPolicy&& policy, UnaryFunction listener, Args... args) const { std::for_each ( policy, std::begin(connections), std::end(connections), [&](const auto& handler) { if constexpr(std::is_void_v) { (*handler)(args...); listener(); } else { listener((*handler)(args...)); } } ); } template void ping(UnaryFunction listener, Args... args) const { ping(std::execution::seq, listener, args...); } /// @} private: /// List of connected signal handlers. std::list> connections; /// Signal connector. connector_type signal_connector; }; //} // namespace event #endif // ANTKEEPER_EVENT_SIGNAL_HPP