/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019, Google Inc. * * signal.h - Signal & slot implementation */ #ifndef __LIBCAMERA_SIGNAL_H__ #define __LIBCAMERA_SIGNAL_H__ #include #include #include namespace libcamera { template class Signal; class SlotBase { public: SlotBase(void *obj, bool isObject) : obj_(obj), isObject_(isObject) {} virtual ~SlotBase() {} void *obj() { return obj_; } bool isObject() const { return isObject_; } protected: void *obj_; bool isObject_; }; template class SlotArgs : public SlotBase { public: SlotArgs(void *obj, bool isObject) : SlotBase(obj, isObject) {} virtual void invoke(Args... args) = 0; protected: friend class Signal; }; template class SlotMember : public SlotArgs { public: SlotMember(T *obj, bool isObject, void (T::*func)(Args...)) : SlotArgs(obj, isObject), func_(func) {} void invoke(Args... args) { (static_cast(this->obj_)->*func_)(args...); } private: friend class Signal; void (T::*func_)(Args...); }; template class SlotStatic : public SlotArgs { public: SlotStatic(void (*func)(Args...)) : SlotArgs(nullptr, false), func_(func) {} void invoke(Args... args) { (*func_)(args...); } private: friend class Signal; void (*func_)(Args...); }; class SignalBase { public: template void disconnect(T *object) { for (auto iter = slots_.begin(); iter != slots_.end(); ) { SlotBase *slot = *iter; if (slot->obj() == object) { iter = slots_.erase(iter); delete slot; } else { ++iter; } } } protected: friend class Object; std::list slots_; }; template class Signal : public SignalBase { public: Signal() {} ~Signal() { for (SlotBase *slot : slots_) { if (slot->isObject()) static_cast(slot->obj())->disconnect(this); delete slot; } } #ifndef __DOXYGEN__ template::value>::type * = nullptr> void connect(T *object, void (T::*func)(Args...)) { object->connect(this); slots_.push_back(new SlotMember(object, true, func)); } template::value>::type * = nullptr> #else template #endif void connect(T *object, void (T::*func)(Args...)) { slots_.push_back(new SlotMember(object, false, func)); } void connect(void (*func)(Args...)) { slots_.push_back(new SlotStatic(func)); } void disconnect() { for (SlotBase *slot : slots_) delete slot; slots_.clear(); } template void disconnect(T *object) { SignalBase::disconnect(object); } template void disconnect(T *object, void (T::*func)(Args...)) { for (auto iter = slots_.begin(); iter != slots_.end(); ) { SlotArgs *slot = static_cast *>(*iter); /* * If the obj() pointer matches the object, the slot is * guaranteed to be a member slot, so we can safely * cast it to SlotMember and access its * func_ member. */ if (slot->obj() == object && static_cast *>(slot)->func_ == func) { iter = slots_.erase(iter); delete slot; } else { ++iter; } } } void disconnect(void (*func)(Args...)) { for (auto iter = slots_.begin(); iter != slots_.end(); ) { SlotArgs *slot = *iter; if (slot->obj() == nullptr && static_cast *>(slot)->func_ == func) { iter = slots_.erase(iter); delete slot; } else { ++iter; } } } void emit(Args... args) { /* * Make a copy of the slots list as the slot could call the * disconnect operation, invalidating the iterator. */ std::vector slots{ slots_.begin(), slots_.end() }; for (SlotBase *slot : slots) { static_cast *>(slot)->invoke(args...); } } }; } /* namespace libcamera */ #endif /* __LIBCAMERA_SIGNAL_H__ */