Фон
Мне кажется, я нашел решение печально известной проблемы шаблонных сигналов и слотов в Qt.Для начала я определил пустой базовый класс Message
, единственное назначение которого - наследоваться и создавать конкретные реализации TMessage
.В Qt, хотя можно подключить сигнал к функтору (в отличие от слота Qt), сигнал не может быть функцией шаблона, поэтому я решил также смоделировать отношение класса Message
, создавабстрактный Messenger
класс, с сигналом void signal_Message(Message)
и template <typename T> void slot_T(TMessage<T>)
.Затем я наткнулся на этот вопрос и этот вопрос, и понял, что это было гораздо более приемлемым решением, и я создал, по сути, мешок Messenger
с.При использовании неподдерживаемого типа четный код завершается с ошибкой во время компиляции с хорошим читаемым сообщением об ошибке: invalid initialization of reference of type Messenger<double>& from expression of type MessengerBag<int, char>
Проблема
Предположим, что мы используем 6 различных типов, int
, float
, char
, double
, некоторые enum
и struct
.Это дает sizeof
96 байт!Однако, учитывая, что желаемое использование предназначено для мест с более чем 50 пользовательскими типами, каждый из которых имеет свои собственные отдельно поддерживаемые контейнеры сигналов и слотов, похоже, что мое решение может выполнить ту же задачу примерно с теми же издержками, но рядом снет затрат на техническое обслуживание.Помимо очевидных преимуществ, связанных с отсутствием необходимости поддерживать 24 отдельных функции, возможно, даже больше в случае схем с запутанным наследованием, есть ли недостатки этого подхода?После некоторого начального тестирования я обнаружил, что MessengerBag
по сути является противоположностью ромбовидного узора.Каждый деструктор называется соответствующим образом, поэтому я не вижу практически никаких недостатков по сравнению с составной структурой, которая потребовала бы другого уровня (который я считаю ненужным и запутывающим) косвенности.Qt по существу презирает виртуальное наследование и запрещает его в классе, который непосредственно наследует QObject: поэтому я не могу сделать Mock виртуальным, как я бы предпочел.
Пример
// Message.h
struct Message {};
template <typename T> struct TMessage {
T t
};
// Messenger.h
struct Messenger : public QObject {
Q_OBJECT
public:
template <typename T> void slot_doSomethingWithMsg(const Message& msg){
const auto& tmsg = static_cast<const TMessage<T>&>(msg);
qDebug() << tmsg.t;
// do something else... this is just example usage
}
signals:
void signal_sendMsg(const BaseMsg& msg);
// MessengerBag.h
template <typename T, typename... Args>
struct MessengerBag : TMessenger<T>, MessengerBag<Args...>{};
template <typename T> struct MessengerBag<T> : TMessenger<T>{};
// Manager.h
MessengerBag<int, char> messengerBag;
Manager(){
QObject::connect(static_cast<TMock<int>*>(messengerBag),
&Mock::signal_sendMsg,
static_cast<TMock<int>*>(messengerBag),
&Mock::slot_doSomethingWithMsg<int>);
QObject::connect(static_cast<TMock<char>*>(messengerBag),
&Mock::signal_sendMsg,
static_cast<TMock<char>*>(messengerBag),
&Mock::slot_doSomethingWithMsg<char>);
template <typename T> void slot_sendMsg(const TMessage<T>& msg){
TMessenger<T>& messenger = messengerBag;
messenger.signal_sendMsg(msg);
}
// main.cpp
auto* manager = new Manager();
TMessage<int> imsg{5};
TMessage<char> cmsg{'f'};
manager->slot_sendMsg(imsg);
manager->slot_sendMsg(cmsg);
Как и ожидалось,Приведенный выше пример печатает 5 и 'f', тем самым устраняя проблему с шаблонными сигналами и слотами.Однако у меня возникли небольшие проблемы с рассмотрением возможных проблем в будущем.Одна проблема, конечно, заключается в том, что QObject
недоступен, потому что QObject
остается неизвестным, который может потенциально обеспечить посредничество композиционного решения (заставить TMessenger
делегировать Messenger
, а MessengerBag
наследовать QObject
в пустом Args
Специализация. Я не решаюсь принять это решение, предварительно не поняв его возможные недостатки. Также не стесняйтесь оставлять комментарии к самому коду.
tl; dr
Сигналы Qt вызывают функторы, но сигналыне может быть шаблонами. Есть ли какие-либо недостатки в том, что объект наследует несколько шаблонных классов, полученных из QObject
, для создания сигналов стиля шаблона с использованием базового класса Message
? Этот Message
является параметром для сигнала(чтобы функтор сигналов и шаблонов имел базовую версию (после чего функтор приводил ее к правильному типу T). Структура будет служить пакетом этих шаблонных классов, унаследовав их все для того, чтобы разрешить передачу вправильный шаблон класса в любое время.