Added new Signal/Slot system in FbTk
This is suppose to replace the obsolete Subject/Observer classes. See the src/tests/testSignals.cc for basic usage.
This commit is contained in:
parent
8e97963e42
commit
e4d4717703
7 changed files with 783 additions and 1 deletions
|
@ -1,4 +1,9 @@
|
||||||
(Format: Year/Month/Day)
|
(Format: Year/Month/Day)
|
||||||
|
Changes for 1.1.2:
|
||||||
|
*08/09/18:
|
||||||
|
* Added new Signal/Slot system to FbTk (Henrik)
|
||||||
|
This is suppose to replace the obsolete Subject/Observer classes.
|
||||||
|
FbTk/Signal.hh, FbTk/Slot.hh, FbTk/MemFun.hh, tests/testSignals.cc
|
||||||
Changes for 1.1.1
|
Changes for 1.1.1
|
||||||
*08/09/14:
|
*08/09/14:
|
||||||
* Fixed a minor pixmap resource leak (Henrik)
|
* Fixed a minor pixmap resource leak (Henrik)
|
||||||
|
|
|
@ -63,6 +63,7 @@ libFbTk_a_SOURCES = App.hh App.cc Color.cc Color.hh Command.hh \
|
||||||
TypeAhead.hh SearchResult.hh SearchResult.cc ITypeAheadable.hh \
|
TypeAhead.hh SearchResult.hh SearchResult.cc ITypeAheadable.hh \
|
||||||
Select2nd.hh STLUtil.hh \
|
Select2nd.hh STLUtil.hh \
|
||||||
CachedPixmap.hh CachedPixmap.cc \
|
CachedPixmap.hh CachedPixmap.cc \
|
||||||
|
Slot.hh Signal.hh MemFun.hh \
|
||||||
${xpm_SOURCE} \
|
${xpm_SOURCE} \
|
||||||
${xft_SOURCE} \
|
${xft_SOURCE} \
|
||||||
${xmb_SOURCE} \
|
${xmb_SOURCE} \
|
||||||
|
|
139
src/FbTk/MemFun.hh
Normal file
139
src/FbTk/MemFun.hh
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
// MemFun.hh for FbTk, Fluxbox Toolkit
|
||||||
|
// Copyright (c) 2008 Henrik Kinnunen (fluxgen at fluxbox dot org)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the "Software"),
|
||||||
|
// to deal in the Software without restriction, including without limitation
|
||||||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef FBTK_MEM_FUN_HH
|
||||||
|
#define FBTK_MEM_FUN_HH
|
||||||
|
|
||||||
|
namespace FbTk {
|
||||||
|
|
||||||
|
/// No argument functor
|
||||||
|
template <typename ReturnType, typename Object>
|
||||||
|
class MemFun0 {
|
||||||
|
public:
|
||||||
|
typedef ReturnType (Object:: *Action)();
|
||||||
|
|
||||||
|
MemFun0(Object& obj, Action action):
|
||||||
|
m_obj(obj),
|
||||||
|
m_action(action) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator ()() {
|
||||||
|
(m_obj.*m_action)();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Object& m_obj;
|
||||||
|
Action m_action;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename ReturnType, typename Object>
|
||||||
|
MemFun0<ReturnType, Object>
|
||||||
|
MemFun( Object& obj, ReturnType (Object:: *action)() ) {
|
||||||
|
return MemFun0<ReturnType, Object>(obj, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One argument functor
|
||||||
|
template <typename ReturnType, typename Object, typename Arg1>
|
||||||
|
class MemFun1 {
|
||||||
|
public:
|
||||||
|
typedef ReturnType (Object:: *Action)(Arg1);
|
||||||
|
|
||||||
|
MemFun1(Object& obj, Action action):
|
||||||
|
m_obj(obj),
|
||||||
|
m_action(action) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator ()(Arg1 arg1) {
|
||||||
|
(m_obj.*m_action)(arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Object& m_obj;
|
||||||
|
Action m_action;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// One argument functor helper function
|
||||||
|
template <typename ReturnType, typename Object, typename Arg1>
|
||||||
|
MemFun1<ReturnType, Object, Arg1>
|
||||||
|
MemFun( Object& obj, ReturnType (Object:: *action)(Arg1) ) {
|
||||||
|
return MemFun1<ReturnType, Object, Arg1>(obj, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Two argument functor
|
||||||
|
template <typename ReturnType, typename Object, typename Arg1, typename Arg2>
|
||||||
|
class MemFun2 {
|
||||||
|
public:
|
||||||
|
typedef ReturnType (Object:: *Action)(Arg1,Arg2);
|
||||||
|
|
||||||
|
MemFun2(Object& obj, Action action):
|
||||||
|
m_obj(obj),
|
||||||
|
m_action(action) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator ()(Arg1 arg1, Arg2 arg2) {
|
||||||
|
(m_obj.*m_action)(arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Object& m_obj;
|
||||||
|
Action m_action;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Two argument functor helper function
|
||||||
|
template <typename ReturnType, typename Object, typename Arg1, typename Arg2>
|
||||||
|
MemFun2<ReturnType, Object, Arg1, Arg2>
|
||||||
|
MemFun( Object& obj, ReturnType (Object:: *action)(Arg1,Arg2) ) {
|
||||||
|
return MemFun2<ReturnType, Object, Arg1, Arg2>(obj, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Three argument functor
|
||||||
|
template <typename ReturnType, typename Object,
|
||||||
|
typename Arg1, typename Arg2, typename Arg3>
|
||||||
|
class MemFun3 {
|
||||||
|
public:
|
||||||
|
typedef ReturnType (Object:: *Action)(Arg1,Arg2,Arg3);
|
||||||
|
|
||||||
|
MemFun3(Object& obj, Action action):
|
||||||
|
m_obj(obj),
|
||||||
|
m_action(action) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator ()(Arg1 arg1, Arg2 arg2, Arg3 arg3) {
|
||||||
|
(m_obj.*m_action)(arg1, arg2, arg3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Object& m_obj;
|
||||||
|
Action m_action;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Three argument functor helper
|
||||||
|
template <typename ReturnType, typename Object, typename Arg1, typename Arg2, typename Arg3>
|
||||||
|
MemFun3<ReturnType, Object, Arg1, Arg2, Arg3>
|
||||||
|
MemFun( Object& obj, ReturnType (Object:: *action)(Arg1, Arg2, Arg3) ) {
|
||||||
|
return MemFun3<ReturnType, Object, Arg1, Arg2, Arg3>(obj, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FbTk
|
||||||
|
|
||||||
|
#endif // FBTK_MEM_FUN_HH
|
||||||
|
|
218
src/FbTk/Signal.hh
Normal file
218
src/FbTk/Signal.hh
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
// Signal.hh for FbTk, Fluxbox Toolkit
|
||||||
|
// Copyright (c) 2008 Henrik Kinnunen (fluxgen at fluxbox dot org)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the "Software"),
|
||||||
|
// to deal in the Software without restriction, including without limitation
|
||||||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef FBTK_SIGNAL_HH
|
||||||
|
#define FBTK_SIGNAL_HH
|
||||||
|
|
||||||
|
#include "Slot.hh"
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace FbTk {
|
||||||
|
|
||||||
|
/// \namespace Implementation details for signals, do not use anything in this namespace
|
||||||
|
namespace SigImpl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent class for all \c Signal[0...*] classes.
|
||||||
|
* It handles the disconnect and holds all the slots. The connect must be
|
||||||
|
* handled by the child class so it can do the type checking.
|
||||||
|
*/
|
||||||
|
class SignalHolder {
|
||||||
|
public:
|
||||||
|
/// Do not use this type outside this class
|
||||||
|
typedef std::list<SlotHolder> SlotList;
|
||||||
|
|
||||||
|
typedef SlotList::iterator Iterator;
|
||||||
|
typedef Iterator SlotID;
|
||||||
|
typedef SlotList::const_iterator ConstIterator;
|
||||||
|
|
||||||
|
/// Remove a specific slot \c id from this signal
|
||||||
|
void disconnect(SlotID slotIt) {
|
||||||
|
m_slots.erase( slotIt );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Removes all slots connected to this
|
||||||
|
void clear() {
|
||||||
|
m_slots.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ConstIterator begin() const { return m_slots.begin(); }
|
||||||
|
ConstIterator end() const { return m_slots.end(); }
|
||||||
|
|
||||||
|
Iterator begin() { return m_slots.begin(); }
|
||||||
|
Iterator end() { return m_slots.end(); }
|
||||||
|
|
||||||
|
/// Connect a slot to this signal. Must only be called by child classes.
|
||||||
|
SlotID connect(const SlotHolder& slot) {
|
||||||
|
return m_slots.insert(m_slots.end(), slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SlotList m_slots; ///< all slots connected to a signal
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Signal with no argument
|
||||||
|
template <typename ReturnType>
|
||||||
|
class Signal0: public SignalHolder {
|
||||||
|
public:
|
||||||
|
typedef Slot0<ReturnType> SlotType;
|
||||||
|
|
||||||
|
void emit() {
|
||||||
|
for ( Iterator it = begin(); it != end(); ++it ) {
|
||||||
|
static_cast<SlotType&>(*it)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotID connect(const SlotType& slot) {
|
||||||
|
return SignalHolder::connect(slot);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Signal with one argument
|
||||||
|
template <typename ReturnType, typename Arg1>
|
||||||
|
class Signal1: public SignalHolder {
|
||||||
|
public:
|
||||||
|
typedef Slot1<ReturnType, Arg1> SlotType;
|
||||||
|
|
||||||
|
void emit(Arg1 arg) {
|
||||||
|
for ( Iterator it = begin(); it != end(); ++it ) {
|
||||||
|
static_cast<SlotType&>(*it)(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotID connect(const SlotType& slot) {
|
||||||
|
return SignalHolder::connect(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Signal with two arguments
|
||||||
|
template <typename ReturnType, typename Arg1, typename Arg2>
|
||||||
|
class Signal2: public SignalHolder {
|
||||||
|
public:
|
||||||
|
typedef Slot2<ReturnType, Arg1, Arg2> SlotType;
|
||||||
|
|
||||||
|
void emit(Arg1 arg1, Arg2 arg2) {
|
||||||
|
for ( Iterator it = begin(); it != end(); ++it ) {
|
||||||
|
static_cast<SlotType&>(*it)(arg1, arg2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotID connect(const SlotType& slot) {
|
||||||
|
return SignalHolder::connect(slot);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Signal with three arguments
|
||||||
|
template <typename ReturnType, typename Arg1, typename Arg2, typename Arg3>
|
||||||
|
class Signal3: public SignalHolder {
|
||||||
|
public:
|
||||||
|
typedef Slot3<ReturnType, Arg1, Arg2, Arg3> SlotType;
|
||||||
|
|
||||||
|
void emit(Arg1 arg1, Arg2 arg2, Arg3 arg3) {
|
||||||
|
for ( Iterator it = begin(); it != end(); ++it ) {
|
||||||
|
static_cast<SlotType&>(*it)(arg1, arg2, arg3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotID connect(const SlotType& slot) {
|
||||||
|
return SignalHolder::connect(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EmptyArg {};
|
||||||
|
|
||||||
|
} // namespace SigImpl
|
||||||
|
|
||||||
|
|
||||||
|
/// Specialization for three arguments.
|
||||||
|
template <typename ReturnType,
|
||||||
|
typename Arg1 = SigImpl::EmptyArg, typename Arg2 = SigImpl::EmptyArg, typename Arg3 = SigImpl::EmptyArg >
|
||||||
|
class Signal: public SigImpl::Signal3< ReturnType, Arg1, Arg2, Arg3 > {
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Specialization for two arguments.
|
||||||
|
template <typename ReturnType, typename Arg1, typename Arg2>
|
||||||
|
class Signal<ReturnType, Arg1, Arg2, SigImpl::EmptyArg>: public SigImpl::Signal2< ReturnType, Arg1, Arg2 > {
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Specialization for one argument.
|
||||||
|
template <typename ReturnType, typename Arg1>
|
||||||
|
class Signal<ReturnType, Arg1, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::Signal1< ReturnType, Arg1 > {
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Specialization for no argument.
|
||||||
|
template <typename ReturnType>
|
||||||
|
class Signal<ReturnType, SigImpl::EmptyArg, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::Signal0< ReturnType > {
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks a signal during it's life time. All signals connected using \c
|
||||||
|
* SignalTracker::join will be erased when this instance dies.
|
||||||
|
*/
|
||||||
|
class SignalTracker {
|
||||||
|
public:
|
||||||
|
/// Internal type, do not use.
|
||||||
|
typedef std::list< std::pair< SigImpl::SignalHolder*, SigImpl::SignalHolder::SlotID > > Connections;
|
||||||
|
typedef Connections::iterator TrackID; ///< \c ID type for join/leave.
|
||||||
|
|
||||||
|
~SignalTracker() {
|
||||||
|
// disconnect all connections
|
||||||
|
for ( Connections::iterator conIt = m_connections.begin();
|
||||||
|
conIt != m_connections.end(); ) {
|
||||||
|
conIt->first->disconnect( conIt->second );
|
||||||
|
conIt = m_connections.erase( conIt );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts tracking a signal.
|
||||||
|
/// @return A tracking ID ( not unique )
|
||||||
|
template <typename Signal, typename Functor>
|
||||||
|
TrackID join(Signal& sig, const Functor& functor) {
|
||||||
|
return
|
||||||
|
m_connections.insert(m_connections.end(),
|
||||||
|
Connections::value_type(&sig, sig.connect(functor)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Leave tracking for a signal
|
||||||
|
/// @param id the \c id from the previous \c join
|
||||||
|
void leave(TrackID id) {
|
||||||
|
m_connections.erase(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// holds all connections to different signals and slots.
|
||||||
|
Connections m_connections;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace FbTk
|
||||||
|
|
||||||
|
#endif // FBTK_SIGNAL_HH
|
294
src/FbTk/Slot.hh
Normal file
294
src/FbTk/Slot.hh
Normal file
|
@ -0,0 +1,294 @@
|
||||||
|
// Slot.hh for FbTk, Fluxbox Toolkit
|
||||||
|
// Copyright (c) 2008 Henrik Kinnunen (fluxgen at fluxbox dot org)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the "Software"),
|
||||||
|
// to deal in the Software without restriction, including without limitation
|
||||||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef FBTK_SLOT_HH
|
||||||
|
#define FBTK_SLOT_HH
|
||||||
|
|
||||||
|
namespace FbTk {
|
||||||
|
|
||||||
|
/// \namespace Implementation details for signals, do not use anything in this namespace
|
||||||
|
namespace SigImpl {
|
||||||
|
|
||||||
|
class CallbackHolder;
|
||||||
|
|
||||||
|
/// Placeholder type for typed callbacks
|
||||||
|
typedef void* (*CallbackFunc)(void *);
|
||||||
|
/// Clone function callback type for cloning typed callback holders
|
||||||
|
typedef CallbackHolder* (*CloneFunc)(CallbackHolder*);
|
||||||
|
/// Kill function callback type for destroying type specific information in
|
||||||
|
/// FunctorHolder
|
||||||
|
typedef void (*KillFunc)(CallbackHolder*);
|
||||||
|
|
||||||
|
/// Holds clone, functor callback, and the kill function for FunctorHolder.
|
||||||
|
class CallbackHolder {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @param callback The callback to call when a slot receives a signal.
|
||||||
|
* @param clone The callback to use for cloning a type specific instance of
|
||||||
|
* this classinstance.
|
||||||
|
* @param kill The callback that knows how to free the memory in type
|
||||||
|
* specific instance of this class.
|
||||||
|
*/
|
||||||
|
CallbackHolder(CallbackFunc callback,
|
||||||
|
CloneFunc clone,
|
||||||
|
KillFunc kill):
|
||||||
|
m_callback(callback),
|
||||||
|
m_kill(kill),
|
||||||
|
m_clone(clone) { }
|
||||||
|
|
||||||
|
~CallbackHolder() {
|
||||||
|
(*m_kill)(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return a clone of this instance
|
||||||
|
CallbackHolder* clone() {
|
||||||
|
return (*m_clone)(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \c Callback to \c Functor specific callback
|
||||||
|
CallbackFunc m_callback;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
CallbackHolder& operator = (const CallbackHolder& other) {
|
||||||
|
if ( this == &other ) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
m_callback = other.m_callback;
|
||||||
|
m_clone = other.m_clone;
|
||||||
|
m_kill = other.m_kill;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallbackHolder(const CallbackHolder& other) {
|
||||||
|
*this = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// This function is called to kill this instance
|
||||||
|
KillFunc m_kill;
|
||||||
|
/// Functions that knows how to clone a specific \c Functor type
|
||||||
|
CloneFunc m_clone;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Holds the functor and creates a clone callback for \c Functor specific type
|
||||||
|
template <typename Functor>
|
||||||
|
class FunctorHolder: public CallbackHolder {
|
||||||
|
public:
|
||||||
|
/// This type.
|
||||||
|
typedef FunctorHolder<Functor> Self;
|
||||||
|
/**
|
||||||
|
* @param functor The functor to be used when a signal is emitted.
|
||||||
|
* @param callback The callback to call when a signal is emitted.
|
||||||
|
*/
|
||||||
|
FunctorHolder(const Functor& functor, CallbackFunc callback):
|
||||||
|
CallbackHolder(callback, &clone, &kill),
|
||||||
|
m_functor(functor) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specific clone for this Functor type
|
||||||
|
static CallbackHolder* clone(CallbackHolder* self) {
|
||||||
|
return new Self( static_cast<Self&>(*self));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kill(CallbackHolder* self) {
|
||||||
|
// Destroy functor
|
||||||
|
static_cast<Self*>( self )->m_functor.~Functor();
|
||||||
|
}
|
||||||
|
|
||||||
|
Functor m_functor; ///< the functor to use when a signal is emitted.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Callback with no arguments.
|
||||||
|
template <typename Functor, typename ReturnType >
|
||||||
|
struct Callback0 {
|
||||||
|
static ReturnType callback(CallbackHolder* base) {
|
||||||
|
static_cast< FunctorHolder<Functor>* >( base )->m_functor();
|
||||||
|
return ReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
|
static CallbackFunc functionAddress() {
|
||||||
|
return reinterpret_cast<CallbackFunc>(&callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Callback with one argument
|
||||||
|
template <typename Functor, typename ReturnType, typename Arg1>
|
||||||
|
struct Callback1 {
|
||||||
|
typedef ReturnType (Functor::* CallbackType)(CallbackHolder*, Arg1);
|
||||||
|
|
||||||
|
static ReturnType callback(CallbackHolder* base, Arg1 arg1) {
|
||||||
|
static_cast< FunctorHolder<Functor>* >( base )->m_functor(arg1);
|
||||||
|
return ReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
|
static CallbackFunc functionAddress() {
|
||||||
|
return reinterpret_cast<CallbackFunc>(&callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Callback with two arguments
|
||||||
|
template <typename Functor, typename ReturnType,
|
||||||
|
typename Arg1, typename Arg2>
|
||||||
|
struct Callback2 {
|
||||||
|
typedef ReturnType (Functor::* CallbackType)(CallbackHolder*, Arg1, Arg2);
|
||||||
|
|
||||||
|
static ReturnType callback(CallbackHolder* base, Arg1 arg1, Arg2 arg2) {
|
||||||
|
static_cast< FunctorHolder<Functor>* >( base )->m_functor(arg1, arg2);
|
||||||
|
return ReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
|
static CallbackFunc functionAddress() {
|
||||||
|
return reinterpret_cast<CallbackFunc>(&callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Callback with three arguments
|
||||||
|
template <typename Functor, typename ReturnType,
|
||||||
|
typename Arg1, typename Arg2, typename Arg3>
|
||||||
|
struct Callback3 {
|
||||||
|
typedef ReturnType (Functor::* CallbackType)(CallbackHolder*, Arg1, Arg2, Arg3);
|
||||||
|
|
||||||
|
static ReturnType callback(CallbackHolder* base, Arg1 arg1, Arg2 arg2, Arg3 arg3) {
|
||||||
|
static_cast< FunctorHolder<Functor>* >( base )->m_functor( arg1, arg2, arg3 );
|
||||||
|
return ReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
|
static CallbackFunc functionAddress() {
|
||||||
|
return reinterpret_cast<CallbackFunc>(&callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Holds callback holder and handles the copying of callback holders for the
|
||||||
|
/// \c Slots.
|
||||||
|
class SlotHolder {
|
||||||
|
public:
|
||||||
|
SlotHolder(const SlotHolder& other):
|
||||||
|
m_holder( other.m_holder ? other.m_holder->clone() : 0 ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~SlotHolder() {
|
||||||
|
delete m_holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotHolder& operator = (const SlotHolder& other) {
|
||||||
|
if ( &other == this ) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
delete m_holder;
|
||||||
|
if ( other.m_holder ) {
|
||||||
|
m_holder = other.m_holder->clone();
|
||||||
|
} else {
|
||||||
|
m_holder = 0;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotHolder():m_holder( 0 ) { }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit SlotHolder(CallbackHolder* holder):
|
||||||
|
m_holder( holder ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
CallbackHolder* m_holder;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Slot with no argument.
|
||||||
|
template <typename ReturnType>
|
||||||
|
class Slot0: public SlotHolder {
|
||||||
|
public:
|
||||||
|
typedef ReturnType (*CallbackType)(CallbackHolder*);
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
Slot0( const Functor& functor ):
|
||||||
|
SlotHolder( new FunctorHolder<Functor>
|
||||||
|
(functor, Callback0<Functor, ReturnType>::functionAddress())) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()() {
|
||||||
|
reinterpret_cast<CallbackType>(m_holder->m_callback)( m_holder );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Slot with one argument.
|
||||||
|
template <typename ReturnType, typename Arg1>
|
||||||
|
class Slot1:public SlotHolder {
|
||||||
|
public:
|
||||||
|
typedef ReturnType (*CallbackType)(CallbackHolder*, Arg1);
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
Slot1( const Functor& functor ):
|
||||||
|
SlotHolder( new FunctorHolder<Functor>
|
||||||
|
(functor, Callback1<Functor, ReturnType, Arg1>::functionAddress())){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(Arg1 arg) {
|
||||||
|
reinterpret_cast<CallbackType>(m_holder->m_callback)(m_holder, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Slot with two arguments
|
||||||
|
template <typename ReturnType, typename Arg1, typename Arg2>
|
||||||
|
class Slot2: public SlotHolder {
|
||||||
|
public:
|
||||||
|
typedef ReturnType (*CallbackType)(CallbackHolder*, Arg1, Arg2);
|
||||||
|
template <typename Functor>
|
||||||
|
Slot2( const Functor& functor ):
|
||||||
|
SlotHolder( new FunctorHolder<Functor>
|
||||||
|
(functor, Callback2<Functor, ReturnType, Arg1, Arg2>::functionAddress())){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(Arg1 arg1, Arg2 arg2) {
|
||||||
|
reinterpret_cast<CallbackType>(m_holder->m_callback)(m_holder, arg1, arg2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Slot with three arguments
|
||||||
|
template <typename ReturnType, typename Arg1, typename Arg2, typename Arg3>
|
||||||
|
class Slot3: public SlotHolder {
|
||||||
|
public:
|
||||||
|
typedef ReturnType (*CallbackType)(CallbackHolder*, Arg1, Arg2, Arg3);
|
||||||
|
template <typename Functor>
|
||||||
|
Slot3( const Functor& functor ):
|
||||||
|
SlotHolder( new FunctorHolder<Functor>
|
||||||
|
(functor, Callback3<Functor, ReturnType, Arg1, Arg2, Arg3>::functionAddress())){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3) {
|
||||||
|
reinterpret_cast<CallbackType>(m_holder->m_callback)
|
||||||
|
( m_holder, arg1, arg2, arg3 );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SigImpl
|
||||||
|
|
||||||
|
} // namespace FbTk
|
||||||
|
|
||||||
|
#endif // FBTK_SLOT_H
|
|
@ -7,7 +7,8 @@ COMPILEFILE=$(CXX) -c $(CXXFLAGS)
|
||||||
FONT_OBJ = ../FbTk/libFbTk.a
|
FONT_OBJ = ../FbTk/libFbTk.a
|
||||||
COMPILE = ${CXX} ${CXXFLAGS} ${XLIBS}
|
COMPILE = ${CXX} ${CXXFLAGS} ${XLIBS}
|
||||||
|
|
||||||
all: testMenu testFont testTexture movetest
|
all: testMenu testFont testTexture movetest testSignals
|
||||||
|
|
||||||
|
|
||||||
.cc.o:
|
.cc.o:
|
||||||
$(CXX) -c $(CXXFLAGS) $<
|
$(CXX) -c $(CXXFLAGS) $<
|
||||||
|
@ -15,6 +16,9 @@ all: testMenu testFont testTexture movetest
|
||||||
glxtest: ../FbTk/App.hh glxtest.cc
|
glxtest: ../FbTk/App.hh glxtest.cc
|
||||||
${CXX} glxtest.cc ${CXXFLAGS} ${XLIBS} -lGL -lGLU -lXpm -o glxtest
|
${CXX} glxtest.cc ${CXXFLAGS} ${XLIBS} -lGL -lGLU -lXpm -o glxtest
|
||||||
|
|
||||||
|
testSignals: testSignals.o ../FbTk/Signal.hh ../FbTk/MemFun.hh
|
||||||
|
$(CXX) $(LIBS) testSignals.o -o testSignals
|
||||||
|
|
||||||
testStringUtil: StringUtiltest.o
|
testStringUtil: StringUtiltest.o
|
||||||
$(CXX) $(LIBS) StringUtiltest.o ../FbTk/libFbTk.a -o testStringUtil
|
$(CXX) $(LIBS) StringUtiltest.o ../FbTk/libFbTk.a -o testStringUtil
|
||||||
|
|
||||||
|
|
121
src/tests/testSignals.cc
Normal file
121
src/tests/testSignals.cc
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
#include <iostream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include "../FbTk/Signal.hh"
|
||||||
|
#include "../FbTk/MemFun.hh"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct NoArgument {
|
||||||
|
void operator() () const {
|
||||||
|
cout << "No Argument." << endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OneArgument {
|
||||||
|
void operator ()( int value ) {
|
||||||
|
cout << "One argument = " << value << endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TwoArguments {
|
||||||
|
void operator ()( int value, const string& message ) {
|
||||||
|
cout << "Two arguments, (1) = " << value << ", (2) = " << message << endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ThreeArguments {
|
||||||
|
void operator ()( int value, const string& message, double value2 ) {
|
||||||
|
cout << "Two arguments, (1) = " << value << ", (2) = " << message
|
||||||
|
<< ", (3) = " << value2 << endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FunctionClass {
|
||||||
|
FunctionClass() {
|
||||||
|
cout << "FunctionClass created." << endl;
|
||||||
|
}
|
||||||
|
~FunctionClass() {
|
||||||
|
cout << "FunctionClass deleted." << endl;
|
||||||
|
}
|
||||||
|
void print() {
|
||||||
|
cout << "Printing." << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void takeIt( string& str ) {
|
||||||
|
cout << "takeIt( " << str << ")" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void showMessage( int value, const string& message ) {
|
||||||
|
cout << "(" << value << "): " << message << endl;
|
||||||
|
}
|
||||||
|
void threeArgs( int value, const string& str, double pi ) {
|
||||||
|
cout << "(" << value << "): " << str << ", pi = " << pi << endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
using FbTk::Signal;
|
||||||
|
using FbTk::SignalTracker;
|
||||||
|
|
||||||
|
Signal<void> no_arg;
|
||||||
|
no_arg.connect( NoArgument() );
|
||||||
|
|
||||||
|
Signal<void, int> one_arg;
|
||||||
|
one_arg.connect( OneArgument() );
|
||||||
|
|
||||||
|
Signal<void, int, const string&> two_args;
|
||||||
|
two_args.connect( TwoArguments() );
|
||||||
|
|
||||||
|
Signal<void, int, const string&, double> three_args;
|
||||||
|
three_args.connect( ThreeArguments() );
|
||||||
|
|
||||||
|
// emit test
|
||||||
|
no_arg.emit();
|
||||||
|
one_arg.emit( 10 );
|
||||||
|
two_args.emit( 10, "Message" );
|
||||||
|
three_args.emit( 10, "Three", 3.141592 );
|
||||||
|
|
||||||
|
// test signal tracker
|
||||||
|
{
|
||||||
|
cout << "---- tracker ----" << endl;
|
||||||
|
SignalTracker tracker;
|
||||||
|
// setup two new slots and track them
|
||||||
|
SignalTracker::TrackID id_no_arg = tracker.join( no_arg, NoArgument() );
|
||||||
|
SignalTracker::TrackID id_one_arg = tracker.join( one_arg, OneArgument() );
|
||||||
|
|
||||||
|
// two outputs each from these two signals
|
||||||
|
no_arg.emit();
|
||||||
|
one_arg.emit( 31 );
|
||||||
|
|
||||||
|
// stop tracking id_one_arg, which should keep the slot after this scope,
|
||||||
|
// the id_no_arg connection should be destroyed after this.
|
||||||
|
tracker.leave( id_one_arg );
|
||||||
|
cout << "---- tracker end ----" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we should have one output from no_arg and two outputs from one_arg
|
||||||
|
no_arg.emit();
|
||||||
|
one_arg.emit( 2 );
|
||||||
|
|
||||||
|
using FbTk::MemFun;
|
||||||
|
FunctionClass obj;
|
||||||
|
no_arg.clear();
|
||||||
|
no_arg.connect(MemFun(obj, &FunctionClass::print));
|
||||||
|
no_arg.emit();
|
||||||
|
|
||||||
|
string takeThis("Take this");
|
||||||
|
Signal<void, string&> ref_arg;
|
||||||
|
ref_arg.connect(MemFun(obj, &FunctionClass::takeIt));
|
||||||
|
ref_arg.emit( takeThis );
|
||||||
|
|
||||||
|
two_args.clear();
|
||||||
|
two_args.connect(MemFun(obj, &FunctionClass::showMessage));
|
||||||
|
two_args.emit(10, "This is a message");
|
||||||
|
|
||||||
|
three_args.clear();
|
||||||
|
three_args.connect(MemFun(obj, &FunctionClass::threeArgs));
|
||||||
|
three_args.emit(9, "nine", 3.141592);
|
||||||
|
}
|
Loading…
Reference in a new issue