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 <laurent.pinchart@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
This commit is contained in:
parent
c4e2b00d51
commit
58720e1dc9
5 changed files with 114 additions and 0 deletions
|
@ -878,6 +878,7 @@ EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \
|
||||||
|
|
||||||
EXCLUDE_SYMBOLS = libcamera::BoundMethodArgs \
|
EXCLUDE_SYMBOLS = libcamera::BoundMethodArgs \
|
||||||
libcamera::BoundMethodBase \
|
libcamera::BoundMethodBase \
|
||||||
|
libcamera::BoundMethodFunctor \
|
||||||
libcamera::BoundMethodMember \
|
libcamera::BoundMethodMember \
|
||||||
libcamera::BoundMethodPack \
|
libcamera::BoundMethodPack \
|
||||||
libcamera::BoundMethodPackBase \
|
libcamera::BoundMethodPackBase \
|
||||||
|
|
|
@ -128,6 +128,37 @@ public:
|
||||||
virtual R invoke(Args... args) = 0;
|
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>
|
template<typename T, typename R, typename... Args>
|
||||||
class BoundMethodMember : public BoundMethodArgs<R, Args...>
|
class BoundMethodMember : public BoundMethodArgs<R, Args...>
|
||||||
{
|
{
|
||||||
|
|
|
@ -61,6 +61,25 @@ public:
|
||||||
SignalBase::connect(new BoundMethodMember<T, R, Args...>(obj, nullptr, func));
|
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>
|
template<typename R>
|
||||||
void connect(R (*func)(Args...))
|
void connect(R (*func)(Args...))
|
||||||
{
|
{
|
||||||
|
|
|
@ -121,6 +121,30 @@ SignalBase::SlotList SignalBase::slots()
|
||||||
* \context This function is \threadsafe.
|
* \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...))
|
* \fn Signal::connect(R (*func)(Args...))
|
||||||
* \brief Connect the signal to a static function slot
|
* \brief Connect the signal to a static function slot
|
||||||
|
|
|
@ -191,6 +191,24 @@ protected:
|
||||||
signalVoid_.connect(slotStaticReturn);
|
signalVoid_.connect(slotStaticReturn);
|
||||||
signalVoid_.connect(this, &SignalTest::slotReturn);
|
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 ----------------- */
|
/* ----------------- Signal -> Object tests ----------------- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -256,6 +274,27 @@ protected:
|
||||||
|
|
||||||
delete slotObject;
|
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) -------- */
|
/* --------- Signal -> Object (multiple inheritance) -------- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue