[libcamera-devel] [PATCH v1 4/6] libcamera: base: signal: Support connecting signals to functors

Laurent Pinchart laurent.pinchart at ideasonboard.com
Tue Aug 31 11:32:13 CEST 2021


Hi Umang,

On Tue, Aug 31, 2021 at 11:32:41AM +0530, Umang Jain wrote:
> Hi Laurent,
> 
> Thanks for the patch. I have a few questions regarding the documentation
> 
> On 8/27/21 8:08 AM, Laurent Pinchart wrote:
> > 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 <laurent.pinchart at ideasonboard.com>
> > ---
> >   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<typename T, typename R, typename Func, typename... Args>
> > +class BoundMethodFunctor : public BoundMethodArgs<R, Args...>
> > +{
> > +public:
> > +	using PackType = typename BoundMethodArgs<R, Args...>::PackType;
> > +
> > +	BoundMethodFunctor(T *obj, Object *object, Func func,
> > +			   ConnectionType type = ConnectionTypeAuto)
> > +		: BoundMethodArgs<R, Args...>(obj, object, type), func_(func)
> > +	{
> > +	}
> > +
> > +	R activate(Args... args, bool deleteMethod = false) override
> > +	{
> > +		if (!this->object_)
> > +			return func_(args...);
> > +
> > +		auto pack = std::make_shared<PackType>(args...);
> > +		bool sync = BoundMethodBase::activatePack(pack, deleteMethod);
> > +		return sync ? pack->returnValue() : R();
> > +	}
> > +
> > +	R invoke(Args... args) override
> > +	{
> > +		return func_(args...);
> > +	}
> > +
> > +private:
> > +	Func func_;
> > +};
> > +
> >   template<typename T, typename R, typename... Args>
> >   class BoundMethodMember : public BoundMethodArgs<R, Args...>
> >   {
> > 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<T, R, Args...>(obj, nullptr, func));
> >   	}
> >   
> > +#ifndef __DOXYGEN__
> > +	template<typename T, typename Func,
> > +		 typename std::enable_if_t<std::is_base_of<Object, T>::value> * = nullptr>
> > +	void connect(T *obj, Func func, ConnectionType type = ConnectionTypeAuto)
> > +	{
> > +		Object *object = static_cast<Object *>(obj);
> > +		SignalBase::connect(new BoundMethodFunctor<T, void, Func, Args...>(obj, object, func, type));
> > +	}
> > +
> > +	template<typename T, typename Func,
> > +		 typename std::enable_if_t<!std::is_base_of<Object, T>::value> * = nullptr>
> > +#else
> > +	template<typename T, typename Func>
> > +#endif
> > +	void connect(T *obj, Func func)
> > +	{
> > +		SignalBase::connect(new BoundMethodFunctor<T, void, Func, Args...>(obj, nullptr, func));
> > +	}
> > +
> >   	template<typename R>
> >   	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.
> 
> So, if I have a instance _not_ inherited from Object, how would I 
> manually disconnect the signal ?

With one of the disconnect() functions of the signal.

> > + *
> > + * 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.
> 
> I am not sure if I am understanding this correctly, but do you mean that 
> the functor version of Signal, should essentially be used within a class 
> inherited from Object /only/?

No, what it means is that

	Signal::connect(T *object, R T::*func)(Args...))

has a matching

	Signal::disconnect(T *object, R (T::*func)(Args...))

function, but

	Signal::connect(T *object, Func func)

doesn't have

	Signal::disconnect(T *object, Func func)

so the

	Signal::disconnect(T *object)

function should be used instead.

> I ask this because, i the previous paragraph there is:
> 
> 	+ * Otherwise the caller shall disconnect signals manually before destroying \a
> 	+ * object.
> 
> The 'Otherwise' seems to address the classes, that are not inherit from 
> Object. It's stated that signal within those needs manual disconnection. 
> But 'How?' isn't clear to me.
> 
> The documentation doesn't seem to clarify  to me that how a class _not_ 
> inheriting from Object handle disconnection of the signal? Can you 
> please clarify?
> 
> > + *
> > + * \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) -------- */
> >   
> >   		/*

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list