Получить тип шаблона класса в шаблоне посетителя
Я пытаюсь написать систему обмена сообщениями с шаблонами, шаблонами посетителей и с помощью CRTP. Я понимаю эти концепции, но, тем не менее, я нахожусь в ситуации, когда мне нужно вернуть "потерянный" тип. у меня есть Base
класс, и я хочу найти Derived<T>
, Это "два" типа, чтобы вывести [Derived
может быть что угодно, T
может быть чем угодно] (даже если это рассматривается как один тип).
Я попытался использовать второй шаблон посетителей, который кажется тяжелым и сумасшедшим, но я не нашел никакого рабочего решения. Даже если это связано с игрой, это просто в качестве примера, его можно применить к другим программам, я полагаю, я не могу представить его в другом контексте.
Вот название, которое я использовал (с ненужными примерами):
SubscriberBase
это класс, который отправляет и получает сообщения (например, сетевой клиент)Broadcaster
является мостом между подписчиками (например, сетевой коммутатор / сервер) и содержит векторSubscriberBase
,
Я придумал этот минимальный код:
class SubscriberBase {};
class Broadcaster {
std::vector<SubscriberBase*> subscribers;
public:
template<typename TMessage>
void broadcast(TMessage& message) {
for(auto subscriber : subscribers) {
// <<< Here is my problem <<<
message.accept<THE_ACTUAL_SUBSCRIBER_TYPE>(subscriber);
}
}
void attach(SubscriberBase* subscriber) {
subscribers.push_back(subscriber);
}
};
//Base class for handling messages of any type
template<typename TMessage>
class MessageHandler {
public:
virtual void handleMessage(TMessage& message) {}
};
//Base class for messages
template<typename TMessage>
class Message {
friend class Broadcaster;
private:
//Visitor pattern with CRTP
template<typename TSubscriber>
void accept(TSubscriber* subscriber) {
subscriber->handleMessage(*static_cast<TMessage*>(this));
}
};
//Messages
struct EntityCreated : public Message<EntityCreated> {};
struct EntityDestroyed : public Message<EntityDestroyed> {};
struct BurnAllGummyBears : public Message<BurnAllGummyBears> {};
//Subscribers
class EntityCache : public SubscriberBase,
public MessageHandler<EntityCreated>,
public MessageHandler<EntityDestroyed>,
public MessageHandler<BurnAllGummyBears>
{
public:
void handleMessage(EntityCreated& message) override { /* add to cache */ }
void handleMessage(EntityDestroyed& message) override { /* remove from cache */ }
//does not override BurnAllGummyBears because it's not relevant for EntityCache
};
Проблема в типе THE_ACTUAL_SUBSCRIBER_TYPE
, Это может быть любой "конкретный" абонент; в этом случае это будет, например, EntityCache
или что-то подобное Logger
, CommandRecorder
...
Я попытался использовать другой CRTP в сочетании с другим классом:
class SubscriberBase {};
template<typename TSubscriber>
class Subscriber : public SubscriberBase { /* some other visitor pattern ? */ };
class EntityCache : public Subscriber<EntityCache>, /* ... */
без успеха.
Все идеи приветствуются, спасибо:)
1 ответ
Я отказался от этой идеи и подумал: "Ну, ПОЦЕЛУЙ мой…", поэтому я решил пойти по более простому проекту, нормальному и чистому подходу для нормального и чистого кода.
//Messages
struct EntityCreated {};
struct EntityDestroyed {};
struct ChopAllTrees {};
struct MakeGummyBearsEvil {};
//Subscriber is now a base class containing all of the message handlers
class Subscriber {
public:
virtual void handleMessage(EntityCreated& msg) {}
virtual void handleMessage(EntityDestroyed& msg) {}
virtual void handleMessage(ChopAllTrees& msg) {}
virtual void handleMessage(MakeGummyBearsEvil& msg) {}
};
class Broadcaster {
std::vector<Subscriber*> subscribers;
public:
template<typename M>
void broadcast(M& msg) {
for(auto subscriber : subscribers) {
subscriber->handleMessage(msg);
}
}
template<typename M>
void broadcast(M&& msg) {
M owner(msg);
broadcast(owner);
}
void attach(Subscriber* subscriber) {
auto it = std::find(subscribers.begin(), subscribers.end(), subscriber);
if(it == subscribers.end()) {
subscribers.push_back(subscriber);
}
}
void detach(Subscriber* subscriber) {
auto it = std::find(subscribers.begin(), subscribers.end(), subscriber);
if(it != subscribers.end()) {
subscribers.erase(it);
}
}
};
//Subscribers simply inherits from Subscriber and overrides interesting handlers
class EntityCache : public Subscriber {
void handleMessage(EntityCreated& msg) override {
std::cout << "Handling creation\n";
}
void handleMessage(EntityDestroyed& msg) override {
std::cout << "Handling destruction\n";
}
};
class Eviler : public Subscriber {
void handleMessage(MakeGummyBearsEvil& msg) override {
std::cout << "Gummy bears are now evil!\n";
}
};
int main() {
EntityCache entityCache;
Eviler eviler;
Broadcaster broadcaster;
broadcaster.attach(&entityCache);
broadcaster.attach(&eviler);
EntityCreated entityCreated;
broadcaster.broadcast(entityCreated); //lvalue test
broadcaster.broadcast(MakeGummyBearsEvil()); //rvalue test
broadcaster.detach(&eviler);
broadcaster.broadcast(EntityDestroyed());
broadcaster.broadcast(MakeGummyBearsEvil()); //no effect
}
Я принимаю этот ответ, так как он решает проблему, но, так как мне все еще интересно, как это сделать, если кто-то сталкивался с этой проблемой в прошлом (вероятно, нет) и справился с ней, не стесняйтесь отвечать, и я приму Это.