[libcamera-devel,04/18] libcamera: signal: Split Slot implementation to reusable classes

Message ID 20190812124642.24287-5-laurent.pinchart@ideasonboard.com
State Superseded
Headers show
Series
  • Object & Thread enhancements
Related show

Commit Message

Laurent Pinchart Aug. 12, 2019, 12:46 p.m. UTC
Move the Slot* classes to bound_method.{h,cpp} and rename them to
Bound*Method*. They will be reused to implement asynchronous method
invocation similar to cross-thread signal delivery.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 Documentation/Doxyfile.in        |  10 +--
 include/libcamera/bound_method.h | 131 ++++++++++++++++++++++++++++
 include/libcamera/meson.build    |   1 +
 include/libcamera/object.h       |   5 +-
 include/libcamera/signal.h       | 145 ++++---------------------------
 src/libcamera/bound_method.cpp   |  33 +++++++
 src/libcamera/include/message.h  |   8 +-
 src/libcamera/meson.build        |   1 +
 src/libcamera/message.cpp        |   4 +-
 src/libcamera/object.cpp         |   2 +-
 src/libcamera/signal.cpp         |  23 -----
 11 files changed, 197 insertions(+), 166 deletions(-)
 create mode 100644 include/libcamera/bound_method.h
 create mode 100644 src/libcamera/bound_method.cpp

Comments

Jacopo Mondi Aug. 15, 2019, 8:47 a.m. UTC | #1
Hi Laurent,

On Mon, Aug 12, 2019 at 03:46:28PM +0300, Laurent Pinchart wrote:
> Move the Slot* classes to bound_method.{h,cpp} and rename them to
> Bound*Method*. They will be reused to implement asynchronous method
> invocation similar to cross-thread signal delivery.

Stupid question, but why do you need to rename to a longer
"Bound*Method*" when "Slot" was quite efficient as a name? Couldn't
you just move the Slot hierarchy definition and implementation to
their own files?

>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  Documentation/Doxyfile.in        |  10 +--
>  include/libcamera/bound_method.h | 131 ++++++++++++++++++++++++++++
>  include/libcamera/meson.build    |   1 +
>  include/libcamera/object.h       |   5 +-
>  include/libcamera/signal.h       | 145 ++++---------------------------
>  src/libcamera/bound_method.cpp   |  33 +++++++
>  src/libcamera/include/message.h  |   8 +-
>  src/libcamera/meson.build        |   1 +
>  src/libcamera/message.cpp        |   4 +-
>  src/libcamera/object.cpp         |   2 +-
>  src/libcamera/signal.cpp         |  23 -----
>  11 files changed, 197 insertions(+), 166 deletions(-)
>  create mode 100644 include/libcamera/bound_method.h
>  create mode 100644 src/libcamera/bound_method.cpp
>
> diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
> index 3d94623a4b8f..a9596c2a32d8 100644
> --- a/Documentation/Doxyfile.in
> +++ b/Documentation/Doxyfile.in
> @@ -865,11 +865,11 @@ EXCLUDE_PATTERNS       =
>  # Note that the wildcards are matched against the file with absolute path, so to
>  # exclude all test directories use the pattern */test/*
>
> -EXCLUDE_SYMBOLS        = libcamera::SignalBase \
> -                         libcamera::SlotArgs \
> -                         libcamera::SlotBase \
> -                         libcamera::SlotMember \
> -                         libcamera::SlotStatic \
> +EXCLUDE_SYMBOLS        = libcamera::BoundMemberMethod \
> +                         libcamera::BoundMethodArgs \
> +                         libcamera::BoundMethodBase \
> +                         libcamera::BoundStaticMethod \
> +                         libcamera::SignalBase \
>                           std::*
>
>  # The EXAMPLE_PATH tag can be used to specify one or more files or directories
> diff --git a/include/libcamera/bound_method.h b/include/libcamera/bound_method.h
> new file mode 100644
> index 000000000000..38c44b923ba1
> --- /dev/null
> +++ b/include/libcamera/bound_method.h
> @@ -0,0 +1,131 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * bound_method.h - Method bind and invocation
> + */
> +#ifndef __LIBCAMERA_BOUND_METHOD_H__
> +#define __LIBCAMERA_BOUND_METHOD_H__
> +
> +#include <tuple>
> +#include <type_traits>
> +
> +namespace libcamera {
> +
> +class Object;
> +template<typename... Args>
> +class Signal;
> +class SignalBase;
> +
> +class BoundMethodBase
> +{
> +public:
> +	BoundMethodBase(void *obj, Object *object)
> +		: obj_(obj), object_(object) {}
> +	virtual ~BoundMethodBase() {}
> +
> +	template<typename T, typename std::enable_if<!std::is_same<Object, T>::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<typename... Args>
> +class BoundMethodArgs : public BoundMethodBase
> +{
> +private:
> +#ifndef __DOXYGEN__
> +	/*
> +	 * This is a cheap partial implementation of std::integer_sequence<>
> +	 * from C++14.
> +	 */
> +	template<int...>
> +	struct sequence {
> +	};
> +
> +	template<int N, int... S>
> +	struct generator : generator<N-1, N-1, S...> {
> +	};
> +
> +	template<int... S>
> +	struct generator<0, S...> {
> +		typedef sequence<S...> type;
> +	};
> +#endif
> +
> +	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> +
> +	template<int... S>
> +	void invokePack(void *pack, sequence<S...>)
> +	{
> +		PackType *args = static_cast<PackType *>(pack);
> +		invoke(std::get<S>(*args)...);
> +		delete args;
> +	}
> +
> +public:
> +	BoundMethodArgs(void *obj, Object *object)
> +		: BoundMethodBase(obj, object) {}
> +
> +	void invokePack(void *pack) override
> +	{
> +		invokePack(pack, typename generator<sizeof...(Args)>::type());
> +	}
> +
> +	virtual void activate(Args... args) = 0;
> +	virtual void invoke(Args... args) = 0;
> +};
> +
> +template<typename T, typename... Args>
> +class BoundMemberMethod : public BoundMethodArgs<Args...>
> +{
> +public:
> +	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> +
> +	BoundMemberMethod(T *obj, Object *object, void (T::*func)(Args...))
> +		: BoundMethodArgs<Args...>(obj, object), func_(func) {}
> +
> +	void activate(Args... args)
> +	{
> +		if (this->object_)
> +			BoundMethodBase::activatePack(new PackType{ args... });
> +		else
> +			(static_cast<T *>(this->obj_)->*func_)(args...);
> +	}
> +
> +	void invoke(Args... args)
> +	{
> +		(static_cast<T *>(this->obj_)->*func_)(args...);
> +	}
> +
> +private:
> +	friend class Signal<Args...>;
> +	void (T::*func_)(Args...);
> +};
> +
> +template<typename... Args>
> +class BoundStaticMethod : public BoundMethodArgs<Args...>
> +{
> +public:
> +	BoundStaticMethod(void (*func)(Args...))
> +		: BoundMethodArgs<Args...>(nullptr, nullptr), func_(func) {}
> +
> +	void activate(Args... args) { (*func_)(args...); }
> +	void invoke(Args... args) {}
> +
> +private:
> +	friend class Signal<Args...>;
> +	void (*func_)(Args...);
> +};
> +
> +}; /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_BOUND_METHOD_H__ */
> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
> index 920eb5fcbfd1..a8a38a9b1db7 100644
> --- a/include/libcamera/meson.build
> +++ b/include/libcamera/meson.build
> @@ -1,4 +1,5 @@
>  libcamera_api = files([
> +    'bound_method.h',
>      'buffer.h',
>      'camera.h',
>      'camera_manager.h',
> diff --git a/include/libcamera/object.h b/include/libcamera/object.h
> index 3d08d69a9044..e3b39cf547b0 100644
> --- a/include/libcamera/object.h
> +++ b/include/libcamera/object.h
> @@ -10,13 +10,14 @@
>  #include <list>
>  #include <memory>
>
> +#include <libcamera/bound_method.h>
> +
>  namespace libcamera {
>
>  class Message;
>  template<typename... Args>
>  class Signal;
>  class SignalBase;
> -class SlotBase;
>  class Thread;
>
>  class Object
> @@ -36,7 +37,7 @@ protected:
>  private:
>  	template<typename... Args>
>  	friend class Signal;
> -	friend class SlotBase;
> +	friend class BoundMethodBase;
>  	friend class Thread;
>
>  	void connect(SignalBase *signal);
> diff --git a/include/libcamera/signal.h b/include/libcamera/signal.h
> index 8f6db700cd80..3b6de30f7d35 100644
> --- a/include/libcamera/signal.h
> +++ b/include/libcamera/signal.h
> @@ -8,127 +8,14 @@
>  #define __LIBCAMERA_SIGNAL_H__
>
>  #include <list>
> -#include <tuple>
>  #include <type_traits>
>  #include <vector>
>
> +#include <libcamera/bound_method.h>
>  #include <libcamera/object.h>
>
>  namespace libcamera {
>
> -template<typename... Args>
> -class Signal;
> -class SignalBase;
> -
> -class SlotBase
> -{
> -public:
> -	SlotBase(void *obj, Object *object)
> -		: obj_(obj), object_(object) {}
> -	virtual ~SlotBase() {}
> -
> -	template<typename T, typename std::enable_if<!std::is_same<Object, T>::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<typename... Args>
> -class SlotArgs : public SlotBase
> -{
> -private:
> -#ifndef __DOXYGEN__
> -	/*
> -	 * This is a cheap partial implementation of std::integer_sequence<>
> -	 * from C++14.
> -	 */
> -	template<int...>
> -	struct sequence {
> -	};
> -
> -	template<int N, int... S>
> -	struct generator : generator<N-1, N-1, S...> {
> -	};
> -
> -	template<int... S>
> -	struct generator<0, S...> {
> -		typedef sequence<S...> type;
> -	};
> -#endif
> -
> -	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> -
> -	template<int... S>
> -	void invokePack(void *pack, sequence<S...>)
> -	{
> -		PackType *args = static_cast<PackType *>(pack);
> -		invoke(std::get<S>(*args)...);
> -		delete args;
> -	}
> -
> -public:
> -	SlotArgs(void *obj, Object *object)
> -		: SlotBase(obj, object) {}
> -
> -	void invokePack(void *pack) override
> -	{
> -		invokePack(pack, typename generator<sizeof...(Args)>::type());
> -	}
> -
> -	virtual void activate(Args... args) = 0;
> -	virtual void invoke(Args... args) = 0;
> -};
> -
> -template<typename T, typename... Args>
> -class SlotMember : public SlotArgs<Args...>
> -{
> -public:
> -	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> -
> -	SlotMember(T *obj, Object *object, void (T::*func)(Args...))
> -		: SlotArgs<Args...>(obj, object), func_(func) {}
> -
> -	void activate(Args... args)
> -	{
> -		if (this->object_)
> -			SlotBase::activatePack(new PackType{ args... });
> -		else
> -			(static_cast<T *>(this->obj_)->*func_)(args...);
> -	}
> -
> -	void invoke(Args... args)
> -	{
> -		(static_cast<T *>(this->obj_)->*func_)(args...);
> -	}
> -
> -private:
> -	friend class Signal<Args...>;
> -	void (T::*func_)(Args...);
> -};
> -
> -template<typename... Args>
> -class SlotStatic : public SlotArgs<Args...>
> -{
> -public:
> -	SlotStatic(void (*func)(Args...))
> -		: SlotArgs<Args...>(nullptr, nullptr), func_(func) {}
> -
> -	void activate(Args... args) { (*func_)(args...); }
> -	void invoke(Args... args) {}
> -
> -private:
> -	friend class Signal<Args...>;
> -	void (*func_)(Args...);
> -};
> -
>  class SignalBase
>  {
>  public:
> @@ -136,7 +23,7 @@ public:
>  	void disconnect(T *obj)
>  	{
>  		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
> -			SlotBase *slot = *iter;
> +			BoundMethodBase *slot = *iter;
>  			if (slot->match(obj)) {
>  				iter = slots_.erase(iter);
>  				delete slot;
> @@ -148,7 +35,7 @@ public:
>
>  protected:
>  	friend class Object;
> -	std::list<SlotBase *> slots_;
> +	std::list<BoundMethodBase *> slots_;
>  };
>
>  template<typename... Args>
> @@ -158,7 +45,7 @@ public:
>  	Signal() {}
>  	~Signal()
>  	{
> -		for (SlotBase *slot : slots_) {
> +		for (BoundMethodBase *slot : slots_) {
>  			slot->disconnect(this);
>  			delete slot;
>  		}
> @@ -170,7 +57,7 @@ public:
>  	{
>  		Object *object = static_cast<Object *>(obj);
>  		object->connect(this);
> -		slots_.push_back(new SlotMember<T, Args...>(obj, object, func));
> +		slots_.push_back(new BoundMemberMethod<T, Args...>(obj, object, func));
>  	}
>
>  	template<typename T, typename std::enable_if<!std::is_base_of<Object, T>::value>::type * = nullptr>
> @@ -179,17 +66,17 @@ public:
>  #endif
>  	void connect(T *obj, void (T::*func)(Args...))
>  	{
> -		slots_.push_back(new SlotMember<T, Args...>(obj, nullptr, func));
> +		slots_.push_back(new BoundMemberMethod<T, Args...>(obj, nullptr, func));
>  	}
>
>  	void connect(void (*func)(Args...))
>  	{
> -		slots_.push_back(new SlotStatic<Args...>(func));
> +		slots_.push_back(new BoundStaticMethod<Args...>(func));
>  	}
>
>  	void disconnect()
>  	{
> -		for (SlotBase *slot : slots_)
> +		for (BoundMethodBase *slot : slots_)
>  			delete slot;
>  		slots_.clear();
>  	}
> @@ -204,15 +91,15 @@ public:
>  	void disconnect(T *obj, void (T::*func)(Args...))
>  	{
>  		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
> -			SlotArgs<Args...> *slot = static_cast<SlotArgs<Args...> *>(*iter);
> +			BoundMethodArgs<Args...> *slot = static_cast<BoundMethodArgs<Args...> *>(*iter);
>  			/*
>  			 * If the object matches the slot, the slot is
>  			 * guaranteed to be a member slot, so we can safely
> -			 * cast it to SlotMember<T, Args...> and access its
> +			 * cast it to BoundMemberMethod<T, Args...> and access its
>  			 * func_ member.
>  			 */
>  			if (slot->match(obj) &&
> -			    static_cast<SlotMember<T, Args...> *>(slot)->func_ == func) {
> +			    static_cast<BoundMemberMethod<T, Args...> *>(slot)->func_ == func) {
>  				iter = slots_.erase(iter);
>  				delete slot;
>  			} else {
> @@ -224,9 +111,9 @@ public:
>  	void disconnect(void (*func)(Args...))
>  	{
>  		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
> -			SlotArgs<Args...> *slot = *iter;
> +			BoundMethodArgs<Args...> *slot = *iter;
>  			if (slot->match(nullptr) &&
> -			    static_cast<SlotStatic<Args...> *>(slot)->func_ == func) {
> +			    static_cast<BoundStaticMethod<Args...> *>(slot)->func_ == func) {
>  				iter = slots_.erase(iter);
>  				delete slot;
>  			} else {
> @@ -241,9 +128,9 @@ public:
>  		 * Make a copy of the slots list as the slot could call the
>  		 * disconnect operation, invalidating the iterator.
>  		 */
> -		std::vector<SlotBase *> slots{ slots_.begin(), slots_.end() };
> -		for (SlotBase *slot : slots)
> -			static_cast<SlotArgs<Args...> *>(slot)->activate(args...);
> +		std::vector<BoundMethodBase *> slots{ slots_.begin(), slots_.end() };
> +		for (BoundMethodBase *slot : slots)
> +			static_cast<BoundMethodArgs<Args...> *>(slot)->activate(args...);
>  	}
>  };
>
> diff --git a/src/libcamera/bound_method.cpp b/src/libcamera/bound_method.cpp
> new file mode 100644
> index 000000000000..0a2d61a6c805
> --- /dev/null
> +++ b/src/libcamera/bound_method.cpp
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * bound_method.cpp - Method bind and invocation
> + */
> +
> +#include <libcamera/bound_method.h>
> +
> +#include "message.h"
> +#include "thread.h"
> +#include "utils.h"
> +
> +namespace libcamera {
> +
> +void BoundMethodBase::disconnect(SignalBase *signal)
> +{
> +	if (object_)
> +		object_->disconnect(signal);
> +}
> +
> +void BoundMethodBase::activatePack(void *pack)
> +{
> +	if (Thread::current() == object_->thread()) {
> +		invokePack(pack);
> +	} else {
> +		std::unique_ptr<Message> msg =
> +			utils::make_unique<SignalMessage>(this, pack);
> +		object_->postMessage(std::move(msg));
> +	}
> +}
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/include/message.h b/src/libcamera/include/message.h
> index 416fe74b10ad..b4670c0eab76 100644
> --- a/src/libcamera/include/message.h
> +++ b/src/libcamera/include/message.h
> @@ -11,8 +11,8 @@
>
>  namespace libcamera {
>
> +class BoundMethodBase;
>  class Object;
> -class SlotBase;
>  class Thread;
>
>  class Message
> @@ -44,12 +44,12 @@ private:
>  class SignalMessage : public Message
>  {
>  public:
> -	SignalMessage(SlotBase *slot, void *pack)
> -		: Message(Message::SignalMessage), slot_(slot), pack_(pack)
> +	SignalMessage(BoundMethodBase *method, void *pack)
> +		: Message(Message::SignalMessage), method_(method), pack_(pack)
>  	{
>  	}
>
> -	SlotBase *slot_;
> +	BoundMethodBase *method_;
>  	void *pack_;
>  };
>
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 7d5d3c04fba0..c5d8f116ffc6 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -1,4 +1,5 @@
>  libcamera_sources = files([
> +    'bound_method.cpp',
>      'buffer.cpp',
>      'camera.cpp',
>      'camera_manager.cpp',
> diff --git a/src/libcamera/message.cpp b/src/libcamera/message.cpp
> index d44d2a4c73a8..8d3376d8a533 100644
> --- a/src/libcamera/message.cpp
> +++ b/src/libcamera/message.cpp
> @@ -114,12 +114,12 @@ Message::Type Message::registerMessageType()
>  /**
>   * \fn SignalMessage::SignalMessage()
>   * \brief Construct a SignalMessage
> - * \param[in] slot The slot that the signal targets
> + * \param[in] method The slot that the signal targets
>   * \param[in] pack The signal arguments
>   */
>
>  /**
> - * \var SignalMessage::slot_
> + * \var SignalMessage::method_
>   * \brief The slot that the signal targets
>   */
>
> diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp
> index 61787fadac0b..0adbc203add8 100644
> --- a/src/libcamera/object.cpp
> +++ b/src/libcamera/object.cpp
> @@ -90,7 +90,7 @@ void Object::message(Message *msg)
>  	switch (msg->type()) {
>  	case Message::SignalMessage: {
>  		SignalMessage *smsg = static_cast<SignalMessage *>(msg);
> -		smsg->slot_->invokePack(smsg->pack_);
> +		smsg->method_->invokePack(smsg->pack_);
>  		break;
>  	}
>
> diff --git a/src/libcamera/signal.cpp b/src/libcamera/signal.cpp
> index ab7dba508dfb..6ee348acc8d4 100644
> --- a/src/libcamera/signal.cpp
> +++ b/src/libcamera/signal.cpp
> @@ -7,10 +7,6 @@
>
>  #include <libcamera/signal.h>
>
> -#include "message.h"
> -#include "thread.h"
> -#include "utils.h"
> -
>  /**
>   * \file signal.h
>   * \brief Signal & slot implementation
> @@ -57,25 +53,6 @@ namespace libcamera {
>   * passed through the signal will remain valid after the signal is emitted.
>   */
>
> -void SlotBase::disconnect(SignalBase *signal)
> -{
> -	if (object_)
> -		object_->disconnect(signal);
> -}
> -
> -void SlotBase::activatePack(void *pack)
> -{
> -	Object *obj = static_cast<Object *>(object_);
> -
> -	if (Thread::current() == obj->thread()) {
> -		invokePack(pack);
> -	} else {
> -		std::unique_ptr<Message> msg =
> -			utils::make_unique<SignalMessage>(this, pack);
> -		obj->postMessage(std::move(msg));
> -	}
> -}
> -
>  /**
>   * \fn Signal::connect(T *object, void(T::*func)(Args...))
>   * \brief Connect the signal to a member function slot
> --
> Regards,
>
> Laurent Pinchart
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Laurent Pinchart Aug. 15, 2019, 9:49 a.m. UTC | #2
Hi Jacopo,

On Thu, Aug 15, 2019 at 10:47:41AM +0200, Jacopo Mondi wrote:
> On Mon, Aug 12, 2019 at 03:46:28PM +0300, Laurent Pinchart wrote:
> > Move the Slot* classes to bound_method.{h,cpp} and rename them to
> > Bound*Method*. They will be reused to implement asynchronous method
> > invocation similar to cross-thread signal delivery.
> 
> Stupid question, but why do you need to rename to a longer
> "Bound*Method*" when "Slot" was quite efficient as a name? Couldn't
> you just move the Slot hierarchy definition and implementation to
> their own files?

The name "Slot" is tied to Signal, and I wanted a better name that would
reflect the extended purpose of this class. I'm open to better
suggestions of course :-)

> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  Documentation/Doxyfile.in        |  10 +--
> >  include/libcamera/bound_method.h | 131 ++++++++++++++++++++++++++++
> >  include/libcamera/meson.build    |   1 +
> >  include/libcamera/object.h       |   5 +-
> >  include/libcamera/signal.h       | 145 ++++---------------------------
> >  src/libcamera/bound_method.cpp   |  33 +++++++
> >  src/libcamera/include/message.h  |   8 +-
> >  src/libcamera/meson.build        |   1 +
> >  src/libcamera/message.cpp        |   4 +-
> >  src/libcamera/object.cpp         |   2 +-
> >  src/libcamera/signal.cpp         |  23 -----
> >  11 files changed, 197 insertions(+), 166 deletions(-)
> >  create mode 100644 include/libcamera/bound_method.h
> >  create mode 100644 src/libcamera/bound_method.cpp
> >
> > diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
> > index 3d94623a4b8f..a9596c2a32d8 100644
> > --- a/Documentation/Doxyfile.in
> > +++ b/Documentation/Doxyfile.in
> > @@ -865,11 +865,11 @@ EXCLUDE_PATTERNS       =
> >  # Note that the wildcards are matched against the file with absolute path, so to
> >  # exclude all test directories use the pattern */test/*
> >
> > -EXCLUDE_SYMBOLS        = libcamera::SignalBase \
> > -                         libcamera::SlotArgs \
> > -                         libcamera::SlotBase \
> > -                         libcamera::SlotMember \
> > -                         libcamera::SlotStatic \
> > +EXCLUDE_SYMBOLS        = libcamera::BoundMemberMethod \
> > +                         libcamera::BoundMethodArgs \
> > +                         libcamera::BoundMethodBase \
> > +                         libcamera::BoundStaticMethod \
> > +                         libcamera::SignalBase \
> >                           std::*
> >
> >  # The EXAMPLE_PATH tag can be used to specify one or more files or directories
> > diff --git a/include/libcamera/bound_method.h b/include/libcamera/bound_method.h
> > new file mode 100644
> > index 000000000000..38c44b923ba1
> > --- /dev/null
> > +++ b/include/libcamera/bound_method.h
> > @@ -0,0 +1,131 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2019, Google Inc.
> > + *
> > + * bound_method.h - Method bind and invocation
> > + */
> > +#ifndef __LIBCAMERA_BOUND_METHOD_H__
> > +#define __LIBCAMERA_BOUND_METHOD_H__
> > +
> > +#include <tuple>
> > +#include <type_traits>
> > +
> > +namespace libcamera {
> > +
> > +class Object;
> > +template<typename... Args>
> > +class Signal;
> > +class SignalBase;
> > +
> > +class BoundMethodBase
> > +{
> > +public:
> > +	BoundMethodBase(void *obj, Object *object)
> > +		: obj_(obj), object_(object) {}
> > +	virtual ~BoundMethodBase() {}
> > +
> > +	template<typename T, typename std::enable_if<!std::is_same<Object, T>::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<typename... Args>
> > +class BoundMethodArgs : public BoundMethodBase
> > +{
> > +private:
> > +#ifndef __DOXYGEN__
> > +	/*
> > +	 * This is a cheap partial implementation of std::integer_sequence<>
> > +	 * from C++14.
> > +	 */
> > +	template<int...>
> > +	struct sequence {
> > +	};
> > +
> > +	template<int N, int... S>
> > +	struct generator : generator<N-1, N-1, S...> {
> > +	};
> > +
> > +	template<int... S>
> > +	struct generator<0, S...> {
> > +		typedef sequence<S...> type;
> > +	};
> > +#endif
> > +
> > +	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> > +
> > +	template<int... S>
> > +	void invokePack(void *pack, sequence<S...>)
> > +	{
> > +		PackType *args = static_cast<PackType *>(pack);
> > +		invoke(std::get<S>(*args)...);
> > +		delete args;
> > +	}
> > +
> > +public:
> > +	BoundMethodArgs(void *obj, Object *object)
> > +		: BoundMethodBase(obj, object) {}
> > +
> > +	void invokePack(void *pack) override
> > +	{
> > +		invokePack(pack, typename generator<sizeof...(Args)>::type());
> > +	}
> > +
> > +	virtual void activate(Args... args) = 0;
> > +	virtual void invoke(Args... args) = 0;
> > +};
> > +
> > +template<typename T, typename... Args>
> > +class BoundMemberMethod : public BoundMethodArgs<Args...>
> > +{
> > +public:
> > +	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> > +
> > +	BoundMemberMethod(T *obj, Object *object, void (T::*func)(Args...))
> > +		: BoundMethodArgs<Args...>(obj, object), func_(func) {}
> > +
> > +	void activate(Args... args)
> > +	{
> > +		if (this->object_)
> > +			BoundMethodBase::activatePack(new PackType{ args... });
> > +		else
> > +			(static_cast<T *>(this->obj_)->*func_)(args...);
> > +	}
> > +
> > +	void invoke(Args... args)
> > +	{
> > +		(static_cast<T *>(this->obj_)->*func_)(args...);
> > +	}
> > +
> > +private:
> > +	friend class Signal<Args...>;
> > +	void (T::*func_)(Args...);
> > +};
> > +
> > +template<typename... Args>
> > +class BoundStaticMethod : public BoundMethodArgs<Args...>
> > +{
> > +public:
> > +	BoundStaticMethod(void (*func)(Args...))
> > +		: BoundMethodArgs<Args...>(nullptr, nullptr), func_(func) {}
> > +
> > +	void activate(Args... args) { (*func_)(args...); }
> > +	void invoke(Args... args) {}
> > +
> > +private:
> > +	friend class Signal<Args...>;
> > +	void (*func_)(Args...);
> > +};
> > +
> > +}; /* namespace libcamera */
> > +
> > +#endif /* __LIBCAMERA_BOUND_METHOD_H__ */
> > diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
> > index 920eb5fcbfd1..a8a38a9b1db7 100644
> > --- a/include/libcamera/meson.build
> > +++ b/include/libcamera/meson.build
> > @@ -1,4 +1,5 @@
> >  libcamera_api = files([
> > +    'bound_method.h',
> >      'buffer.h',
> >      'camera.h',
> >      'camera_manager.h',
> > diff --git a/include/libcamera/object.h b/include/libcamera/object.h
> > index 3d08d69a9044..e3b39cf547b0 100644
> > --- a/include/libcamera/object.h
> > +++ b/include/libcamera/object.h
> > @@ -10,13 +10,14 @@
> >  #include <list>
> >  #include <memory>
> >
> > +#include <libcamera/bound_method.h>
> > +
> >  namespace libcamera {
> >
> >  class Message;
> >  template<typename... Args>
> >  class Signal;
> >  class SignalBase;
> > -class SlotBase;
> >  class Thread;
> >
> >  class Object
> > @@ -36,7 +37,7 @@ protected:
> >  private:
> >  	template<typename... Args>
> >  	friend class Signal;
> > -	friend class SlotBase;
> > +	friend class BoundMethodBase;
> >  	friend class Thread;
> >
> >  	void connect(SignalBase *signal);
> > diff --git a/include/libcamera/signal.h b/include/libcamera/signal.h
> > index 8f6db700cd80..3b6de30f7d35 100644
> > --- a/include/libcamera/signal.h
> > +++ b/include/libcamera/signal.h
> > @@ -8,127 +8,14 @@
> >  #define __LIBCAMERA_SIGNAL_H__
> >
> >  #include <list>
> > -#include <tuple>
> >  #include <type_traits>
> >  #include <vector>
> >
> > +#include <libcamera/bound_method.h>
> >  #include <libcamera/object.h>
> >
> >  namespace libcamera {
> >
> > -template<typename... Args>
> > -class Signal;
> > -class SignalBase;
> > -
> > -class SlotBase
> > -{
> > -public:
> > -	SlotBase(void *obj, Object *object)
> > -		: obj_(obj), object_(object) {}
> > -	virtual ~SlotBase() {}
> > -
> > -	template<typename T, typename std::enable_if<!std::is_same<Object, T>::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<typename... Args>
> > -class SlotArgs : public SlotBase
> > -{
> > -private:
> > -#ifndef __DOXYGEN__
> > -	/*
> > -	 * This is a cheap partial implementation of std::integer_sequence<>
> > -	 * from C++14.
> > -	 */
> > -	template<int...>
> > -	struct sequence {
> > -	};
> > -
> > -	template<int N, int... S>
> > -	struct generator : generator<N-1, N-1, S...> {
> > -	};
> > -
> > -	template<int... S>
> > -	struct generator<0, S...> {
> > -		typedef sequence<S...> type;
> > -	};
> > -#endif
> > -
> > -	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> > -
> > -	template<int... S>
> > -	void invokePack(void *pack, sequence<S...>)
> > -	{
> > -		PackType *args = static_cast<PackType *>(pack);
> > -		invoke(std::get<S>(*args)...);
> > -		delete args;
> > -	}
> > -
> > -public:
> > -	SlotArgs(void *obj, Object *object)
> > -		: SlotBase(obj, object) {}
> > -
> > -	void invokePack(void *pack) override
> > -	{
> > -		invokePack(pack, typename generator<sizeof...(Args)>::type());
> > -	}
> > -
> > -	virtual void activate(Args... args) = 0;
> > -	virtual void invoke(Args... args) = 0;
> > -};
> > -
> > -template<typename T, typename... Args>
> > -class SlotMember : public SlotArgs<Args...>
> > -{
> > -public:
> > -	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> > -
> > -	SlotMember(T *obj, Object *object, void (T::*func)(Args...))
> > -		: SlotArgs<Args...>(obj, object), func_(func) {}
> > -
> > -	void activate(Args... args)
> > -	{
> > -		if (this->object_)
> > -			SlotBase::activatePack(new PackType{ args... });
> > -		else
> > -			(static_cast<T *>(this->obj_)->*func_)(args...);
> > -	}
> > -
> > -	void invoke(Args... args)
> > -	{
> > -		(static_cast<T *>(this->obj_)->*func_)(args...);
> > -	}
> > -
> > -private:
> > -	friend class Signal<Args...>;
> > -	void (T::*func_)(Args...);
> > -};
> > -
> > -template<typename... Args>
> > -class SlotStatic : public SlotArgs<Args...>
> > -{
> > -public:
> > -	SlotStatic(void (*func)(Args...))
> > -		: SlotArgs<Args...>(nullptr, nullptr), func_(func) {}
> > -
> > -	void activate(Args... args) { (*func_)(args...); }
> > -	void invoke(Args... args) {}
> > -
> > -private:
> > -	friend class Signal<Args...>;
> > -	void (*func_)(Args...);
> > -};
> > -
> >  class SignalBase
> >  {
> >  public:
> > @@ -136,7 +23,7 @@ public:
> >  	void disconnect(T *obj)
> >  	{
> >  		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
> > -			SlotBase *slot = *iter;
> > +			BoundMethodBase *slot = *iter;
> >  			if (slot->match(obj)) {
> >  				iter = slots_.erase(iter);
> >  				delete slot;
> > @@ -148,7 +35,7 @@ public:
> >
> >  protected:
> >  	friend class Object;
> > -	std::list<SlotBase *> slots_;
> > +	std::list<BoundMethodBase *> slots_;
> >  };
> >
> >  template<typename... Args>
> > @@ -158,7 +45,7 @@ public:
> >  	Signal() {}
> >  	~Signal()
> >  	{
> > -		for (SlotBase *slot : slots_) {
> > +		for (BoundMethodBase *slot : slots_) {
> >  			slot->disconnect(this);
> >  			delete slot;
> >  		}
> > @@ -170,7 +57,7 @@ public:
> >  	{
> >  		Object *object = static_cast<Object *>(obj);
> >  		object->connect(this);
> > -		slots_.push_back(new SlotMember<T, Args...>(obj, object, func));
> > +		slots_.push_back(new BoundMemberMethod<T, Args...>(obj, object, func));
> >  	}
> >
> >  	template<typename T, typename std::enable_if<!std::is_base_of<Object, T>::value>::type * = nullptr>
> > @@ -179,17 +66,17 @@ public:
> >  #endif
> >  	void connect(T *obj, void (T::*func)(Args...))
> >  	{
> > -		slots_.push_back(new SlotMember<T, Args...>(obj, nullptr, func));
> > +		slots_.push_back(new BoundMemberMethod<T, Args...>(obj, nullptr, func));
> >  	}
> >
> >  	void connect(void (*func)(Args...))
> >  	{
> > -		slots_.push_back(new SlotStatic<Args...>(func));
> > +		slots_.push_back(new BoundStaticMethod<Args...>(func));
> >  	}
> >
> >  	void disconnect()
> >  	{
> > -		for (SlotBase *slot : slots_)
> > +		for (BoundMethodBase *slot : slots_)
> >  			delete slot;
> >  		slots_.clear();
> >  	}
> > @@ -204,15 +91,15 @@ public:
> >  	void disconnect(T *obj, void (T::*func)(Args...))
> >  	{
> >  		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
> > -			SlotArgs<Args...> *slot = static_cast<SlotArgs<Args...> *>(*iter);
> > +			BoundMethodArgs<Args...> *slot = static_cast<BoundMethodArgs<Args...> *>(*iter);
> >  			/*
> >  			 * If the object matches the slot, the slot is
> >  			 * guaranteed to be a member slot, so we can safely
> > -			 * cast it to SlotMember<T, Args...> and access its
> > +			 * cast it to BoundMemberMethod<T, Args...> and access its
> >  			 * func_ member.
> >  			 */
> >  			if (slot->match(obj) &&
> > -			    static_cast<SlotMember<T, Args...> *>(slot)->func_ == func) {
> > +			    static_cast<BoundMemberMethod<T, Args...> *>(slot)->func_ == func) {
> >  				iter = slots_.erase(iter);
> >  				delete slot;
> >  			} else {
> > @@ -224,9 +111,9 @@ public:
> >  	void disconnect(void (*func)(Args...))
> >  	{
> >  		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
> > -			SlotArgs<Args...> *slot = *iter;
> > +			BoundMethodArgs<Args...> *slot = *iter;
> >  			if (slot->match(nullptr) &&
> > -			    static_cast<SlotStatic<Args...> *>(slot)->func_ == func) {
> > +			    static_cast<BoundStaticMethod<Args...> *>(slot)->func_ == func) {
> >  				iter = slots_.erase(iter);
> >  				delete slot;
> >  			} else {
> > @@ -241,9 +128,9 @@ public:
> >  		 * Make a copy of the slots list as the slot could call the
> >  		 * disconnect operation, invalidating the iterator.
> >  		 */
> > -		std::vector<SlotBase *> slots{ slots_.begin(), slots_.end() };
> > -		for (SlotBase *slot : slots)
> > -			static_cast<SlotArgs<Args...> *>(slot)->activate(args...);
> > +		std::vector<BoundMethodBase *> slots{ slots_.begin(), slots_.end() };
> > +		for (BoundMethodBase *slot : slots)
> > +			static_cast<BoundMethodArgs<Args...> *>(slot)->activate(args...);
> >  	}
> >  };
> >
> > diff --git a/src/libcamera/bound_method.cpp b/src/libcamera/bound_method.cpp
> > new file mode 100644
> > index 000000000000..0a2d61a6c805
> > --- /dev/null
> > +++ b/src/libcamera/bound_method.cpp
> > @@ -0,0 +1,33 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2019, Google Inc.
> > + *
> > + * bound_method.cpp - Method bind and invocation
> > + */
> > +
> > +#include <libcamera/bound_method.h>
> > +
> > +#include "message.h"
> > +#include "thread.h"
> > +#include "utils.h"
> > +
> > +namespace libcamera {
> > +
> > +void BoundMethodBase::disconnect(SignalBase *signal)
> > +{
> > +	if (object_)
> > +		object_->disconnect(signal);
> > +}
> > +
> > +void BoundMethodBase::activatePack(void *pack)
> > +{
> > +	if (Thread::current() == object_->thread()) {
> > +		invokePack(pack);
> > +	} else {
> > +		std::unique_ptr<Message> msg =
> > +			utils::make_unique<SignalMessage>(this, pack);
> > +		object_->postMessage(std::move(msg));
> > +	}
> > +}
> > +
> > +} /* namespace libcamera */
> > diff --git a/src/libcamera/include/message.h b/src/libcamera/include/message.h
> > index 416fe74b10ad..b4670c0eab76 100644
> > --- a/src/libcamera/include/message.h
> > +++ b/src/libcamera/include/message.h
> > @@ -11,8 +11,8 @@
> >
> >  namespace libcamera {
> >
> > +class BoundMethodBase;
> >  class Object;
> > -class SlotBase;
> >  class Thread;
> >
> >  class Message
> > @@ -44,12 +44,12 @@ private:
> >  class SignalMessage : public Message
> >  {
> >  public:
> > -	SignalMessage(SlotBase *slot, void *pack)
> > -		: Message(Message::SignalMessage), slot_(slot), pack_(pack)
> > +	SignalMessage(BoundMethodBase *method, void *pack)
> > +		: Message(Message::SignalMessage), method_(method), pack_(pack)
> >  	{
> >  	}
> >
> > -	SlotBase *slot_;
> > +	BoundMethodBase *method_;
> >  	void *pack_;
> >  };
> >
> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> > index 7d5d3c04fba0..c5d8f116ffc6 100644
> > --- a/src/libcamera/meson.build
> > +++ b/src/libcamera/meson.build
> > @@ -1,4 +1,5 @@
> >  libcamera_sources = files([
> > +    'bound_method.cpp',
> >      'buffer.cpp',
> >      'camera.cpp',
> >      'camera_manager.cpp',
> > diff --git a/src/libcamera/message.cpp b/src/libcamera/message.cpp
> > index d44d2a4c73a8..8d3376d8a533 100644
> > --- a/src/libcamera/message.cpp
> > +++ b/src/libcamera/message.cpp
> > @@ -114,12 +114,12 @@ Message::Type Message::registerMessageType()
> >  /**
> >   * \fn SignalMessage::SignalMessage()
> >   * \brief Construct a SignalMessage
> > - * \param[in] slot The slot that the signal targets
> > + * \param[in] method The slot that the signal targets
> >   * \param[in] pack The signal arguments
> >   */
> >
> >  /**
> > - * \var SignalMessage::slot_
> > + * \var SignalMessage::method_
> >   * \brief The slot that the signal targets
> >   */
> >
> > diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp
> > index 61787fadac0b..0adbc203add8 100644
> > --- a/src/libcamera/object.cpp
> > +++ b/src/libcamera/object.cpp
> > @@ -90,7 +90,7 @@ void Object::message(Message *msg)
> >  	switch (msg->type()) {
> >  	case Message::SignalMessage: {
> >  		SignalMessage *smsg = static_cast<SignalMessage *>(msg);
> > -		smsg->slot_->invokePack(smsg->pack_);
> > +		smsg->method_->invokePack(smsg->pack_);
> >  		break;
> >  	}
> >
> > diff --git a/src/libcamera/signal.cpp b/src/libcamera/signal.cpp
> > index ab7dba508dfb..6ee348acc8d4 100644
> > --- a/src/libcamera/signal.cpp
> > +++ b/src/libcamera/signal.cpp
> > @@ -7,10 +7,6 @@
> >
> >  #include <libcamera/signal.h>
> >
> > -#include "message.h"
> > -#include "thread.h"
> > -#include "utils.h"
> > -
> >  /**
> >   * \file signal.h
> >   * \brief Signal & slot implementation
> > @@ -57,25 +53,6 @@ namespace libcamera {
> >   * passed through the signal will remain valid after the signal is emitted.
> >   */
> >
> > -void SlotBase::disconnect(SignalBase *signal)
> > -{
> > -	if (object_)
> > -		object_->disconnect(signal);
> > -}
> > -
> > -void SlotBase::activatePack(void *pack)
> > -{
> > -	Object *obj = static_cast<Object *>(object_);
> > -
> > -	if (Thread::current() == obj->thread()) {
> > -		invokePack(pack);
> > -	} else {
> > -		std::unique_ptr<Message> msg =
> > -			utils::make_unique<SignalMessage>(this, pack);
> > -		obj->postMessage(std::move(msg));
> > -	}
> > -}
> > -
> >  /**
> >   * \fn Signal::connect(T *object, void(T::*func)(Args...))
> >   * \brief Connect the signal to a member function slot
> > --
> > Regards,
> >
> > Laurent Pinchart
> >
> > _______________________________________________
> > libcamera-devel mailing list
> > libcamera-devel@lists.libcamera.org
> > https://lists.libcamera.org/listinfo/libcamera-devel
Niklas Söderlund Aug. 17, 2019, 2:21 p.m. UTC | #3
Hi Laurent,

Thanks for your work.

On 2019-08-12 15:46:28 +0300, Laurent Pinchart wrote:
> Move the Slot* classes to bound_method.{h,cpp} and rename them to
> Bound*Method*. They will be reused to implement asynchronous method
> invocation similar to cross-thread signal delivery.

I would clarify that this is *only* a move and rename patch, to ease 
review as it's a quiet big patch.

> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>

> ---
>  Documentation/Doxyfile.in        |  10 +--
>  include/libcamera/bound_method.h | 131 ++++++++++++++++++++++++++++
>  include/libcamera/meson.build    |   1 +
>  include/libcamera/object.h       |   5 +-
>  include/libcamera/signal.h       | 145 ++++---------------------------
>  src/libcamera/bound_method.cpp   |  33 +++++++
>  src/libcamera/include/message.h  |   8 +-
>  src/libcamera/meson.build        |   1 +
>  src/libcamera/message.cpp        |   4 +-
>  src/libcamera/object.cpp         |   2 +-
>  src/libcamera/signal.cpp         |  23 -----
>  11 files changed, 197 insertions(+), 166 deletions(-)
>  create mode 100644 include/libcamera/bound_method.h
>  create mode 100644 src/libcamera/bound_method.cpp
> 
> diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
> index 3d94623a4b8f..a9596c2a32d8 100644
> --- a/Documentation/Doxyfile.in
> +++ b/Documentation/Doxyfile.in
> @@ -865,11 +865,11 @@ EXCLUDE_PATTERNS       =
>  # Note that the wildcards are matched against the file with absolute path, so to
>  # exclude all test directories use the pattern */test/*
>  
> -EXCLUDE_SYMBOLS        = libcamera::SignalBase \
> -                         libcamera::SlotArgs \
> -                         libcamera::SlotBase \
> -                         libcamera::SlotMember \
> -                         libcamera::SlotStatic \
> +EXCLUDE_SYMBOLS        = libcamera::BoundMemberMethod \
> +                         libcamera::BoundMethodArgs \
> +                         libcamera::BoundMethodBase \
> +                         libcamera::BoundStaticMethod \
> +                         libcamera::SignalBase \
>                           std::*
>  
>  # The EXAMPLE_PATH tag can be used to specify one or more files or directories
> diff --git a/include/libcamera/bound_method.h b/include/libcamera/bound_method.h
> new file mode 100644
> index 000000000000..38c44b923ba1
> --- /dev/null
> +++ b/include/libcamera/bound_method.h
> @@ -0,0 +1,131 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * bound_method.h - Method bind and invocation
> + */
> +#ifndef __LIBCAMERA_BOUND_METHOD_H__
> +#define __LIBCAMERA_BOUND_METHOD_H__
> +
> +#include <tuple>
> +#include <type_traits>
> +
> +namespace libcamera {
> +
> +class Object;
> +template<typename... Args>
> +class Signal;
> +class SignalBase;
> +
> +class BoundMethodBase
> +{
> +public:
> +	BoundMethodBase(void *obj, Object *object)
> +		: obj_(obj), object_(object) {}
> +	virtual ~BoundMethodBase() {}
> +
> +	template<typename T, typename std::enable_if<!std::is_same<Object, T>::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<typename... Args>
> +class BoundMethodArgs : public BoundMethodBase
> +{
> +private:
> +#ifndef __DOXYGEN__
> +	/*
> +	 * This is a cheap partial implementation of std::integer_sequence<>
> +	 * from C++14.
> +	 */
> +	template<int...>
> +	struct sequence {
> +	};
> +
> +	template<int N, int... S>
> +	struct generator : generator<N-1, N-1, S...> {
> +	};
> +
> +	template<int... S>
> +	struct generator<0, S...> {
> +		typedef sequence<S...> type;
> +	};
> +#endif
> +
> +	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> +
> +	template<int... S>
> +	void invokePack(void *pack, sequence<S...>)
> +	{
> +		PackType *args = static_cast<PackType *>(pack);
> +		invoke(std::get<S>(*args)...);
> +		delete args;
> +	}
> +
> +public:
> +	BoundMethodArgs(void *obj, Object *object)
> +		: BoundMethodBase(obj, object) {}
> +
> +	void invokePack(void *pack) override
> +	{
> +		invokePack(pack, typename generator<sizeof...(Args)>::type());
> +	}
> +
> +	virtual void activate(Args... args) = 0;
> +	virtual void invoke(Args... args) = 0;
> +};
> +
> +template<typename T, typename... Args>
> +class BoundMemberMethod : public BoundMethodArgs<Args...>
> +{
> +public:
> +	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> +
> +	BoundMemberMethod(T *obj, Object *object, void (T::*func)(Args...))
> +		: BoundMethodArgs<Args...>(obj, object), func_(func) {}
> +
> +	void activate(Args... args)
> +	{
> +		if (this->object_)
> +			BoundMethodBase::activatePack(new PackType{ args... });
> +		else
> +			(static_cast<T *>(this->obj_)->*func_)(args...);
> +	}
> +
> +	void invoke(Args... args)
> +	{
> +		(static_cast<T *>(this->obj_)->*func_)(args...);
> +	}
> +
> +private:
> +	friend class Signal<Args...>;
> +	void (T::*func_)(Args...);
> +};
> +
> +template<typename... Args>
> +class BoundStaticMethod : public BoundMethodArgs<Args...>
> +{
> +public:
> +	BoundStaticMethod(void (*func)(Args...))
> +		: BoundMethodArgs<Args...>(nullptr, nullptr), func_(func) {}
> +
> +	void activate(Args... args) { (*func_)(args...); }
> +	void invoke(Args... args) {}
> +
> +private:
> +	friend class Signal<Args...>;
> +	void (*func_)(Args...);
> +};
> +
> +}; /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_BOUND_METHOD_H__ */
> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
> index 920eb5fcbfd1..a8a38a9b1db7 100644
> --- a/include/libcamera/meson.build
> +++ b/include/libcamera/meson.build
> @@ -1,4 +1,5 @@
>  libcamera_api = files([
> +    'bound_method.h',
>      'buffer.h',
>      'camera.h',
>      'camera_manager.h',
> diff --git a/include/libcamera/object.h b/include/libcamera/object.h
> index 3d08d69a9044..e3b39cf547b0 100644
> --- a/include/libcamera/object.h
> +++ b/include/libcamera/object.h
> @@ -10,13 +10,14 @@
>  #include <list>
>  #include <memory>
>  
> +#include <libcamera/bound_method.h>
> +
>  namespace libcamera {
>  
>  class Message;
>  template<typename... Args>
>  class Signal;
>  class SignalBase;
> -class SlotBase;
>  class Thread;
>  
>  class Object
> @@ -36,7 +37,7 @@ protected:
>  private:
>  	template<typename... Args>
>  	friend class Signal;
> -	friend class SlotBase;
> +	friend class BoundMethodBase;
>  	friend class Thread;
>  
>  	void connect(SignalBase *signal);
> diff --git a/include/libcamera/signal.h b/include/libcamera/signal.h
> index 8f6db700cd80..3b6de30f7d35 100644
> --- a/include/libcamera/signal.h
> +++ b/include/libcamera/signal.h
> @@ -8,127 +8,14 @@
>  #define __LIBCAMERA_SIGNAL_H__
>  
>  #include <list>
> -#include <tuple>
>  #include <type_traits>
>  #include <vector>
>  
> +#include <libcamera/bound_method.h>
>  #include <libcamera/object.h>
>  
>  namespace libcamera {
>  
> -template<typename... Args>
> -class Signal;
> -class SignalBase;
> -
> -class SlotBase
> -{
> -public:
> -	SlotBase(void *obj, Object *object)
> -		: obj_(obj), object_(object) {}
> -	virtual ~SlotBase() {}
> -
> -	template<typename T, typename std::enable_if<!std::is_same<Object, T>::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<typename... Args>
> -class SlotArgs : public SlotBase
> -{
> -private:
> -#ifndef __DOXYGEN__
> -	/*
> -	 * This is a cheap partial implementation of std::integer_sequence<>
> -	 * from C++14.
> -	 */
> -	template<int...>
> -	struct sequence {
> -	};
> -
> -	template<int N, int... S>
> -	struct generator : generator<N-1, N-1, S...> {
> -	};
> -
> -	template<int... S>
> -	struct generator<0, S...> {
> -		typedef sequence<S...> type;
> -	};
> -#endif
> -
> -	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> -
> -	template<int... S>
> -	void invokePack(void *pack, sequence<S...>)
> -	{
> -		PackType *args = static_cast<PackType *>(pack);
> -		invoke(std::get<S>(*args)...);
> -		delete args;
> -	}
> -
> -public:
> -	SlotArgs(void *obj, Object *object)
> -		: SlotBase(obj, object) {}
> -
> -	void invokePack(void *pack) override
> -	{
> -		invokePack(pack, typename generator<sizeof...(Args)>::type());
> -	}
> -
> -	virtual void activate(Args... args) = 0;
> -	virtual void invoke(Args... args) = 0;
> -};
> -
> -template<typename T, typename... Args>
> -class SlotMember : public SlotArgs<Args...>
> -{
> -public:
> -	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
> -
> -	SlotMember(T *obj, Object *object, void (T::*func)(Args...))
> -		: SlotArgs<Args...>(obj, object), func_(func) {}
> -
> -	void activate(Args... args)
> -	{
> -		if (this->object_)
> -			SlotBase::activatePack(new PackType{ args... });
> -		else
> -			(static_cast<T *>(this->obj_)->*func_)(args...);
> -	}
> -
> -	void invoke(Args... args)
> -	{
> -		(static_cast<T *>(this->obj_)->*func_)(args...);
> -	}
> -
> -private:
> -	friend class Signal<Args...>;
> -	void (T::*func_)(Args...);
> -};
> -
> -template<typename... Args>
> -class SlotStatic : public SlotArgs<Args...>
> -{
> -public:
> -	SlotStatic(void (*func)(Args...))
> -		: SlotArgs<Args...>(nullptr, nullptr), func_(func) {}
> -
> -	void activate(Args... args) { (*func_)(args...); }
> -	void invoke(Args... args) {}
> -
> -private:
> -	friend class Signal<Args...>;
> -	void (*func_)(Args...);
> -};
> -
>  class SignalBase
>  {
>  public:
> @@ -136,7 +23,7 @@ public:
>  	void disconnect(T *obj)
>  	{
>  		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
> -			SlotBase *slot = *iter;
> +			BoundMethodBase *slot = *iter;
>  			if (slot->match(obj)) {
>  				iter = slots_.erase(iter);
>  				delete slot;
> @@ -148,7 +35,7 @@ public:
>  
>  protected:
>  	friend class Object;
> -	std::list<SlotBase *> slots_;
> +	std::list<BoundMethodBase *> slots_;
>  };
>  
>  template<typename... Args>
> @@ -158,7 +45,7 @@ public:
>  	Signal() {}
>  	~Signal()
>  	{
> -		for (SlotBase *slot : slots_) {
> +		for (BoundMethodBase *slot : slots_) {
>  			slot->disconnect(this);
>  			delete slot;
>  		}
> @@ -170,7 +57,7 @@ public:
>  	{
>  		Object *object = static_cast<Object *>(obj);
>  		object->connect(this);
> -		slots_.push_back(new SlotMember<T, Args...>(obj, object, func));
> +		slots_.push_back(new BoundMemberMethod<T, Args...>(obj, object, func));
>  	}
>  
>  	template<typename T, typename std::enable_if<!std::is_base_of<Object, T>::value>::type * = nullptr>
> @@ -179,17 +66,17 @@ public:
>  #endif
>  	void connect(T *obj, void (T::*func)(Args...))
>  	{
> -		slots_.push_back(new SlotMember<T, Args...>(obj, nullptr, func));
> +		slots_.push_back(new BoundMemberMethod<T, Args...>(obj, nullptr, func));
>  	}
>  
>  	void connect(void (*func)(Args...))
>  	{
> -		slots_.push_back(new SlotStatic<Args...>(func));
> +		slots_.push_back(new BoundStaticMethod<Args...>(func));
>  	}
>  
>  	void disconnect()
>  	{
> -		for (SlotBase *slot : slots_)
> +		for (BoundMethodBase *slot : slots_)
>  			delete slot;
>  		slots_.clear();
>  	}
> @@ -204,15 +91,15 @@ public:
>  	void disconnect(T *obj, void (T::*func)(Args...))
>  	{
>  		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
> -			SlotArgs<Args...> *slot = static_cast<SlotArgs<Args...> *>(*iter);
> +			BoundMethodArgs<Args...> *slot = static_cast<BoundMethodArgs<Args...> *>(*iter);
>  			/*
>  			 * If the object matches the slot, the slot is
>  			 * guaranteed to be a member slot, so we can safely
> -			 * cast it to SlotMember<T, Args...> and access its
> +			 * cast it to BoundMemberMethod<T, Args...> and access its
>  			 * func_ member.
>  			 */
>  			if (slot->match(obj) &&
> -			    static_cast<SlotMember<T, Args...> *>(slot)->func_ == func) {
> +			    static_cast<BoundMemberMethod<T, Args...> *>(slot)->func_ == func) {
>  				iter = slots_.erase(iter);
>  				delete slot;
>  			} else {
> @@ -224,9 +111,9 @@ public:
>  	void disconnect(void (*func)(Args...))
>  	{
>  		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
> -			SlotArgs<Args...> *slot = *iter;
> +			BoundMethodArgs<Args...> *slot = *iter;
>  			if (slot->match(nullptr) &&
> -			    static_cast<SlotStatic<Args...> *>(slot)->func_ == func) {
> +			    static_cast<BoundStaticMethod<Args...> *>(slot)->func_ == func) {
>  				iter = slots_.erase(iter);
>  				delete slot;
>  			} else {
> @@ -241,9 +128,9 @@ public:
>  		 * Make a copy of the slots list as the slot could call the
>  		 * disconnect operation, invalidating the iterator.
>  		 */
> -		std::vector<SlotBase *> slots{ slots_.begin(), slots_.end() };
> -		for (SlotBase *slot : slots)
> -			static_cast<SlotArgs<Args...> *>(slot)->activate(args...);
> +		std::vector<BoundMethodBase *> slots{ slots_.begin(), slots_.end() };
> +		for (BoundMethodBase *slot : slots)
> +			static_cast<BoundMethodArgs<Args...> *>(slot)->activate(args...);
>  	}
>  };
>  
> diff --git a/src/libcamera/bound_method.cpp b/src/libcamera/bound_method.cpp
> new file mode 100644
> index 000000000000..0a2d61a6c805
> --- /dev/null
> +++ b/src/libcamera/bound_method.cpp
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * bound_method.cpp - Method bind and invocation
> + */
> +
> +#include <libcamera/bound_method.h>
> +
> +#include "message.h"
> +#include "thread.h"
> +#include "utils.h"
> +
> +namespace libcamera {
> +
> +void BoundMethodBase::disconnect(SignalBase *signal)
> +{
> +	if (object_)
> +		object_->disconnect(signal);
> +}
> +
> +void BoundMethodBase::activatePack(void *pack)
> +{
> +	if (Thread::current() == object_->thread()) {
> +		invokePack(pack);
> +	} else {
> +		std::unique_ptr<Message> msg =
> +			utils::make_unique<SignalMessage>(this, pack);
> +		object_->postMessage(std::move(msg));
> +	}
> +}
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/include/message.h b/src/libcamera/include/message.h
> index 416fe74b10ad..b4670c0eab76 100644
> --- a/src/libcamera/include/message.h
> +++ b/src/libcamera/include/message.h
> @@ -11,8 +11,8 @@
>  
>  namespace libcamera {
>  
> +class BoundMethodBase;
>  class Object;
> -class SlotBase;
>  class Thread;
>  
>  class Message
> @@ -44,12 +44,12 @@ private:
>  class SignalMessage : public Message
>  {
>  public:
> -	SignalMessage(SlotBase *slot, void *pack)
> -		: Message(Message::SignalMessage), slot_(slot), pack_(pack)
> +	SignalMessage(BoundMethodBase *method, void *pack)
> +		: Message(Message::SignalMessage), method_(method), pack_(pack)
>  	{
>  	}
>  
> -	SlotBase *slot_;
> +	BoundMethodBase *method_;
>  	void *pack_;
>  };
>  
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 7d5d3c04fba0..c5d8f116ffc6 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -1,4 +1,5 @@
>  libcamera_sources = files([
> +    'bound_method.cpp',
>      'buffer.cpp',
>      'camera.cpp',
>      'camera_manager.cpp',
> diff --git a/src/libcamera/message.cpp b/src/libcamera/message.cpp
> index d44d2a4c73a8..8d3376d8a533 100644
> --- a/src/libcamera/message.cpp
> +++ b/src/libcamera/message.cpp
> @@ -114,12 +114,12 @@ Message::Type Message::registerMessageType()
>  /**
>   * \fn SignalMessage::SignalMessage()
>   * \brief Construct a SignalMessage
> - * \param[in] slot The slot that the signal targets
> + * \param[in] method The slot that the signal targets
>   * \param[in] pack The signal arguments
>   */
>  
>  /**
> - * \var SignalMessage::slot_
> + * \var SignalMessage::method_
>   * \brief The slot that the signal targets
>   */
>  
> diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp
> index 61787fadac0b..0adbc203add8 100644
> --- a/src/libcamera/object.cpp
> +++ b/src/libcamera/object.cpp
> @@ -90,7 +90,7 @@ void Object::message(Message *msg)
>  	switch (msg->type()) {
>  	case Message::SignalMessage: {
>  		SignalMessage *smsg = static_cast<SignalMessage *>(msg);
> -		smsg->slot_->invokePack(smsg->pack_);
> +		smsg->method_->invokePack(smsg->pack_);
>  		break;
>  	}
>  
> diff --git a/src/libcamera/signal.cpp b/src/libcamera/signal.cpp
> index ab7dba508dfb..6ee348acc8d4 100644
> --- a/src/libcamera/signal.cpp
> +++ b/src/libcamera/signal.cpp
> @@ -7,10 +7,6 @@
>  
>  #include <libcamera/signal.h>
>  
> -#include "message.h"
> -#include "thread.h"
> -#include "utils.h"
> -
>  /**
>   * \file signal.h
>   * \brief Signal & slot implementation
> @@ -57,25 +53,6 @@ namespace libcamera {
>   * passed through the signal will remain valid after the signal is emitted.
>   */
>  
> -void SlotBase::disconnect(SignalBase *signal)
> -{
> -	if (object_)
> -		object_->disconnect(signal);
> -}
> -
> -void SlotBase::activatePack(void *pack)
> -{
> -	Object *obj = static_cast<Object *>(object_);
> -
> -	if (Thread::current() == obj->thread()) {
> -		invokePack(pack);
> -	} else {
> -		std::unique_ptr<Message> msg =
> -			utils::make_unique<SignalMessage>(this, pack);
> -		obj->postMessage(std::move(msg));
> -	}
> -}
> -
>  /**
>   * \fn Signal::connect(T *object, void(T::*func)(Args...))
>   * \brief Connect the signal to a member function slot
> -- 
> Regards,
> 
> Laurent Pinchart
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

Patch

diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
index 3d94623a4b8f..a9596c2a32d8 100644
--- a/Documentation/Doxyfile.in
+++ b/Documentation/Doxyfile.in
@@ -865,11 +865,11 @@  EXCLUDE_PATTERNS       =
 # Note that the wildcards are matched against the file with absolute path, so to
 # exclude all test directories use the pattern */test/*
 
-EXCLUDE_SYMBOLS        = libcamera::SignalBase \
-                         libcamera::SlotArgs \
-                         libcamera::SlotBase \
-                         libcamera::SlotMember \
-                         libcamera::SlotStatic \
+EXCLUDE_SYMBOLS        = libcamera::BoundMemberMethod \
+                         libcamera::BoundMethodArgs \
+                         libcamera::BoundMethodBase \
+                         libcamera::BoundStaticMethod \
+                         libcamera::SignalBase \
                          std::*
 
 # The EXAMPLE_PATH tag can be used to specify one or more files or directories
diff --git a/include/libcamera/bound_method.h b/include/libcamera/bound_method.h
new file mode 100644
index 000000000000..38c44b923ba1
--- /dev/null
+++ b/include/libcamera/bound_method.h
@@ -0,0 +1,131 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * bound_method.h - Method bind and invocation
+ */
+#ifndef __LIBCAMERA_BOUND_METHOD_H__
+#define __LIBCAMERA_BOUND_METHOD_H__
+
+#include <tuple>
+#include <type_traits>
+
+namespace libcamera {
+
+class Object;
+template<typename... Args>
+class Signal;
+class SignalBase;
+
+class BoundMethodBase
+{
+public:
+	BoundMethodBase(void *obj, Object *object)
+		: obj_(obj), object_(object) {}
+	virtual ~BoundMethodBase() {}
+
+	template<typename T, typename std::enable_if<!std::is_same<Object, T>::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<typename... Args>
+class BoundMethodArgs : public BoundMethodBase
+{
+private:
+#ifndef __DOXYGEN__
+	/*
+	 * This is a cheap partial implementation of std::integer_sequence<>
+	 * from C++14.
+	 */
+	template<int...>
+	struct sequence {
+	};
+
+	template<int N, int... S>
+	struct generator : generator<N-1, N-1, S...> {
+	};
+
+	template<int... S>
+	struct generator<0, S...> {
+		typedef sequence<S...> type;
+	};
+#endif
+
+	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
+
+	template<int... S>
+	void invokePack(void *pack, sequence<S...>)
+	{
+		PackType *args = static_cast<PackType *>(pack);
+		invoke(std::get<S>(*args)...);
+		delete args;
+	}
+
+public:
+	BoundMethodArgs(void *obj, Object *object)
+		: BoundMethodBase(obj, object) {}
+
+	void invokePack(void *pack) override
+	{
+		invokePack(pack, typename generator<sizeof...(Args)>::type());
+	}
+
+	virtual void activate(Args... args) = 0;
+	virtual void invoke(Args... args) = 0;
+};
+
+template<typename T, typename... Args>
+class BoundMemberMethod : public BoundMethodArgs<Args...>
+{
+public:
+	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
+
+	BoundMemberMethod(T *obj, Object *object, void (T::*func)(Args...))
+		: BoundMethodArgs<Args...>(obj, object), func_(func) {}
+
+	void activate(Args... args)
+	{
+		if (this->object_)
+			BoundMethodBase::activatePack(new PackType{ args... });
+		else
+			(static_cast<T *>(this->obj_)->*func_)(args...);
+	}
+
+	void invoke(Args... args)
+	{
+		(static_cast<T *>(this->obj_)->*func_)(args...);
+	}
+
+private:
+	friend class Signal<Args...>;
+	void (T::*func_)(Args...);
+};
+
+template<typename... Args>
+class BoundStaticMethod : public BoundMethodArgs<Args...>
+{
+public:
+	BoundStaticMethod(void (*func)(Args...))
+		: BoundMethodArgs<Args...>(nullptr, nullptr), func_(func) {}
+
+	void activate(Args... args) { (*func_)(args...); }
+	void invoke(Args... args) {}
+
+private:
+	friend class Signal<Args...>;
+	void (*func_)(Args...);
+};
+
+}; /* namespace libcamera */
+
+#endif /* __LIBCAMERA_BOUND_METHOD_H__ */
diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
index 920eb5fcbfd1..a8a38a9b1db7 100644
--- a/include/libcamera/meson.build
+++ b/include/libcamera/meson.build
@@ -1,4 +1,5 @@ 
 libcamera_api = files([
+    'bound_method.h',
     'buffer.h',
     'camera.h',
     'camera_manager.h',
diff --git a/include/libcamera/object.h b/include/libcamera/object.h
index 3d08d69a9044..e3b39cf547b0 100644
--- a/include/libcamera/object.h
+++ b/include/libcamera/object.h
@@ -10,13 +10,14 @@ 
 #include <list>
 #include <memory>
 
+#include <libcamera/bound_method.h>
+
 namespace libcamera {
 
 class Message;
 template<typename... Args>
 class Signal;
 class SignalBase;
-class SlotBase;
 class Thread;
 
 class Object
@@ -36,7 +37,7 @@  protected:
 private:
 	template<typename... Args>
 	friend class Signal;
-	friend class SlotBase;
+	friend class BoundMethodBase;
 	friend class Thread;
 
 	void connect(SignalBase *signal);
diff --git a/include/libcamera/signal.h b/include/libcamera/signal.h
index 8f6db700cd80..3b6de30f7d35 100644
--- a/include/libcamera/signal.h
+++ b/include/libcamera/signal.h
@@ -8,127 +8,14 @@ 
 #define __LIBCAMERA_SIGNAL_H__
 
 #include <list>
-#include <tuple>
 #include <type_traits>
 #include <vector>
 
+#include <libcamera/bound_method.h>
 #include <libcamera/object.h>
 
 namespace libcamera {
 
-template<typename... Args>
-class Signal;
-class SignalBase;
-
-class SlotBase
-{
-public:
-	SlotBase(void *obj, Object *object)
-		: obj_(obj), object_(object) {}
-	virtual ~SlotBase() {}
-
-	template<typename T, typename std::enable_if<!std::is_same<Object, T>::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<typename... Args>
-class SlotArgs : public SlotBase
-{
-private:
-#ifndef __DOXYGEN__
-	/*
-	 * This is a cheap partial implementation of std::integer_sequence<>
-	 * from C++14.
-	 */
-	template<int...>
-	struct sequence {
-	};
-
-	template<int N, int... S>
-	struct generator : generator<N-1, N-1, S...> {
-	};
-
-	template<int... S>
-	struct generator<0, S...> {
-		typedef sequence<S...> type;
-	};
-#endif
-
-	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
-
-	template<int... S>
-	void invokePack(void *pack, sequence<S...>)
-	{
-		PackType *args = static_cast<PackType *>(pack);
-		invoke(std::get<S>(*args)...);
-		delete args;
-	}
-
-public:
-	SlotArgs(void *obj, Object *object)
-		: SlotBase(obj, object) {}
-
-	void invokePack(void *pack) override
-	{
-		invokePack(pack, typename generator<sizeof...(Args)>::type());
-	}
-
-	virtual void activate(Args... args) = 0;
-	virtual void invoke(Args... args) = 0;
-};
-
-template<typename T, typename... Args>
-class SlotMember : public SlotArgs<Args...>
-{
-public:
-	using PackType = std::tuple<typename std::remove_reference<Args>::type...>;
-
-	SlotMember(T *obj, Object *object, void (T::*func)(Args...))
-		: SlotArgs<Args...>(obj, object), func_(func) {}
-
-	void activate(Args... args)
-	{
-		if (this->object_)
-			SlotBase::activatePack(new PackType{ args... });
-		else
-			(static_cast<T *>(this->obj_)->*func_)(args...);
-	}
-
-	void invoke(Args... args)
-	{
-		(static_cast<T *>(this->obj_)->*func_)(args...);
-	}
-
-private:
-	friend class Signal<Args...>;
-	void (T::*func_)(Args...);
-};
-
-template<typename... Args>
-class SlotStatic : public SlotArgs<Args...>
-{
-public:
-	SlotStatic(void (*func)(Args...))
-		: SlotArgs<Args...>(nullptr, nullptr), func_(func) {}
-
-	void activate(Args... args) { (*func_)(args...); }
-	void invoke(Args... args) {}
-
-private:
-	friend class Signal<Args...>;
-	void (*func_)(Args...);
-};
-
 class SignalBase
 {
 public:
@@ -136,7 +23,7 @@  public:
 	void disconnect(T *obj)
 	{
 		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
-			SlotBase *slot = *iter;
+			BoundMethodBase *slot = *iter;
 			if (slot->match(obj)) {
 				iter = slots_.erase(iter);
 				delete slot;
@@ -148,7 +35,7 @@  public:
 
 protected:
 	friend class Object;
-	std::list<SlotBase *> slots_;
+	std::list<BoundMethodBase *> slots_;
 };
 
 template<typename... Args>
@@ -158,7 +45,7 @@  public:
 	Signal() {}
 	~Signal()
 	{
-		for (SlotBase *slot : slots_) {
+		for (BoundMethodBase *slot : slots_) {
 			slot->disconnect(this);
 			delete slot;
 		}
@@ -170,7 +57,7 @@  public:
 	{
 		Object *object = static_cast<Object *>(obj);
 		object->connect(this);
-		slots_.push_back(new SlotMember<T, Args...>(obj, object, func));
+		slots_.push_back(new BoundMemberMethod<T, Args...>(obj, object, func));
 	}
 
 	template<typename T, typename std::enable_if<!std::is_base_of<Object, T>::value>::type * = nullptr>
@@ -179,17 +66,17 @@  public:
 #endif
 	void connect(T *obj, void (T::*func)(Args...))
 	{
-		slots_.push_back(new SlotMember<T, Args...>(obj, nullptr, func));
+		slots_.push_back(new BoundMemberMethod<T, Args...>(obj, nullptr, func));
 	}
 
 	void connect(void (*func)(Args...))
 	{
-		slots_.push_back(new SlotStatic<Args...>(func));
+		slots_.push_back(new BoundStaticMethod<Args...>(func));
 	}
 
 	void disconnect()
 	{
-		for (SlotBase *slot : slots_)
+		for (BoundMethodBase *slot : slots_)
 			delete slot;
 		slots_.clear();
 	}
@@ -204,15 +91,15 @@  public:
 	void disconnect(T *obj, void (T::*func)(Args...))
 	{
 		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
-			SlotArgs<Args...> *slot = static_cast<SlotArgs<Args...> *>(*iter);
+			BoundMethodArgs<Args...> *slot = static_cast<BoundMethodArgs<Args...> *>(*iter);
 			/*
 			 * If the object matches the slot, the slot is
 			 * guaranteed to be a member slot, so we can safely
-			 * cast it to SlotMember<T, Args...> and access its
+			 * cast it to BoundMemberMethod<T, Args...> and access its
 			 * func_ member.
 			 */
 			if (slot->match(obj) &&
-			    static_cast<SlotMember<T, Args...> *>(slot)->func_ == func) {
+			    static_cast<BoundMemberMethod<T, Args...> *>(slot)->func_ == func) {
 				iter = slots_.erase(iter);
 				delete slot;
 			} else {
@@ -224,9 +111,9 @@  public:
 	void disconnect(void (*func)(Args...))
 	{
 		for (auto iter = slots_.begin(); iter != slots_.end(); ) {
-			SlotArgs<Args...> *slot = *iter;
+			BoundMethodArgs<Args...> *slot = *iter;
 			if (slot->match(nullptr) &&
-			    static_cast<SlotStatic<Args...> *>(slot)->func_ == func) {
+			    static_cast<BoundStaticMethod<Args...> *>(slot)->func_ == func) {
 				iter = slots_.erase(iter);
 				delete slot;
 			} else {
@@ -241,9 +128,9 @@  public:
 		 * Make a copy of the slots list as the slot could call the
 		 * disconnect operation, invalidating the iterator.
 		 */
-		std::vector<SlotBase *> slots{ slots_.begin(), slots_.end() };
-		for (SlotBase *slot : slots)
-			static_cast<SlotArgs<Args...> *>(slot)->activate(args...);
+		std::vector<BoundMethodBase *> slots{ slots_.begin(), slots_.end() };
+		for (BoundMethodBase *slot : slots)
+			static_cast<BoundMethodArgs<Args...> *>(slot)->activate(args...);
 	}
 };
 
diff --git a/src/libcamera/bound_method.cpp b/src/libcamera/bound_method.cpp
new file mode 100644
index 000000000000..0a2d61a6c805
--- /dev/null
+++ b/src/libcamera/bound_method.cpp
@@ -0,0 +1,33 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * bound_method.cpp - Method bind and invocation
+ */
+
+#include <libcamera/bound_method.h>
+
+#include "message.h"
+#include "thread.h"
+#include "utils.h"
+
+namespace libcamera {
+
+void BoundMethodBase::disconnect(SignalBase *signal)
+{
+	if (object_)
+		object_->disconnect(signal);
+}
+
+void BoundMethodBase::activatePack(void *pack)
+{
+	if (Thread::current() == object_->thread()) {
+		invokePack(pack);
+	} else {
+		std::unique_ptr<Message> msg =
+			utils::make_unique<SignalMessage>(this, pack);
+		object_->postMessage(std::move(msg));
+	}
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/include/message.h b/src/libcamera/include/message.h
index 416fe74b10ad..b4670c0eab76 100644
--- a/src/libcamera/include/message.h
+++ b/src/libcamera/include/message.h
@@ -11,8 +11,8 @@ 
 
 namespace libcamera {
 
+class BoundMethodBase;
 class Object;
-class SlotBase;
 class Thread;
 
 class Message
@@ -44,12 +44,12 @@  private:
 class SignalMessage : public Message
 {
 public:
-	SignalMessage(SlotBase *slot, void *pack)
-		: Message(Message::SignalMessage), slot_(slot), pack_(pack)
+	SignalMessage(BoundMethodBase *method, void *pack)
+		: Message(Message::SignalMessage), method_(method), pack_(pack)
 	{
 	}
 
-	SlotBase *slot_;
+	BoundMethodBase *method_;
 	void *pack_;
 };
 
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 7d5d3c04fba0..c5d8f116ffc6 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -1,4 +1,5 @@ 
 libcamera_sources = files([
+    'bound_method.cpp',
     'buffer.cpp',
     'camera.cpp',
     'camera_manager.cpp',
diff --git a/src/libcamera/message.cpp b/src/libcamera/message.cpp
index d44d2a4c73a8..8d3376d8a533 100644
--- a/src/libcamera/message.cpp
+++ b/src/libcamera/message.cpp
@@ -114,12 +114,12 @@  Message::Type Message::registerMessageType()
 /**
  * \fn SignalMessage::SignalMessage()
  * \brief Construct a SignalMessage
- * \param[in] slot The slot that the signal targets
+ * \param[in] method The slot that the signal targets
  * \param[in] pack The signal arguments
  */
 
 /**
- * \var SignalMessage::slot_
+ * \var SignalMessage::method_
  * \brief The slot that the signal targets
  */
 
diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp
index 61787fadac0b..0adbc203add8 100644
--- a/src/libcamera/object.cpp
+++ b/src/libcamera/object.cpp
@@ -90,7 +90,7 @@  void Object::message(Message *msg)
 	switch (msg->type()) {
 	case Message::SignalMessage: {
 		SignalMessage *smsg = static_cast<SignalMessage *>(msg);
-		smsg->slot_->invokePack(smsg->pack_);
+		smsg->method_->invokePack(smsg->pack_);
 		break;
 	}
 
diff --git a/src/libcamera/signal.cpp b/src/libcamera/signal.cpp
index ab7dba508dfb..6ee348acc8d4 100644
--- a/src/libcamera/signal.cpp
+++ b/src/libcamera/signal.cpp
@@ -7,10 +7,6 @@ 
 
 #include <libcamera/signal.h>
 
-#include "message.h"
-#include "thread.h"
-#include "utils.h"
-
 /**
  * \file signal.h
  * \brief Signal & slot implementation
@@ -57,25 +53,6 @@  namespace libcamera {
  * passed through the signal will remain valid after the signal is emitted.
  */
 
-void SlotBase::disconnect(SignalBase *signal)
-{
-	if (object_)
-		object_->disconnect(signal);
-}
-
-void SlotBase::activatePack(void *pack)
-{
-	Object *obj = static_cast<Object *>(object_);
-
-	if (Thread::current() == obj->thread()) {
-		invokePack(pack);
-	} else {
-		std::unique_ptr<Message> msg =
-			utils::make_unique<SignalMessage>(this, pack);
-		obj->postMessage(std::move(msg));
-	}
-}
-
 /**
  * \fn Signal::connect(T *object, void(T::*func)(Args...))
  * \brief Connect the signal to a member function slot