From 58720e1dc98186e79ef4e758a851b58df562f7b4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 27 Aug 2021 04:05:33 +0300 Subject: libcamera: base: signal: Support connecting signals to functors It can be useful to connect a signal to a functor, and in particular a lambda function, while still operating in the context of a receiver object (to support both object-based disconnection and queued connections to Object instances). Add a BoundMethodFunctor class to bind a functor, and a corresponding Signal::connect() function. There is no corresponding disconnect() function, as a lambda passed to connect() can't be later passed to disconnect(). Disconnection typically uses disconnect(T *object), which will cover the vast majority of use cases. Signed-off-by: Laurent Pinchart Reviewed-by: Umang Jain --- Documentation/Doxyfile.in | 1 + include/libcamera/base/bound_method.h | 31 ++++++++++++++++++++++++++++ include/libcamera/base/signal.h | 19 +++++++++++++++++ src/libcamera/base/signal.cpp | 24 +++++++++++++++++++++ test/signal.cpp | 39 +++++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+) diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in index 24ccf0c0..6e627192 100644 --- a/Documentation/Doxyfile.in +++ b/Documentation/Doxyfile.in @@ -878,6 +878,7 @@ EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \ EXCLUDE_SYMBOLS = libcamera::BoundMethodArgs \ libcamera::BoundMethodBase \ + libcamera::BoundMethodFunctor \ libcamera::BoundMethodMember \ libcamera::BoundMethodPack \ libcamera::BoundMethodPackBase \ diff --git a/include/libcamera/base/bound_method.h b/include/libcamera/base/bound_method.h index 76ce8017..ebd297ab 100644 --- a/include/libcamera/base/bound_method.h +++ b/include/libcamera/base/bound_method.h @@ -128,6 +128,37 @@ public: virtual R invoke(Args... args) = 0; }; +template +class BoundMethodFunctor : public BoundMethodArgs +{ +public: + using PackType = typename BoundMethodArgs::PackType; + + BoundMethodFunctor(T *obj, Object *object, Func func, + ConnectionType type = ConnectionTypeAuto) + : BoundMethodArgs(obj, object, type), func_(func) + { + } + + R activate(Args... args, bool deleteMethod = false) override + { + if (!this->object_) + return func_(args...); + + auto pack = std::make_shared(args...); + bool sync = BoundMethodBase::activatePack(pack, deleteMethod); + return sync ? pack->returnValue() : R(); + } + + R invoke(Args... args) override + { + return func_(args...); + } + +private: + Func func_; +}; + template class BoundMethodMember : public BoundMethodArgs { diff --git a/include/libcamera/base/signal.h b/include/libcamera/base/signal.h index c2521769..8d9f82f6 100644 --- a/include/libcamera/base/signal.h +++ b/include/libcamera/base/signal.h @@ -61,6 +61,25 @@ public: SignalBase::connect(new BoundMethodMember(obj, nullptr, func)); } +#ifndef __DOXYGEN__ + template::value> * = nullptr> + void connect(T *obj, Func func, ConnectionType type = ConnectionTypeAuto) + { + Object *object = static_cast(obj); + SignalBase::connect(new BoundMethodFunctor(obj, object, func, type)); + } + + template::value> * = nullptr> +#else + template +#endif + void connect(T *obj, Func func) + { + SignalBase::connect(new BoundMethodFunctor(obj, nullptr, func)); + } + template void connect(R (*func)(Args...)) { diff --git a/src/libcamera/base/signal.cpp b/src/libcamera/base/signal.cpp index adcfa796..9c2319c5 100644 --- a/src/libcamera/base/signal.cpp +++ b/src/libcamera/base/signal.cpp @@ -121,6 +121,30 @@ SignalBase::SlotList SignalBase::slots() * \context This function is \threadsafe. */ +/** + * \fn Signal::connect(T *object, Func func) + * \brief Connect the signal to a function object slot + * \param[in] object The slot object pointer + * \param[in] func The function object + * + * If the typename T inherits from Object, the signal will be automatically + * disconnected from the \a func slot of \a object when \a object is destroyed. + * Otherwise the caller shall disconnect signals manually before destroying \a + * object. + * + * The function object is typically a lambda function, but may be any object + * that satisfies the FunctionObject named requirements. The types of the + * function object arguments shall match the types of the signal arguments. + * + * No matching disconnect() function exist, as it wouldn't be possible to pass + * to a disconnect() function the same lambda that was passed to connect(). The + * connection created by this function can not be removed selectively if the + * signal is connected to multiple slots of the same receiver, but may be + * otherwise be removed using the disconnect(T *object) function. + * + * \context This function is \threadsafe. + */ + /** * \fn Signal::connect(R (*func)(Args...)) * \brief Connect the signal to a static function slot diff --git a/test/signal.cpp b/test/signal.cpp index 595782a2..fcf2def1 100644 --- a/test/signal.cpp +++ b/test/signal.cpp @@ -191,6 +191,24 @@ protected: signalVoid_.connect(slotStaticReturn); signalVoid_.connect(this, &SignalTest::slotReturn); + /* Test signal connection to a lambda. */ + int value = 0; + signalInt_.connect(this, [&](int v) { value = v; }); + signalInt_.emit(42); + + if (value != 42) { + cout << "Signal connection to lambda failed" << endl; + return TestFail; + } + + signalInt_.disconnect(this); + signalInt_.emit(0); + + if (value != 42) { + cout << "Signal disconnection from lambda failed" << endl; + return TestFail; + } + /* ----------------- Signal -> Object tests ----------------- */ /* @@ -256,6 +274,27 @@ protected: delete slotObject; + /* Test signal connection to a lambda. */ + slotObject = new SlotObject(); + value = 0; + signalInt_.connect(slotObject, [&](int v) { value = v; }); + signalInt_.emit(42); + + if (value != 42) { + cout << "Signal connection to Object lambda failed" << endl; + return TestFail; + } + + signalInt_.disconnect(slotObject); + signalInt_.emit(0); + + if (value != 42) { + cout << "Signal disconnection from Object lambda failed" << endl; + return TestFail; + } + + delete slotObject; + /* --------- Signal -> Object (multiple inheritance) -------- */ /* -- cgit v1.2.1