Skip to content

Commit 560f800

Browse files
committed
Add trackable_signal_with_accumulator and trackable_signal
trackable_signal_with_accumulator derives from trackable. A slot made with trackable_signal_with_accumulator::make_slot() is automatically disconnected when the signal is deleted, as in sigc++2. Fixes #80
1 parent c07d75d commit 560f800

File tree

4 files changed

+308
-12
lines changed

4 files changed

+308
-12
lines changed

sigc++/signal.h

Lines changed: 285 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ struct signal_emit<void, void, T_arg...>
372372
} /* namespace internal */
373373

374374
/** Signal declaration.
375-
* signal_with_accumulator can be used to connect() slots that are invoked
375+
* %signal_with_accumulator can be used to connect() slots that are invoked
376376
* during subsequent calls to emit(). Any functor or slot
377377
* can be passed into connect(). It is converted into a slot
378378
* implicitly.
@@ -462,10 +462,11 @@ class signal_with_accumulator : public signal_base
462462

463463
/** Creates a functor that calls emit() on this signal.
464464
*
465-
* @note %sigc::signal does not derive from sigc::trackable in sigc++3.
466-
* If you connect the returned functor (calling %emit() on signal1) to
467-
* another signal (signal2) and then delete signal1, you must manually
465+
* @note %sigc::signal does not derive from sigc::trackable.
466+
* If you connect the returned functor that calls %emit() on signal1,
467+
* to another signal (signal2) and then delete signal1, you must manually
468468
* disconnect signal1 from signal2 before you delete signal1.
469+
* Alternatively, make a slot of a sigc::trackable_signal.
469470
*
470471
* @code
471472
* sigc::mem_fun(mysignal, &sigc::signal_with_accumulator::emit)
@@ -517,7 +518,8 @@ class signal_with_accumulator : public signal_base
517518
*
518519
* The template arguments determine the function signature of
519520
* the emit() function:
520-
* - @e T_return The desired return type of the emit() function. * - @e T_arg Argument types used in
521+
* - @e T_return The desired return type of the emit() function.
522+
* - @e T_arg Argument types used in
521523
* the definition of emit().
522524
*
523525
* For instance, to declare a signal whose connected slot returns void and takes
@@ -531,7 +533,7 @@ class signal_with_accumulator : public signal_base
531533
* @par Example:
532534
* @code
533535
* void foo(int) {}
534-
* sigc::signal<void, long> sig;
536+
* sigc::signal<void(long)> sig;
535537
* sig.connect(sigc::ptr_fun(&foo));
536538
* sig.emit(19);
537539
* @endcode
@@ -625,6 +627,283 @@ class signal<T_return(T_arg...)> : public signal_with_accumulator<T_return, void
625627
}
626628
};
627629

630+
// TODO: When we can break ABI, let signal_base derive from trackable, as in sigc++2,
631+
// and delete trackable_signal_with_accumulator and trackable_signal.
632+
// https://github.com/libsigcplusplus/libsigcplusplus/issues/80
633+
634+
/** Signal declaration.
635+
* %trackable_signal_with_accumulator can be used to connect() slots that are invoked
636+
* during subsequent calls to emit(). Any functor or slot
637+
* can be passed into connect(). It is converted into a slot implicitly.
638+
*
639+
* If you want to connect one signal to another, use make_slot()
640+
* to retrieve a functor that emits the signal when invoked.
641+
*
642+
* Be careful if you directly pass one signal into the connect()
643+
* method of another: a shallow copy of the signal is made and
644+
* the signal's slots are not disconnected until both the signal
645+
* and its clone are destroyed, which is probably not what you want!
646+
*
647+
* The following template arguments are used:
648+
* - @e T_return The desired return type for the emit() function (may be overridden by the
649+
* accumulator).
650+
* - @e T_arg Argument types used in the definition of emit().
651+
* - @e T_accumulator The accumulator type used for emission. The default
652+
* @p void means that no accumulator should be used, for example if signal
653+
* emission returns the return value of the last slot invoked.
654+
*
655+
* @newin{3,4}
656+
*
657+
* @ingroup signal
658+
*/
659+
template<typename T_return, typename T_accumulator, typename... T_arg>
660+
class trackable_signal_with_accumulator
661+
: public signal_base
662+
, public trackable
663+
{
664+
public:
665+
using slot_type = slot<T_return(T_arg...)>;
666+
667+
/** Add a slot to the list of slots.
668+
* Any functor or slot may be passed into connect().
669+
* It will be converted into a slot implicitly.
670+
* The returned connection may be stored for disconnection
671+
* of the slot at some later point. It stays valid until
672+
* the slot is disconnected from the signal.
673+
* std::function<> and C++11 lambda expressions are functors.
674+
* These are examples of functors that can be connected to a signal.
675+
*
676+
* %std::bind() creates a functor, but this functor typically has an
677+
* %operator()() which is a variadic template.
678+
* Our functor_trait can't deduce the result type
679+
* of such a functor. If you first assign the return value of %std::bind()
680+
* to a std::function, you can connect the std::function to a signal.
681+
*
682+
* @param slot_ The slot to add to the list of slots.
683+
* @return A connection.
684+
*/
685+
connection connect(const slot_type& slot_)
686+
{
687+
auto iter = signal_base::connect(slot_);
688+
auto& slot_base = *iter;
689+
return connection(slot_base);
690+
}
691+
692+
/** Add a slot to the list of slots.
693+
* @see connect(const slot_type& slot_).
694+
*/
695+
connection connect(slot_type&& slot_)
696+
{
697+
auto iter = signal_base::connect(std::move(slot_));
698+
auto& slot_base = *iter;
699+
return connection(slot_base);
700+
}
701+
702+
/** Triggers the emission of the signal.
703+
* During signal emission all slots that have been connected
704+
* to the signal are invoked unless they are manually set into
705+
* a blocking state. The parameters are passed on to the slots.
706+
* If @e T_accumulated is not @p void, an accumulator of this type
707+
* is used to process the return values of the slot invocations.
708+
* Otherwise, the return value of the last slot invoked is returned.
709+
* @param a Arguments to be passed on to the slots.
710+
* @return The accumulated return values of the slot invocations.
711+
*/
712+
decltype(auto) emit(type_trait_take_t<T_arg>... a) const
713+
{
714+
using emitter_type = internal::signal_emit<T_return, T_accumulator, T_arg...>;
715+
return emitter_type::emit(impl_, std::forward<type_trait_take_t<T_arg>>(a)...);
716+
}
717+
718+
/** Triggers the emission of the signal (see emit()). */
719+
decltype(auto) operator()(type_trait_take_t<T_arg>... a) const
720+
{
721+
return emit(std::forward<type_trait_take_t<T_arg>>(a)...);
722+
}
723+
724+
/** Creates a functor that calls emit() on this signal.
725+
*
726+
* @code
727+
* sigc::mem_fun(mysignal, &sigc::trackable_signal_with_accumulator::emit)
728+
* @endcode
729+
* yields the same result.
730+
* @return A functor that calls emit() on this signal.
731+
*/
732+
decltype(auto) make_slot() const
733+
{
734+
// TODO: Instead use std::invoke_result<> on the static emitter_type::emit()
735+
using result_type = typename internal::member_method_result<
736+
decltype(&trackable_signal_with_accumulator::emit)>::type;
737+
return bound_mem_functor<result_type (trackable_signal_with_accumulator::*)(
738+
type_trait_take_t<T_arg>...) const,
739+
type_trait_take_t<T_arg>...>(*this, &trackable_signal_with_accumulator::emit);
740+
}
741+
742+
trackable_signal_with_accumulator() = default;
743+
744+
trackable_signal_with_accumulator(const trackable_signal_with_accumulator& src)
745+
: signal_base(src), trackable(src)
746+
{
747+
}
748+
749+
trackable_signal_with_accumulator(trackable_signal_with_accumulator&& src)
750+
: signal_base(std::move(src)), trackable(std::move(src))
751+
{
752+
}
753+
754+
trackable_signal_with_accumulator& operator=(const trackable_signal_with_accumulator& src)
755+
{
756+
signal_base::operator=(src);
757+
// Don't call trackable::operator=(src).
758+
// It calls notify_callbacks(). This signal is not destroyed.
759+
return *this;
760+
}
761+
762+
trackable_signal_with_accumulator& operator=(trackable_signal_with_accumulator&& src)
763+
{
764+
signal_base::operator=(std::move(src));
765+
if (src.impl_ != impl_)
766+
src.notify_callbacks();
767+
// Don't call trackable::operator=(std::move(src)).
768+
// It calls notify_callbacks(). This signal is not destroyed.
769+
return *this;
770+
}
771+
};
772+
773+
/** %trackable_signal can be used to connect() slots that are invoked
774+
* during subsequent calls to emit(). Any functor or slot
775+
* can be passed into connect(). It is converted into a slot
776+
* implicitly.
777+
*
778+
* If you want to connect one signal to another, use make_slot()
779+
* to retrieve a functor that emits the signal when invoked.
780+
*
781+
* Be careful if you directly pass one signal into the connect()
782+
* method of another: a shallow copy of the signal is made and
783+
* the signal's slots are not disconnected until both the signal
784+
* and its clone are destroyed, which is probably not what you want!
785+
*
786+
* The template arguments determine the function signature of
787+
* the emit() function:
788+
* - @e T_return The desired return type of the emit() function.
789+
* - @e T_arg Argument types used in
790+
* the definition of emit().
791+
*
792+
* For instance, to declare a %trackable_signal whose connected slot returns void and takes
793+
* two parameters of bool and int:
794+
* @code
795+
* sigc::trackable_signal<void(bool, int)> some_signal;
796+
* @endcode
797+
*
798+
* To specify an accumulator type the nested class trackable_signal::accumulated can be used.
799+
*
800+
* @par Example:
801+
* @code
802+
* void foo(int) {}
803+
* sigc::trackable_signal<void(long)> sig;
804+
* sig.connect(sigc::ptr_fun(&foo));
805+
* sig.emit(19);
806+
* @endcode
807+
*
808+
* @newin{3,4}
809+
*
810+
* @ingroup signal
811+
*/
812+
#ifndef DOXYGEN_SHOULD_SKIP_THIS
813+
template<typename T_return, typename... T_arg>
814+
class trackable_signal;
815+
#endif // DOXYGEN_SHOULD_SKIP_THIS
816+
817+
template<typename T_return, typename... T_arg>
818+
class trackable_signal<T_return(T_arg...)>
819+
: public trackable_signal_with_accumulator<T_return, void, T_arg...>
820+
{
821+
public:
822+
using accumulator_type = void;
823+
824+
/** Like @ref sigc::trackable_signal<T_return(T_arg...)> "sigc::trackable_signal"
825+
* but the additional template parameter @e T_accumulator defines the accumulator
826+
* type that should be used.
827+
*
828+
* An accumulator is a functor that uses a pair of special iterators
829+
* to step through a list of slots and calculate a return value
830+
* from the results of the slot invocations. The iterators' operator*()
831+
* executes the slot. The return value is buffered, so that in an expression
832+
* like @code a = (*i) * (*i); @endcode the slot is executed only once.
833+
*
834+
* @par Example 1:
835+
* This accumulator calculates the arithmetic mean value:
836+
* @code
837+
* struct arithmetic_mean_accumulator
838+
* {
839+
* template<typename T_iterator>
840+
* double operator()(T_iterator first, T_iterator last) const
841+
* {
842+
* double value_ = 0;
843+
* int n_ = 0;
844+
* for (; first != last; ++first, ++n_)
845+
* value_ += *first;
846+
* return value_ / n_;
847+
* }
848+
* };
849+
* @endcode
850+
*
851+
* @par Example 2:
852+
* This accumulator stops signal emission when a slot returns zero:
853+
* @code
854+
* struct interruptable_accumulator
855+
* {
856+
* template<typename T_iterator>
857+
* bool operator()(T_iterator first, T_iterator last) const
858+
* {
859+
* for (; first != last; ++first, ++n_)
860+
* if (!*first) return false;
861+
* return true;
862+
* }
863+
* };
864+
* @endcode
865+
*
866+
* @newin{3,4}
867+
*
868+
* @ingroup signal
869+
*/
870+
template<typename T_accumulator>
871+
class accumulated : public trackable_signal_with_accumulator<T_return, T_accumulator, T_arg...>
872+
{
873+
public:
874+
accumulated() = default;
875+
accumulated(const accumulated& src)
876+
: trackable_signal_with_accumulator<T_return, T_accumulator, T_arg...>(src)
877+
{
878+
}
879+
};
880+
881+
trackable_signal() = default;
882+
883+
trackable_signal(const trackable_signal& src)
884+
: trackable_signal_with_accumulator<T_return, accumulator_type, T_arg...>(src)
885+
{
886+
}
887+
888+
trackable_signal(trackable_signal&& src)
889+
: trackable_signal_with_accumulator<T_return, accumulator_type, T_arg...>(std::move(src))
890+
{
891+
}
892+
893+
trackable_signal& operator=(const trackable_signal& src)
894+
{
895+
trackable_signal_with_accumulator<T_return, accumulator_type, T_arg...>::operator=(src);
896+
return *this;
897+
}
898+
899+
trackable_signal& operator=(trackable_signal&& src)
900+
{
901+
trackable_signal_with_accumulator<T_return, accumulator_type, T_arg...>::operator=(
902+
std::move(src));
903+
return *this;
904+
}
905+
};
906+
628907
} /* namespace sigc */
629908

630909
#endif /* SIGC_SIGNAL_H */

sigc++/signal_base.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,10 @@ struct SIGC_API signal_impl_holder
266266
* @ref sigc::signal_with_accumulator::connect() "sigc::signal::connect()".
267267
*/
268268

269-
// TODO: When we can break ABI, let signal_base derive from trackable again.
270-
// It does in sigc++2. Otherwise the slot returned from signal::make_slot()
269+
// TODO: When we can break ABI, let signal_base derive from trackable again,
270+
// as in sigc++2. Otherwise the slot returned from signal::make_slot()
271271
// is not automatically disconnected when the signal is deleted.
272+
// And delete trackable_signal_with_accumulator and trackable_signal.
272273
// https://github.com/libsigcplusplus/libsigcplusplus/issues/80
273274

274275
/** Base class for the @ref sigc::signal<T_return(T_arg...)> "sigc::signal" template.

tests/test_accumulated.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,11 @@ test_empty_signal()
7979
util->check_result(result_stream, "Vector result (empty slot list): empty");
8080
}
8181

82+
template<typename T_signal>
8283
void
8384
test_mean()
8485
{
85-
sigc::signal<int(int)>::accumulated<arithmetic_mean_accumulator> sig;
86+
typename T_signal::accumulated<arithmetic_mean_accumulator> sig;
8687

8788
A a;
8889
sig.connect(sigc::ptr_fun(&foo));
@@ -137,7 +138,8 @@ main(int argc, char* argv[])
137138
return util->get_result_and_delete_instance() ? EXIT_SUCCESS : EXIT_FAILURE;
138139

139140
test_empty_signal();
140-
test_mean();
141+
test_mean<sigc::signal<int(int)>>();
142+
test_mean<sigc::trackable_signal<int(int)>>();
141143
test_vector_accumulator();
142144

143145
return util->get_result_and_delete_instance() ? EXIT_SUCCESS : EXIT_FAILURE;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy