/* Copyright 2004 Bastiaan Veelo This file is part of dcouple. Dcouple 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 2 of the License, or (at your option) any later version. You are free to negociate a different license with the copyright holder. Dcouple 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 dcouple; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ module dcouple.slot; private import dcouple.signal; private import dcouple.signalslot; //! An interface for the management of slots. /*! Provides an interface that must be implemented by classes that own \link Slot Slots \endlink in a managed way. */ interface SlotManager { /*! Deriving classes must mixin SlotManagement, or implement these functions to keep a list of \link Slot Slots \endlink that it owns, and disconnect() them in the destructor (so that connected \link Signal Signals \endlink remove their reference). Also, deriving classes must provide and use a method (say, .discard()) to disconnect all slots when the object is no longer used, so that it may be garbage collected. */ void register(GenericSlot); void deregister(GenericSlot); } /*! SlotManager implementation */ template SlotManagement() { /* ~this() { deleteSlots(); } */ protected: void register(GenericSlot s) { //_slots[s] = s; int len = _slots.length; int n = len-1; while (n >= 0 && _slots[n] is null) --n; if (n < len-1) _slots[n+1] = s; else _slots ~= s; } void deregister(GenericSlot s) { //_slots.remove(s); if (_slots.length == 0) return; bool found = false; int n; for (n=0 ; n < _slots.length-1; ++n) { if (!found && _slots[n] is s) found = true; if (found) _slots[n] = _slots[n+1]; if (_slots[n] is null) break; } if (found || _slots[length-1] is s) _slots[length-1] = null; } //GenericSlot[GenericSlot] _slots; GenericSlot[] _slots; void deleteSlots() { debug printf("SlotManager::deleteSlots entered.\n_slots:\n"); debug foreach(GenericSlot s; _slots) printf("slot %p.\n",s); foreach(GenericSlot s; _slots) { if (s is null) break; // prevent s from manipulating _slots: s.freeFrom(this); debug printf("SlotManager::deleteSlots: deleting %p.\n", s); delete s; pragma(msg, "_slots contains dangling references. OK?"); } } } //! An interface shared by all Slots interface GenericSlot //abstract class GenericSlot { //! Disconnect all. /*! Breaks all connections from this Slot. */ // void disconnect(); void disconnect(); SlotManager owner(); void genericConnect(GenericSignal); void genericDisconnect(GenericSignal); uint numberOfArguments(); char[][] arguments(); void freeFrom(SlotManager); } /*! The generic part of \link Slot Slots. \endlink */ private template SlotGenericCore() { //! Constructor. /*! The Slot \link SlotManager::register(Slot) registers \endlink itself with its \a owner. \a callBack must be a member function of \a owner, and is the method that is called when a signal is received. */ // this(SlotManager owner, CallBack callBack) // Waiting for fix to http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D.bugs/1890 // this(Function callBack) { this(null, callBack); } this(SlotManager owner, Function callBack) { debug printf( "contructing slot %p on Function.\n", this); _owner = owner; _callBack = new StaticCallBack(callBack); if (owner !is null) owner.register(this); initArguments(); } // this(Delegate callBack) { this(null, callBack); } this(SlotManager owner, Delegate callBack) { debug printf( "contructing slot %p on Delegate.\n", this); _owner = owner; debug printf( "owner set.\n"); _callBack = new NonStaticCallBack(callBack); debug printf( "NonStaticCallBack newed.\n"); if (owner) owner.register(this); debug printf( "Going to init arguments.\n"); initArguments(); debug printf( "Arguments initialised.\n"); } //! Destructor. /*! Disconnects all connected \link Signal Signals \endlink, and \link SlotManager::deregister(Slot) de-registers \endlink itself with its owner. */ ~this() { debug printf("slot %p is destructing.\n", this); disconnect(); /* Strictly spoken, destructors are not allowed to reference other objects, because the order in which the GC calls destructors is not defined, so those objects may not exist anymore. The disconnect() function above breaks this rule indirectly. But this particular case is OK, because we only consider references to CompatibleSignals that *are still alive*. Signals that are already destructed have removed our reference to them, just like we have removed references to us in other signals in disconnect(). */ if(_owner) _owner.deregister(this); debug printf("slot %p done destructing.\n", this); } /*! Establishes a connection between this Slot and Signal \a s, and vice versa (i.e., registers this Slot within Signal \a s). Multiple identical connections may exist, which causes the slot to receive the same signal as many times per emission as connections have been made. */ void connect(CompatibleSignal s) { debug printf("slot::connect entered.\n"); connectToo(s); s.connectToo(this); } package void connectToo(CompatibleSignal s) { debug printf("slot::connectToo entered.\n"); debug printf("_signals before:\n"); debug foreach( CompatibleSignal s; _signals ) printf( "signal %p\n", s); int len = _signals.length; int n = len-1; while (n >= 0 && _signals[n] is null) --n; if (n < len-1) _signals[n+1] = s; else _signals ~= s; debug printf("_signals after:\n"); debug foreach( CompatibleSignal s; _signals ) printf( "signal %p\n", s); } void genericConnect(GenericSignal gs) { debug printf("slot::genericConnect entered.\n"); CompatibleSignal s = cast(CompatibleSignal) gs; if( s ) connect(s); else { if (_owner) printf("dcouple WARNING: %.*s.%.*s!(", _owner.classinfo.name, this.classinfo.name); else printf("dcouple WARNING: %.*s!(", this.classinfo.name); if(numberOfArguments()>0) printf("%.*s",arguments()[0]); for(int i=1; i0) printf("%.*s",gs.arguments()[0]); for(int i=1; i0) printf("%.*s",arguments()[0]); for(int i=1; i0) printf("%.*s",gs.arguments()[0]); for(int i=1; i= 0; i-- ) { if( _signals[i] !is null ) disconnect(_signals[i]); } } /*! \returns the number of connected \link Signal Signals. \endlink */ int count() { return _signals.length; } uint numberOfArguments() { return _arguments.length; } char[][] arguments() {return _arguments;} SlotManager owner() { return _owner; } void freeFrom(SlotManager owner) { // Only the real owner can set us free. assert(owner is _owner); _owner = null; // Do not deregister, the owner is in charge. } private: //! This Slot is managed by _owner. SlotManager _owner; protected: //! "delegate", or pointer-to-member-function. CallBack _callBack; //! Collection of connected \link Signal Signals. \endlink //CompatibleSignal[CompatibleSignal] _signals; CompatibleSignal[] _signals; char[][] _arguments; } //! A slot to which signals can be connected. /*! A Slot should be owned by a \link SlotManager managing \endlink class, and can be \link connect() connected \endlink to one or more \link Signal Signals, \endlink possibly in other classes. This Slot is then notified when a connected Signal is \link Signal::emit() emitted, \endlink and the corresponding call-back function ("delegate") called. \todo Free slots have a problem. When they receive a signal during a garbage collection run, e.g., from an object that is configured to emit a "destroyed" signal, at a time when they themselves have become garbage (or more importantly, their call back function) they can cause a segmentation fault when calling the collected function. This situation is likely to occur at program termination (when "almost" verything is collected). This is supposedly not a problem for owned slots that refer to member functions of the owner, because the destructor of the owner is called before its members are invalidated (right?). Solution: have all free slots register with a global static SlotManager. Before letting the program terminate, this owner must be instructed to disconnect all slots. */ class Slot() : GenericSlot { alias void function() Function; alias void delegate() Delegate; alias PointerToFunction!() CallBack; alias PointerToStaticFunction!() StaticCallBack; alias PointerToNonStaticFunction!() NonStaticCallBack; alias Signal!() CompatibleSignal; // Signal!() is a Signal!(), so it is compatible too. mixin SlotGenericCore; /*! Calls the registered callBack member function. Allows the Slot to be called like a function. */ void opCall() { _callBack(); } void initArguments() {} } class Slot(T1) : GenericSlot { alias void function(T1) Function; alias void delegate(T1) Delegate; alias PointerToFunction!(T1) CallBack; alias PointerToStaticFunction!(T1) StaticCallBack; alias PointerToNonStaticFunction!(T1) NonStaticCallBack; alias Signal!(T1) CompatibleSignal; mixin SlotGenericCore; /*! Calls the registered callBack member function. Allows the Slot to be called like a function. */ void opCall(T1 t1) { _callBack(t1); } void initArguments() { _arguments ~= typeid(T1).toString(); } } class Slot(T1,T2) : GenericSlot { alias void function(T1,T2) Function; alias void delegate(T1,T2) Delegate; alias PointerToFunction!(T1,T2) CallBack; alias PointerToStaticFunction!(T1,T2) StaticCallBack; alias PointerToNonStaticFunction!(T1,T2) NonStaticCallBack; alias Signal!(T1,T2) CompatibleSignal; mixin SlotGenericCore; /*! Calls the registered callBack member function. Allows the Slot to be called like a function. */ void opCall(T1 t1, T2 t2) { _callBack(t1, t2); } void initArguments() { _arguments ~= typeid(T1).toString(); _arguments ~= typeid(T2).toString(); } } class Slot(T1,T2,T3) : GenericSlot { alias void function(T1,T2,T3) Function; alias void delegate(T1,T2,T3) Delegate; alias PointerToFunction!(T1,T2,T3) CallBack; alias PointerToStaticFunction!(T1,T2,T3) StaticCallBack; alias PointerToNonStaticFunction!(T1,T2,T3) NonStaticCallBack; alias Signal!(T1,T2,T3) CompatibleSignal; mixin SlotGenericCore; /*! Calls the registered callBack member function. Allows the Slot to be called like a function. */ void opCall(T1 t1, T2 t2, T3 t3) { _callBack(t1, t2, t3); } void initArguments() { _arguments ~= typeid(T1).toString(); _arguments ~= typeid(T2).toString(); _arguments ~= typeid(T3).toString(); } } class Slot(T1,T2,T3,T4) : GenericSlot { alias void function(T1,T2,T3,T4) Function; alias void delegate(T1,T2,T3,T4) Delegate; alias PointerToFunction!(T1,T2,T3,T4) CallBack; alias PointerToStaticFunction!(T1,T2,T3,T4) StaticCallBack; alias PointerToNonStaticFunction!(T1,T2,T3,T4) NonStaticCallBack; alias Signal!(T1,T2,T3,T4) CompatibleSignal; mixin SlotGenericCore; /*! Calls the registered callBack member function. Allows the Slot to be called like a function. */ void opCall(T1 t1, T2 t2, T3 t3, T4 t4) { _callBack(t1, t2, t3, t4); } void initArguments() { _arguments ~= typeid(T1).toString(); _arguments ~= typeid(T2).toString(); _arguments ~= typeid(T3).toString(); _arguments ~= typeid(T4).toString(); } } class Slot(T1,T2,T3,T4,T5) : GenericSlot { alias void function(T1,T2,T3,T4,T5) Function; alias void delegate(T1,T2,T3,T4,T5) Delegate; alias PointerToFunction!(T1,T2,T3,T4,T5) CallBack; alias PointerToStaticFunction!(T1,T2,T3,T4,T5) StaticCallBack; alias PointerToNonStaticFunction!(T1,T2,T3,T4,T5) NonStaticCallBack; alias Signal!(T1,T2,T3,T4,T5) CompatibleSignal; mixin SlotGenericCore; /*! Calls the registered callBack member function. Allows the Slot to be called like a function. */ void opCall(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) { _callBack(t1, t2, t3, t4, t5); } void initArguments() { _arguments ~= typeid(T1).toString(); _arguments ~= typeid(T2).toString(); _arguments ~= typeid(T3).toString(); _arguments ~= typeid(T4).toString(); _arguments ~= typeid(T5).toString(); } } class Slot(T1,T2,T3,T4,T5,T6) : GenericSlot { alias void function(T1,T2,T3,T4,T5,T6) Function; alias void delegate(T1,T2,T3,T4,T5,T6) Delegate; alias PointerToFunction!(T1,T2,T3,T4,T5,T6) CallBack; alias PointerToStaticFunction!(T1,T2,T3,T4,T5,T6) StaticCallBack; alias PointerToNonStaticFunction!(T1,T2,T3,T4,T5,T6) NonStaticCallBack; alias Signal!(T1,T2,T3,T4,T5,T6) CompatibleSignal; mixin SlotGenericCore; /*! Calls the registered callBack member function. Allows the Slot to be called like a function. */ void opCall(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { _callBack(t1, t2, t3, t4, t5, t6); } void initArguments() { _arguments ~= typeid(T1).toString(); _arguments ~= typeid(T2).toString(); _arguments ~= typeid(T3).toString(); _arguments ~= typeid(T4).toString(); _arguments ~= typeid(T5).toString(); _arguments ~= typeid(T6).toString(); } } class Slot(T1,T2,T3,T4,T5,T6,T7) : GenericSlot { alias void function(T1,T2,T3,T4,T5,T6,T7) Function; alias void delegate(T1,T2,T3,T4,T5,T6,T7) Delegate; alias PointerToFunction!(T1,T2,T3,T4,T5,T6,T7) CallBack; alias PointerToStaticFunction!(T1,T2,T3,T4,T5,T6,T7) StaticCallBack; alias PointerToNonStaticFunction!(T1,T2,T3,T4,T5,T6,T7) NonStaticCallBack; alias Signal!(T1,T2,T3,T4,T5,T6,T7) CompatibleSignal; mixin SlotGenericCore; /*! Calls the registered callBack member function. Allows the Slot to be called like a function. */ void opCall(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) { _callBack(t1, t2, t3, t4, t5, t6, t7); } void initArguments() { _arguments ~= typeid(T1).toString(); _arguments ~= typeid(T2).toString(); _arguments ~= typeid(T3).toString(); _arguments ~= typeid(T4).toString(); _arguments ~= typeid(T5).toString(); _arguments ~= typeid(T6).toString(); _arguments ~= typeid(T7).toString(); } } private { abstract class PointerToFunction() { void opCall(); } class PointerToStaticFunction() : PointerToFunction!() { this(void function() fp) { _fp = fp; } void opCall() { _fp(); } private: void function() _fp; } class PointerToNonStaticFunction() : PointerToFunction!() { this(void delegate() dg) { _dg = dg; } void opCall() { _dg(); } private: void delegate() _dg; } abstract class PointerToFunction(T1) { void opCall(T1); } class PointerToStaticFunction(T1) : PointerToFunction!(T1) { this(void function(T1) fp) { _fp = fp; } void opCall(T1 t1) { _fp(t1); } private: void function(T1) _fp; } class PointerToNonStaticFunction(T1) : PointerToFunction!(T1) { this(void delegate(T1) dg) { _dg = dg; } void opCall(T1 t1) { _dg(t1); } private: void delegate(T1) _dg; } abstract class PointerToFunction(T1, T2) { void opCall(T1, T2); } class PointerToStaticFunction(T1, T2) : PointerToFunction!(T1, T2) { this(void function(T1, T2) fp) { _fp = fp; } void opCall(T1 t1, T2 t2) { _fp(t1, t2); } private: void function(T1, T2) _fp; } class PointerToNonStaticFunction(T1, T2) : PointerToFunction!(T1, T2) { this(void delegate(T1, T2) dg) { _dg = dg; } void opCall(T1 t1, T2 t2) { _dg(t1, t2); } private: void delegate(T1, T2) _dg; } abstract class PointerToFunction(T1, T2, T3) { void opCall(T1, T2, T3); } class PointerToStaticFunction(T1, T2, T3) : PointerToFunction!(T1, T2, T3) { this(void function(T1, T2, T3) fp) { _fp = fp; } void opCall(T1 t1, T2 t2, T3 t3) { _fp(t1, t2, t3); } private: void function(T1, T2, T3) _fp; } class PointerToNonStaticFunction(T1, T2, T3) : PointerToFunction!(T1, T2, T3) { this(void delegate(T1, T2, T3) dg) { _dg = dg; } void opCall(T1 t1, T2 t2, T3 t3) { _dg(t1, t2, t3); } private: void delegate(T1, T2, T3) _dg; } abstract class PointerToFunction(T1, T2, T3, T4) { void opCall(T1, T2, T3, T4); } class PointerToStaticFunction(T1, T2, T3, T4) : PointerToFunction!(T1, T2, T3, T4) { this(void function(T1, T2, T3, T4) fp) { _fp = fp; } void opCall(T1 t1, T2 t2, T3 t3, T4 t4) { _fp(t1, t2, t3, t4); } private: void function(T1, T2, T3, T4) _fp; } class PointerToNonStaticFunction(T1, T2, T3, T4) : PointerToFunction!(T1, T2, T3, T4) { this(void delegate(T1, T2, T3, T4) dg) { _dg = dg; } void opCall(T1 t1, T2 t2, T3 t3, T4 t4) { _dg(t1, t2, t3, t4); } private: void delegate(T1, T2, T3, T4) _dg; } abstract class PointerToFunction(T1, T2, T3, T4, T5) { void opCall(T1, T2, T3, T4, T5); } class PointerToStaticFunction(T1, T2, T3, T4, T5) : PointerToFunction!(T1, T2, T3, T4, T5) { this(void function(T1, T2, T3, T4, T5) fp) { _fp = fp; } void opCall(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) { _fp(t1, t2, t3, t4, t5); } private: void function(T1, T2, T3, T4, T5) _fp; } class PointerToNonStaticFunction(T1, T2, T3, T4, T5) : PointerToFunction!(T1, T2, T3, T4, T5) { this(void delegate(T1, T2, T3, T4, T5) dg) { _dg = dg; } void opCall(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) { _dg(t1, t2, t3, t4, t5); } private: void delegate(T1, T2, T3, T4, T5) _dg; } abstract class PointerToFunction(T1, T2, T3, T4, T5, T6) { void opCall(T1, T2, T3, T4, T5, T6); } class PointerToStaticFunction(T1, T2, T3, T4, T5, T6) : PointerToFunction!(T1, T2, T3, T4, T5, T6) { this(void function(T1, T2, T3, T4, T5, T6) fp) { _fp = fp; } void opCall(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { _fp(t1, t2, t3, t4, t5, t6); } private: void function(T1, T2, T3, T4, T5, T6) _fp; } class PointerToNonStaticFunction(T1, T2, T3, T4, T5, T6) : PointerToFunction!(T1, T2, T3, T4, T5, T6) { this(void delegate(T1, T2, T3, T4, T5, T6) dg) { _dg = dg; } void opCall(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { _dg(t1, t2, t3, t4, t5, t6); } private: void delegate(T1, T2, T3, T4, T5, T6) _dg; } abstract class PointerToFunction(T1, T2, T3, T4, T5, T6, T7) { void opCall(T1, T2, T3, T4, T5, T6, T7); } class PointerToStaticFunction(T1, T2, T3, T4, T5, T6, T7) : PointerToFunction!(T1, T2, T3, T4, T5, T6, T7) { this(void function(T1, T2, T3, T4, T5, T6, T7) fp) { _fp = fp; } void opCall(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { _fp(t1, t2, t3, t4, t5, t6, t7); } private: void function(T1, T2, T3, T4, T5, T6, T7) _fp; } class PointerToNonStaticFunction(T1, T2, T3, T4, T5, T6, T7) : PointerToFunction!(T1, T2, T3, T4, T5, T6, T7) { this(void delegate(T1, T2, T3, T4, T5, T6, T7) dg) { _dg = dg; } void opCall(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { _dg(t1, t2, t3, t4, t5, t6, t7); } private: void delegate(T1, T2, T3, T4, T5, T6, T7) _dg; } } // private