From patchwork Fri Aug 27 02:38:27 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 13525 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 48D3FBD87D for ; Fri, 27 Aug 2021 02:38:52 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E0DED68928; Fri, 27 Aug 2021 04:38:51 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="quB1niBk"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3852268932 for ; Fri, 27 Aug 2021 04:38:47 +0200 (CEST) Received: from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D09B8739 for ; Fri, 27 Aug 2021 04:38:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1630031927; bh=cs4NVSKpxfneJ2591N2rLe10LLTCB1h3oRmVFwa8SjM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=quB1niBkCj2TxxANOpxwtnTO/q0tBJxllYyMpnsadlLtsZ5+Jr03F+JPzNuT0PJKu gLrW5E9u7j5CnPDeSMwwIRNK3o5W+GjYScYZhlY+4DDgr7DDBvbFHXE9bOS6Pfy8oZ ZNEZDXl24mwCaMrlH3p9iKXYWfkSaO9mmC02DJMM= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Fri, 27 Aug 2021 05:38:27 +0300 Message-Id: <20210827023829.5871-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210827023829.5871-1-laurent.pinchart@ideasonboard.com> References: <20210827023829.5871-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v1 4/6] libcamera: base: signal: Support connecting signals to functors X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 dc03cbea4b02..d562510e902e 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 76ce8017e721..ebd297ab8209 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 c2521769a843..8d9f82f62d0d 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 adcfa796870e..9c2319c59106 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 595782a2cd6e..fcf2def18df4 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) -------- */ /*