diff --git a/libaegisub/include/libaegisub/signal.h b/libaegisub/include/libaegisub/signal.h index 3dd7972a1..6ef497b48 100644 --- a/libaegisub/include/libaegisub/signal.h +++ b/libaegisub/include/libaegisub/signal.h @@ -23,11 +23,7 @@ #include #include -namespace agi { - namespace signal { - -using namespace std::placeholders; - +namespace agi { namespace signal { class Connection; /// Implementation details; nothing outside this file should directly touch @@ -39,18 +35,17 @@ namespace detail { friend class SignalBase; SignalBase *signal; - bool blocked; - bool claimed; + bool blocked = false; + bool claimed = false; - ConnectionToken(SignalBase *signal) : signal(signal), blocked(false), claimed(false) { } + ConnectionToken(SignalBase *signal) : signal(signal) { } inline void Disconnect(); public: ~ConnectionToken() { Disconnect(); } }; } -/// @class Connection -/// @brief Object representing a connection to a signal +/// Object representing a connection to a signal class Connection { std::unique_ptr token; public: @@ -73,8 +68,8 @@ public: /// @brief Reenable this connection after it was disabled by Block void Unblock() { if (token) token->blocked = false; } }; -/// @class UnscopedConnection -/// @brief A connection which is not automatically closed + +/// A connection which is not automatically closed /// /// Connections initially start out owned by the signal. If a slot knows that it /// will outlive a signal and does not need to be able to block a connection, it @@ -92,7 +87,7 @@ public: }; namespace detail { - /// @brief Polymorphic base class for slots + /// Polymorphic base class for slots /// /// This class has two purposes: to avoid having to make Connection /// templated on what type of connection it is controlling, and to avoid @@ -104,10 +99,10 @@ namespace detail { virtual void Disconnect(ConnectionToken *tok)=0; /// Signals can't be copied - SignalBase(SignalBase const&); - SignalBase& operator=(SignalBase const&); + SignalBase(SignalBase const&) = delete; + SignalBase& operator=(SignalBase const&) = delete; protected: - SignalBase() { } + SignalBase() = default; /// @brief Notify a slot that it has been disconnected /// @param tok Token to disconnect /// @@ -130,158 +125,80 @@ namespace detail { if (signal) signal->Disconnect(this); signal = nullptr; } - - /// @brief Templated common code for signals - template - class SignalBaseImpl : public SignalBase { - protected: - typedef boost::container::map SlotMap; - - SlotMap slots; /// Signals currently connected to this slot - - void Disconnect(ConnectionToken *tok) override { - slots.erase(tok); - } - - /// Protected destructor so that we don't need a virtual destructor - ~SignalBaseImpl() { - for (auto& slot : slots) { - DisconnectToken(slot.first); - if (!TokenClaimed(slot.first)) delete slot.first; - } - } - public: - /// @brief Connect a signal to this slot - /// @param sig Signal to connect - /// @return The connection object - UnscopedConnection Connect(Slot sig) { - ConnectionToken *token = MakeToken(); - slots.insert(std::make_pair(token, sig)); - return UnscopedConnection(token); - } - template - UnscopedConnection Connect(F func, Arg1 a1) { - return Connect(std::bind(func, a1)); - } - template - UnscopedConnection Connect(F func, Arg1 a1, Arg2 a2) { - return Connect(std::bind(func, a1, a2)); - } - template - UnscopedConnection Connect(F func, Arg1 a1, Arg2 a2, Arg3 a3) { - return Connect(std::bind(func, a1, a2, a3)); - } - }; } -#define SIGNALS_H_FOR_EACH_SIGNAL(...) \ - for (auto cur = slots.begin(); cur != slots.end();) { \ - if (Blocked(cur->first)) \ - ++cur; \ - else \ - (cur++)->second(__VA_ARGS__); \ +template +class Signal final : private detail::SignalBase { + using Slot = std::function; + boost::container::map slots; /// Signals currently connected to this slot + + void Disconnect(detail::ConnectionToken *tok) override { + slots.erase(tok); } -/// @class Signal -/// @brief Two-argument signal -/// @param Arg1 Type of first argument to pass to slots -/// @param Arg2 Type of second argument to pass to slots -template -class Signal final : public detail::SignalBaseImpl > { - typedef detail::SignalBaseImpl > super; - using super::Blocked; - using super::slots; -public: - Signal() { } + UnscopedConnection DoConnect(Slot sig) { + auto token = MakeToken(); + slots.insert(std::make_pair(token, sig)); + return UnscopedConnection(token); + } - /// @brief Trigger this signal - /// @param a1 First argument to the signal - /// @param a2 Second argument to the signal +public: + ~Signal() { + for (auto& slot : slots) { + DisconnectToken(slot.first); + if (!TokenClaimed(slot.first)) delete slot.first; + } + } + + /// Trigger this signal /// /// The order in which connected slots are called is undefined and should /// not be relied on - void operator()(Arg1 a1, Arg2 a2) { SIGNALS_H_FOR_EACH_SIGNAL(a1, a2) } - - // Don't hide the base overloads - using super::Connect; - - /// @brief Connect a member function with the correct signature to this signal - /// @param func Function to connect - /// @param a1 Object - /// - /// This overload is purely for convenience so that classes can do - /// sig.Connect(&Class::Foo, this) rather than - /// sig.Connect(&Class::Foo, this, _1, _2) - template - UnscopedConnection Connect(void (T::*func)(Arg1, Arg2), T* a1) { - return Connect(std::bind(func, a1, _1, _2)); + void operator()(Args... args) { + for (auto cur = slots.begin(); cur != slots.end(); ) { + if (Blocked(cur->first)) + ++cur; + else + (cur++)->second(args...); + } } - /// @brief Connect a member function with the correct signature to this signal - /// @param func Function to connect - /// @param a1 Object - /// - /// This overload is purely for convenience so that classes can do - /// sig.Connect(&Class::Foo, this) rather than - /// sig.Connect(&Class::Foo, this, _1) - template + /// @brief Connect a signal to this slot + /// @param sig Signal to connect + /// @return The connection object + UnscopedConnection Connect(Slot sig) { + return DoConnect(sig); + } + + // Convenience wrapper for a member function which matches the signal's signature + template + UnscopedConnection Connect(void (T::*func)(Args...), T* a1) { + return DoConnect([=](Args... args) { (a1->*func)(args...); }); + } + + // Convenience wrapper for a callable which does not use any signal args + template + UnscopedConnection Connect(Thunk&& func) { + return DoConnect([=](Args... args) mutable { func(); }); + } + + // Convenience wrapper for a member function which does not use any signal + // args. The match is overly-broad to avoid having two methods with the + // same signature when the signal has no args. + template + UnscopedConnection Connect(MemberThunk func, T* obj) { + return DoConnect([=](Args... args) { (obj->*func)(); }); + } + + // Convenience wrapper for a member function which uses only the first + // signal arg. + template UnscopedConnection Connect(void (T::*func)(Arg1), T* a1) { - return Connect(std::bind(func, a1, _1)); + return DoConnect(std::bind(func, a1, std::placeholders::_1)); } }; -/// @class Signal -/// @brief One-argument signal -/// @param Arg1 Type of the argument to pass to slots -template -class Signal : public detail::SignalBaseImpl > { - typedef detail::SignalBaseImpl > super; - using super::Blocked; - using super::slots; -public: - Signal() { } - - /// @brief Trigger this signal - /// @param a1 The argument to the signal - /// - /// The order in which connected slots are called is undefined and should - /// not be relied on - void operator()(Arg1 a1) { SIGNALS_H_FOR_EACH_SIGNAL(a1) } - - // Don't hide the base overloads - using super::Connect; - - /// @brief Connect a member function with the correct signature to this signal - /// @param func Function to connect - /// @param a1 Object - /// - /// This overload is purely for convenience so that classes can do - /// sig.Connect(&Class::Foo, this) rather than sig.Connect(&Class::Foo, this, _1) - template - UnscopedConnection Connect(void (T::*func)(Arg1), T* a1) { - return Connect(std::bind(func, a1, _1)); - } -}; - -/// @class Signal -/// @brief Zero-argument signal -template<> -class Signal final : public detail::SignalBaseImpl > { - typedef detail::SignalBaseImpl > super; - using super::Blocked; - using super::slots; -public: - Signal() { } - /// @brief Trigger this signal - /// - /// The order in which connected slots are called is undefined and should - /// not be relied on - void operator()() { SIGNALS_H_FOR_EACH_SIGNAL() } -}; - -#undef SIGNALS_H_FOR_EACH_SIGNAL - } -} +} } /// @brief Define functions which forward their arguments to the connect method /// of the named signal @@ -295,7 +212,7 @@ public: /// /// Defines AddSignalNameListener #define DEFINE_SIGNAL_ADDERS(sig, method) \ - template agi::signal::UnscopedConnection method(A a) { return sig.Connect(a); } \ - template agi::signal::UnscopedConnection method(A a,B b) { return sig.Connect(a,b); } \ - template agi::signal::UnscopedConnection method(A a,B b,C c) { return sig.Connect(a,b,c); } \ - template agi::signal::UnscopedConnection method(A a,B b,C c,D d) { return sig.Connect(a,b,c,d); } + template \ + agi::signal::UnscopedConnection method(Args&&... args) { \ + return sig.Connect(std::forward(args)...); \ + } diff --git a/src/audio_display.cpp b/src/audio_display.cpp index 4f654ba97..7d6ec16d6 100644 --- a/src/audio_display.cpp +++ b/src/audio_display.cpp @@ -1159,7 +1159,7 @@ void AudioDisplay::OnAudioOpen(AudioProvider *provider) { if (connections.empty()) { - connections.push_back(controller->AddAudioCloseListener(&AudioDisplay::OnAudioOpen, this, nullptr)); + connections.push_back(controller->AddAudioCloseListener([=] { OnAudioOpen(nullptr); })); connections.push_back(controller->AddPlaybackPositionListener(&AudioDisplay::OnPlaybackPosition, this)); connections.push_back(controller->AddPlaybackStopListener(&AudioDisplay::RemoveTrackCursor, this)); connections.push_back(controller->AddTimingControllerListener(&AudioDisplay::OnTimingController, this)); diff --git a/src/frame_main.cpp b/src/frame_main.cpp index a7583cef5..057529616 100644 --- a/src/frame_main.cpp +++ b/src/frame_main.cpp @@ -224,7 +224,7 @@ FrameMain::FrameMain() StartupLog("Create views and inner main window controls"); InitContents(); - OPT_SUB("Video/Detached/Enabled", &FrameMain::OnVideoDetach, this, agi::signal::_1); + OPT_SUB("Video/Detached/Enabled", &FrameMain::OnVideoDetach, this); StartupLog("Set up drag/drop target"); SetDropTarget(new AegisubFileDropTarget(this)); diff --git a/src/subs_controller.cpp b/src/subs_controller.cpp index df261045b..0b81147d0 100644 --- a/src/subs_controller.cpp +++ b/src/subs_controller.cpp @@ -130,8 +130,8 @@ SubsController::SubsController(agi::Context *context) , undo_connection(context->ass->AddUndoManager(&SubsController::OnCommit, this)) { autosave_timer_changed(&autosave_timer); - OPT_SUB("App/Auto/Save", autosave_timer_changed, &autosave_timer); - OPT_SUB("App/Auto/Save Every Seconds", autosave_timer_changed, &autosave_timer); + OPT_SUB("App/Auto/Save", [=] { autosave_timer_changed(&autosave_timer); }); + OPT_SUB("App/Auto/Save Every Seconds", [=] { autosave_timer_changed(&autosave_timer); }); autosave_timer.Bind(wxEVT_TIMER, [=](wxTimerEvent&) { try { diff --git a/src/video_slider.cpp b/src/video_slider.cpp index b6370847b..1490a838d 100644 --- a/src/video_slider.cpp +++ b/src/video_slider.cpp @@ -53,7 +53,7 @@ VideoSlider::VideoSlider (wxWindow* parent, agi::Context *c) SetMinSize(wxSize(20, 25)); SetBackgroundStyle(wxBG_STYLE_PAINT); - slots.push_back(OPT_SUB("Video/Slider/Show Keyframes", &wxWindow::Refresh, this, false, nullptr)); + slots.push_back(OPT_SUB("Video/Slider/Show Keyframes", [=] { Refresh(false); })); slots.push_back(c->videoController->AddSeekListener(&VideoSlider::SetValue, this)); slots.push_back(c->videoController->AddVideoOpenListener(&VideoSlider::VideoOpened, this)); slots.push_back(c->videoController->AddKeyframesListener(&VideoSlider::KeyframesChanged, this));