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

Umang Jain umang.jain at ideasonboard.com
Tue Aug 31 12:41:52 CEST 2021


Hi Laurent,

On 8/31/21 3:02 PM, Laurent Pinchart wrote:
> 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...))


Ah yeah, duh me!

>
> 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.

Got it.

Reviewed-by: Umang Jain <umang.jain at ideasonboard.com>

thanks for clarifications :D

>
>> 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) -------- */
>>>    
>>>    		/*


More information about the libcamera-devel mailing list