/* 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 #include #include namespace libcamera { template class Signal; class SignalBase; class SlotBase { public: SlotBase(void *obj, Object *object) : obj_(obj), object_(object) {} virtual ~SlotBase() {} template::value>::type * = nullptr> bool match(T *obj) { return obj == obj_; } bool match(Object *object) { return object == object_; } void disconnect(SignalBase *signal); void activatePack(void *pack); virtual void invokePack(void *pack) = 0; protected: void *obj_; Object *object_; }; template class SlotArgs : public SlotBase { private: #ifndef __DOXYGEN__ /* * This is a cheap partial implementation of std::integer_sequence<> * from C++14. */ template struct sequence { }; template struct generator : generator { }; template struct generator<0, S...> { typedef sequence type; }; #endif using PackType = std::tuple::type...>; template void invokePack(void *pack, sequence) { PackType *args = static_cast(pack); invoke(std::get(*args)...); delete args; } public: SlotArgs(void *obj, Object *object) : SlotBase(obj, object) {} void invokePack(void *pack) override { invokePack(pack, typename generator::type()); } virtual void activate(Args... args) = 0; virtual void invoke(Args... args) = 0; }; template class SlotMember : public SlotArgs { public: using PackType = std::tuple::type...>; SlotMember(T *obj, Object *object, void (T::*func)(Args...)) : SlotArgs(obj, object), func_(func) {} void activate(Args... args) { if (this->object_) SlotBase::activatePack(new PackType{ args... }); else (static_cast(this->obj_)->*func_)(args...); } 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, nullptr), func_(func) {} void activate(Args... args) { (*func_)(args...); } void invoke(Args... args) {} private: friend class Signal; void (*func_)(Args...); }; class SignalBase { public: template void disconnect(T *obj) { for (auto iter = slots_.begin(); iter != slots_.end(); ) { SlotBase *slot = *iter; if (slot->match(obj)) { 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_) { slot->disconnect(this); delete slot; } } #ifndef __DOXYGEN__ template::value>::type * = nullptr> void connect(T *obj, void (T::*func)(Args...)) { Object *object = static_cast(obj); object->connect(this); slots_.push_back(new SlotMember(obj, object, func)); } template::value>::type * = nullptr> #else template #endif void connect(T *obj, void (T::*func)(Args...)) { slots_.push_back(new SlotMember(obj, nullptr, 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 *obj) { SignalBase::disconnect(obj); } template void disconnect(T *obj, void (T::*func)(Args...)) { for (auto iter = slots_.begin(); iter != slots_.end(); ) { SlotArgs *slot = static_cast *>(*iter); /* * If the object matches the slot, the slot is * guaranteed to be a member slot, so we can safely * cast it to SlotMember and access its * func_ member. */ if (slot->match(obj) && 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->match(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)->activate(args...); } }; } /* namespace libcamera */ #endif /* __LIBCAMERA_SIGNAL_H__ */