Я реализую вариант шаблона наблюдателя в C ++.Однако из-за характера моего проекта он НЕ МОЖЕТ ИСПОЛЬЗОВАТЬ ЛЮБЫЕ ФУНКЦИИ ВИРТУАЛЬНОГО УЧАСТНИКА , поскольку совокупные издержки из-за поиска в vtable и пропусков кэша недопустимы.
Нужно ли было создаватьинтерфейсы через виртуальные функции-члены, я бы тривиально написал следующее:
template <class MessageType>
class MessageSubscriber {
public:
virtual void OnMessage(MessageType *message) = 0;
};
template <class MessageType>
class MessagePublisher {
public:
void AddSubscriber(MessageSubscriber<MessageType> *subscriber) {
subscribers.push_back(subscriber);
}
protected:
void Publish(MessageType *message) {
for (auto subscriber : subscribers)
subscriber.OnMessage(message);
}
private:
std::vector<MessageSubscriber<MessageType>*> subscribers;
};
Тогда, например, я мог бы иметь классы, которые реализуют MessageSubscriber
для некоторых MessageType
, SafetyMessage
, например так:
class SafetyMessageSubscriberA : public MessageSubscriber<SafetyMessage> {
public:
virtual void OnMessage(SafetyMessage *message) override {
/* process message */
}
};
class SafetyMessageSubscriberB : public MessageSubscriber<SafetyMessage> {
public:
virtual void OnMessage(SafetyMessage *message) override {
/* process message */
}
};
class SafetyMessagePublisher : public MessagePublisher<SafetyMessage> {
public:
void Run {
/* manipulate message data */
this->Publish(&message);
}
private:
SafetyMessage message;
};
Это позволит выполнить работу, но, как подчеркивалось ранее, издержки поиска в vtable недопустимы в контексте приложения, несмотря на полиморфное удобство, которое оно обеспечивает, и оно также необходимо для приложения.Естественно, тогда я попробовал несколько подходов, сосредоточенных вокруг статического полиморфизма, который можно использовать с помощью шаблонов.
Сначала я попытался использовать CTRP, но в этом случае он не работает, поскольку указатели, содержащиеся в MessagePublisher::subscribers
, должны указывать натот же базовый класс, когда вызывается MessagePublisher::Publish(MessageType *message)
.Следовательно, у вас не может быть некоторого шаблона CTRP по линиям MessageSubscriber<SafetyMessageSubscriberA>
, MessageSubscriber<SafetyMessageSubscriberB>
, поскольку аргументы шаблона должны быть одинаковыми, чтобы оба объекта были юридически разрешены в MessagePublisher::subscribers
.
MyСамая последняя попытка решить эту проблему побудила меня попробовать некоторые варианты специализации шаблонов функций-членов, хотя и безуспешно.Я попробовал следующий вариант интерфейса шаблона:
class MessageSubscriber {
public:
template <class MessageType>
void OnMessage(MessageType *message);
};
class MessagePublisher {
public:
template <class MessageType>
void Publish(MessageType *message) {
for (auto subscriber: subscribers)
subscriber->OnMessage<MessageType>(message);
}
private:
std::vector<MessageSubscriber*> subscribers;
};
template<class MessageType>
void MessageSubscriber::OnMessageOnMessage(MessageType *message) {
/* "interface" call; do nothing */
}
С такими реализациями, как:
class SafetyMessageSubscriberA : public MessageSubscriber {
public:
// declare for legal overload
template <class MessageType>
void OnMessage(MessageType *message);
};
class SafetyMessageSubscriberB : public MessageSubscriber {
public:
// declare for legal overload
template <class MessageType>
void OnMessage(MessageType *message);
};
template<>
void SafetyMessageSubscriberA::OnMessage<SafetyMessage*>OnMessage(SafetyMessage *message) {
/* process message */
}
template<>
void SafetyMessageSubscriberB::OnMessage<SafetyMessage*>OnMessage(SafetyMessage *message) {
/* process message */
}
Однако, когда я попробовал это, MessagePublisher::Publish(SafetyMessage *message)
всегда вызывал бы универсальный MessageSubscriber::OnMessage(MessageType *m)
реализация для базового класса, а не те, которые были реализованы для производных классов, специфичных для SafetyMessage*
.
Неправильно ли я специализирую шаблоны функций по назначению, или есть другое более эффективное решение?Заранее извиняюсь за неточную формулировку, связанную с концепциями перегрузки и специализации шаблонов элементов.