From e4d4717703b365bc14f189bf36b3edb1e4430b90 Mon Sep 17 00:00:00 2001 From: Henrik Kinnunen Date: Thu, 18 Sep 2008 22:24:35 +0200 Subject: 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. --- ChangeLog | 5 + src/FbTk/Makefile.am | 1 + src/FbTk/MemFun.hh | 139 ++++++++++++++++++++++ src/FbTk/Signal.hh | 218 +++++++++++++++++++++++++++++++++++ src/FbTk/Slot.hh | 294 +++++++++++++++++++++++++++++++++++++++++++++++ src/tests/Makefile | 6 +- src/tests/testSignals.cc | 121 +++++++++++++++++++ 7 files changed, 783 insertions(+), 1 deletion(-) create mode 100644 src/FbTk/MemFun.hh create mode 100644 src/FbTk/Signal.hh create mode 100644 src/FbTk/Slot.hh create mode 100644 src/tests/testSignals.cc diff --git a/ChangeLog b/ChangeLog index 94da1ba..da15ff6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,9 @@ (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 *08/09/14: * Fixed a minor pixmap resource leak (Henrik) diff --git a/src/FbTk/Makefile.am b/src/FbTk/Makefile.am index 0033ab9..2d2f3a2 100644 --- a/src/FbTk/Makefile.am +++ b/src/FbTk/Makefile.am @@ -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 \ Select2nd.hh STLUtil.hh \ CachedPixmap.hh CachedPixmap.cc \ + Slot.hh Signal.hh MemFun.hh \ ${xpm_SOURCE} \ ${xft_SOURCE} \ ${xmb_SOURCE} \ diff --git a/src/FbTk/MemFun.hh b/src/FbTk/MemFun.hh new file mode 100644 index 0000000..4c834dd --- /dev/null +++ b/src/FbTk/MemFun.hh @@ -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 +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 +MemFun0 +MemFun( Object& obj, ReturnType (Object:: *action)() ) { + return MemFun0(obj, action); +} + +/// One argument functor +template +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 +MemFun1 +MemFun( Object& obj, ReturnType (Object:: *action)(Arg1) ) { + return MemFun1(obj, action); +} + +/// Two argument functor +template +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 +MemFun2 +MemFun( Object& obj, ReturnType (Object:: *action)(Arg1,Arg2) ) { + return MemFun2(obj, action); +} + +/// Three argument functor +template +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 +MemFun3 +MemFun( Object& obj, ReturnType (Object:: *action)(Arg1, Arg2, Arg3) ) { + return MemFun3(obj, action); +} + +} // namespace FbTk + +#endif // FBTK_MEM_FUN_HH + diff --git a/src/FbTk/Signal.hh b/src/FbTk/Signal.hh new file mode 100644 index 0000000..768ca90 --- /dev/null +++ b/src/FbTk/Signal.hh @@ -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 +#include + +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 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 +class Signal0: public SignalHolder { +public: + typedef Slot0 SlotType; + + void emit() { + for ( Iterator it = begin(); it != end(); ++it ) { + static_cast(*it)(); + } + } + + SlotID connect(const SlotType& slot) { + return SignalHolder::connect(slot); + + } +}; + +/// Signal with one argument +template +class Signal1: public SignalHolder { +public: + typedef Slot1 SlotType; + + void emit(Arg1 arg) { + for ( Iterator it = begin(); it != end(); ++it ) { + static_cast(*it)(arg); + } + } + + SlotID connect(const SlotType& slot) { + return SignalHolder::connect(slot); + } + +}; + +/// Signal with two arguments +template +class Signal2: public SignalHolder { +public: + typedef Slot2 SlotType; + + void emit(Arg1 arg1, Arg2 arg2) { + for ( Iterator it = begin(); it != end(); ++it ) { + static_cast(*it)(arg1, arg2); + } + } + + SlotID connect(const SlotType& slot) { + return SignalHolder::connect(slot); + } +}; + +/// Signal with three arguments +template +class Signal3: public SignalHolder { +public: + typedef Slot3 SlotType; + + void emit(Arg1 arg1, Arg2 arg2, Arg3 arg3) { + for ( Iterator it = begin(); it != end(); ++it ) { + static_cast(*it)(arg1, arg2, arg3); + } + } + + SlotID connect(const SlotType& slot) { + return SignalHolder::connect(slot); + } + +}; + +struct EmptyArg {}; + +} // namespace SigImpl + + +/// Specialization for three arguments. +template +class Signal: public SigImpl::Signal3< ReturnType, Arg1, Arg2, Arg3 > { +public: +}; + +/// Specialization for two arguments. +template +class Signal: public SigImpl::Signal2< ReturnType, Arg1, Arg2 > { +public: +}; + +/// Specialization for one argument. +template +class Signal: public SigImpl::Signal1< ReturnType, Arg1 > { +public: +}; + +/// Specialization for no argument. +template +class Signal: 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 + 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 diff --git a/src/FbTk/Slot.hh b/src/FbTk/Slot.hh new file mode 100644 index 0000000..ba3cfc4 --- /dev/null +++ b/src/FbTk/Slot.hh @@ -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 +class FunctorHolder: public CallbackHolder { +public: + /// This type. + typedef FunctorHolder 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)); + } + + static void kill(CallbackHolder* self) { + // Destroy functor + static_cast( self )->m_functor.~Functor(); + } + + Functor m_functor; ///< the functor to use when a signal is emitted. +}; + + + +/// Callback with no arguments. +template +struct Callback0 { + static ReturnType callback(CallbackHolder* base) { + static_cast< FunctorHolder* >( base )->m_functor(); + return ReturnType(); + } + + static CallbackFunc functionAddress() { + return reinterpret_cast(&callback); + } +}; + +/// Callback with one argument +template +struct Callback1 { + typedef ReturnType (Functor::* CallbackType)(CallbackHolder*, Arg1); + + static ReturnType callback(CallbackHolder* base, Arg1 arg1) { + static_cast< FunctorHolder* >( base )->m_functor(arg1); + return ReturnType(); + } + + static CallbackFunc functionAddress() { + return reinterpret_cast(&callback); + } +}; + +/// Callback with two arguments +template +struct Callback2 { + typedef ReturnType (Functor::* CallbackType)(CallbackHolder*, Arg1, Arg2); + + static ReturnType callback(CallbackHolder* base, Arg1 arg1, Arg2 arg2) { + static_cast< FunctorHolder* >( base )->m_functor(arg1, arg2); + return ReturnType(); + } + + static CallbackFunc functionAddress() { + return reinterpret_cast(&callback); + } +}; + +/// Callback with three arguments +template +struct Callback3 { + typedef ReturnType (Functor::* CallbackType)(CallbackHolder*, Arg1, Arg2, Arg3); + + static ReturnType callback(CallbackHolder* base, Arg1 arg1, Arg2 arg2, Arg3 arg3) { + static_cast< FunctorHolder* >( base )->m_functor( arg1, arg2, arg3 ); + return ReturnType(); + } + + static CallbackFunc functionAddress() { + return reinterpret_cast(&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 +class Slot0: public SlotHolder { +public: + typedef ReturnType (*CallbackType)(CallbackHolder*); + + template + Slot0( const Functor& functor ): + SlotHolder( new FunctorHolder + (functor, Callback0::functionAddress())) { + } + + void operator()() { + reinterpret_cast(m_holder->m_callback)( m_holder ); + } +}; + +/// Slot with one argument. +template +class Slot1:public SlotHolder { +public: + typedef ReturnType (*CallbackType)(CallbackHolder*, Arg1); + + template + Slot1( const Functor& functor ): + SlotHolder( new FunctorHolder + (functor, Callback1::functionAddress())){ + + } + + void operator()(Arg1 arg) { + reinterpret_cast(m_holder->m_callback)(m_holder, arg); + } + +}; + +/// Slot with two arguments +template +class Slot2: public SlotHolder { +public: + typedef ReturnType (*CallbackType)(CallbackHolder*, Arg1, Arg2); + template + Slot2( const Functor& functor ): + SlotHolder( new FunctorHolder + (functor, Callback2::functionAddress())){ + + } + + void operator()(Arg1 arg1, Arg2 arg2) { + reinterpret_cast(m_holder->m_callback)(m_holder, arg1, arg2); + } +}; + +/// Slot with three arguments +template +class Slot3: public SlotHolder { +public: + typedef ReturnType (*CallbackType)(CallbackHolder*, Arg1, Arg2, Arg3); + template + Slot3( const Functor& functor ): + SlotHolder( new FunctorHolder + (functor, Callback3::functionAddress())){ + + } + + void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3) { + reinterpret_cast(m_holder->m_callback) + ( m_holder, arg1, arg2, arg3 ); + } +}; + +} // namespace SigImpl + +} // namespace FbTk + +#endif // FBTK_SLOT_H diff --git a/src/tests/Makefile b/src/tests/Makefile index 8d1c482..668126e 100644 --- a/src/tests/Makefile +++ b/src/tests/Makefile @@ -7,7 +7,8 @@ COMPILEFILE=$(CXX) -c $(CXXFLAGS) FONT_OBJ = ../FbTk/libFbTk.a COMPILE = ${CXX} ${CXXFLAGS} ${XLIBS} -all: testMenu testFont testTexture movetest +all: testMenu testFont testTexture movetest testSignals + .cc.o: $(CXX) -c $(CXXFLAGS) $< @@ -15,6 +16,9 @@ all: testMenu testFont testTexture movetest glxtest: ../FbTk/App.hh glxtest.cc ${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 $(CXX) $(LIBS) StringUtiltest.o ../FbTk/libFbTk.a -o testStringUtil diff --git a/src/tests/testSignals.cc b/src/tests/testSignals.cc new file mode 100644 index 0000000..86096bf --- /dev/null +++ b/src/tests/testSignals.cc @@ -0,0 +1,121 @@ +#include +using namespace std; + +#include "../FbTk/Signal.hh" +#include "../FbTk/MemFun.hh" + +#include + + + +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 no_arg; + no_arg.connect( NoArgument() ); + + Signal one_arg; + one_arg.connect( OneArgument() ); + + Signal two_args; + two_args.connect( TwoArguments() ); + + Signal 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 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); +} -- cgit v0.11.2