/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * notifier.h * * Thu Sep 3 15:48:39 CEST 2015 * Copyright 2015 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of DrumGizmo. * * DrumGizmo is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * DrumGizmo 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with DrumGizmo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #pragma once #include #include #include #include namespace aux { template struct placeholder { }; } namespace std { template struct is_placeholder> : integral_constant { }; } namespace aux { // std::integer_sequence introduced in C++14 so remove this once we start requiring that. template struct int_sequence { }; template struct gen_int_sequence : gen_int_sequence { }; template struct gen_int_sequence<0, Ns...> : int_sequence { }; }; //namespace GUI { class Listener; class NotifierBase { public: virtual void disconnect(Listener* object) {} }; class Listener { public: virtual ~Listener() { for(auto signal : signals) { signal->disconnect(this); } } void registerNotifier(NotifierBase* signal) { signals.insert(signal); } void unregisterNotifier(NotifierBase* signal) { signals.erase(signal); } private: std::set signals; }; template class Notifier : public NotifierBase { public: Notifier() {} //! \brief When dtor is called it will automatically disconnect all its listeners. ~Notifier() { for(auto& slot : slots) { slot.first->unregisterNotifier(this); } } using callback_type = std::function; //! \brief Connect object to this Notifier. template void connect(O* p, const F& fn) { slots.emplace_back(std::make_pair(p, std::move(construct_mem_fn(fn, p, aux::gen_int_sequence{})))); if(p && dynamic_cast(p)) { dynamic_cast(p)->registerNotifier(this); } } //! \brief Disconnect object from this Notifier. void disconnect(Listener* object) { for(auto it = slots.begin(); it != slots.end(); ++it) { if(it->first == object) { slots.erase(it); return; } } } //! \brief Activate this notifier by pretending it is a function. //! Example: Notifier foo; foo(42); void operator()(Args... args) { for(auto& slot : slots) { slot.second(args...); } } private: std::list> slots; template callback_type construct_mem_fn(const F& fn, O* p, aux::int_sequence) const { return std::bind(fn, p, aux::placeholder{}...); } }; //} // GUI:: #define CONNECT(SRC, SIG, TAR, SLO) (SRC)->SIG.connect(TAR, SLO)