libcamera: signal: Split Slot implementation to reusable classes

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.

This is only a move and rename, no functional changes are included.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
This commit is contained in:
Laurent Pinchart 2019-08-12 02:36:37 +03:00
parent a66e5ca8c6
commit 0e65ed8145
11 changed files with 197 additions and 166 deletions

View file

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

View file

@ -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__ */

View file

@ -1,4 +1,5 @@
libcamera_api = files([
'bound_method.h',
'buffer.h',
'camera.h',
'camera_manager.h',

View file

@ -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);

View file

@ -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...);
}
};

View file

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

View file

@ -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_;
};

View file

@ -1,4 +1,5 @@
libcamera_sources = files([
'bound_method.cpp',
'buffer.cpp',
'camera.cpp',
'camera_manager.cpp',

View file

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

View file

@ -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;
}

View file

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