dirty messages

This commit is contained in:
2025-02-05 20:42:46 +01:00
parent 65275a2cc6
commit 6bbc849076
8 changed files with 720 additions and 146 deletions

61
include/sp/Field.h Normal file
View File

@@ -0,0 +1,61 @@
#pragma once
#include <sp/common/DataBuffer.h>
namespace sp {
template <typename ValueType>
class Field {
public:
// Provide an access to the stored value
ValueType& GetValue();
const ValueType& GetValue() const;
// Read (deserialise) and update internal value
void Read(DataBuffer& buffer) {
buffer >> m_Value;
}
// Write (serialise) internal value
void Write(DataBuffer& buffer) const {
buffer << m_Value;
}
Field& operator=(const ValueType& value) {
m_Value = value;
return *this;
}
private:
ValueType m_Value;
};
// Functor used to read all tuple values
class FieldReader {
public:
FieldReader(DataBuffer& buffer) : m_Buffer(buffer) {}
template <typename TField>
void operator()(TField& field) {
field.Read(m_Buffer);
}
private:
DataBuffer& m_Buffer;
};
// Functor used to write all tuple values
class FieldWriter {
public:
FieldWriter(DataBuffer& buffer) : m_Buffer(buffer) {}
template <typename TField>
void operator()(TField& field) {
field.Write(m_Buffer);
}
private:
DataBuffer& m_Buffer;
};
} // namespace sp

View File

@@ -1,38 +1,13 @@
#pragma once #pragma once
#include <memory> #include <sp/MessageInterfaceBuilder.h>
#include <vector>
#include <sp/common/DataBuffer.h>
namespace sp { namespace sp {
class Handler;
class Message { template <typename... TOptions>
class Message : public option::MessageInterfaceBuilder<TOptions...>::Type {
public: public:
virtual ~Message() {} using ParsedOptions = typename option::MessageInterfaceBuilder<TOptions...>::ParsedOptions;
void Write(DataBuffer& buffer) const {
buffer << GetId();
WriteImpl(buffer);
}
void Read(const DataBuffer& buffer) {
ReadImpl(buffer);
}
void Dispatch(Handler& handler) {
DispatchImpl(handler);
}
virtual int GetId() const = 0;
virtual std::string_view GetName() const = 0;
protected:
virtual void DispatchImpl(Handler& handler) = 0;
virtual void WriteImpl(DataBuffer& buffer) const = 0;
virtual void ReadImpl(const DataBuffer& buffer) = 0;
}; };
} // namespace sp } // namespace sp

View File

@@ -3,16 +3,270 @@
#include <sp/Message.h> #include <sp/Message.h>
namespace sp { namespace sp {
namespace option {
template <typename TDerived> // Provide static numeric ID, to facilitate implementation of GetIdImpl()
class MessageBase : public Message { template <std::intmax_t TId>
protected: struct StaticNumIdImpl {};
virtual void DispatchImpl(Handler& handler) override {
handler.Handle(static_cast<TDerived&>(*this));
}
virtual void WriteImpl(DataBuffer& buffer) const = 0; // Facilitate implementation of DispatchImpl()
virtual void ReadImpl(const DataBuffer& buffer) = 0; template <typename TActual>
struct DispatchImpl {};
// Provide fields of the message, facilitate implementation of
// ReadImpl(), WriteImpl(), ValidImpl(), etc...
template <typename TFields>
struct FieldsImpl {};
} // namespace option
template <typename... TOptions>
class MessageImplParsedOptions;
template <>
struct MessageImplParsedOptions<> {
static const bool HasStaticNumIdImpl = false;
static const bool HasDispatchImpl = false;
static const bool HasFieldsImpl = false;
}; };
template <std::intmax_t TId, typename... TOptions>
struct MessageImplParsedOptions<option::StaticNumIdImpl<TId>, TOptions...> : public MessageImplParsedOptions<TOptions...> {
static const bool HasStaticNumIdImpl = true;
static const std::intmax_t MsgId = TId;
};
template <typename TActual, typename... TOptions>
struct MessageImplParsedOptions<option::DispatchImpl<TActual>, TOptions...> : public MessageImplParsedOptions<TOptions...> {
static const bool HasDispatchImpl = true;
using ActualMessage = TActual;
};
template <typename TFields, typename... TOptions>
struct MessageImplParsedOptions<option::FieldsImpl<TFields>, TOptions...> : public MessageImplParsedOptions<TOptions...> {
static const bool HasFieldsImpl = true;
using Fields = TFields;
};
// ID information chunk
template <typename TBase, std::intmax_t TId>
class MessageImplStaticNumIdBase : public TBase {
public:
// Reuse the message ID type defined in the interface
using MsgIdType = typename TBase::MsgIdType;
protected:
virtual MsgIdType GetIdImpl() const override {
return static_cast<MsgIdType>(TId);
}
};
// Dispatch implementation chunk
template <typename TBase, typename TActual>
class MessageImplDispatchBase : public TBase {
public:
// Reuse the Handler type defined in the interface class
using Handler = typename TBase::HandlerType;
protected:
virtual void DispatchImpl(Handler& handler) override {
handler.Handle(static_cast<TActual&>(*this));
}
};
template <typename TBase, typename TFields>
class MessageImplFieldsBase : public TBase {
public:
using AllFields = TFields;
AllFields& GetFields() {
return m_Fields;
}
const AllFields& GetFields() const {
return m_Fields;
}
private:
TFields m_Fields;
};
template <typename TBase>
class MessageImplFieldsReadBase : public TBase {
protected:
void ReadImpl(DataBuffer& buffer) override {
// Access fields via interface provided in previous chunk
auto& allFields = TBase::GetFields();
//... // read all the fields
}
};
template <typename TBase>
class MessageImplFieldsWriteBase : public TBase {
protected:
void WriteImpl(DataBuffer& buffer) override {
// Access fields via interface provided in previous chunk
auto& allFields = TBase::GetFields();
//... // write all the fields
}
};
template <typename TBase>
class MessageImplFieldsValidBase : public TBase {
protected:
bool ValidImpl() const override {
// Access fields via interface provided in previous chunk
// auto& allFields = TBase::GetFields();
//... // validate all the fields
}
};
// id impl
template <typename TBase, typename ParsedImplOptions, bool TImplement>
struct MessageImplProcessStaticNumId;
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessStaticNumId<TBase, ParsedImplOptions, true> {
using Type = MessageImplStaticNumIdBase<TBase, ParsedImplOptions::MsgId>;
};
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessStaticNumId<TBase, ParsedImplOptions, false> {
using Type = TBase;
};
// dispatch impl
template <typename TBase, typename ParsedImplOptions, bool TImplement>
struct MessageImplProcessDispatch;
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessDispatch<TBase, ParsedImplOptions, true> {
using Type = MessageImplDispatchBase<TBase, typename ParsedImplOptions::ActualMessage>;
};
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessDispatch<TBase, ParsedImplOptions, false> {
using Type = TBase;
};
// fields impl
template <typename TBase, typename ParsedImplOptions, bool TImplement>
struct MessageImplProcessFields;
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessFields<TBase, ParsedImplOptions, true> {
using Type = MessageImplFieldsBase<TBase, typename ParsedImplOptions::Fields>;
};
template <typename TBase, typename ParsedImplOptions>
struct MessageImplProcessFields<TBase, ParsedImplOptions, false> {
using Type = TBase;
};
// read impl
template <typename TBase, bool TImplement>
struct MessageImplProcessReadFields;
template <typename TBase>
struct MessageImplProcessReadFields<TBase, true> {
using Type = MessageImplFieldsReadBase<TBase>;
};
template <typename TBase>
struct MessageImplProcessReadFields<TBase, false> {
using Type = TBase;
};
// write impl
template <typename TBase, bool TImplement>
struct MessageImplProcessWriteFields;
template <typename TBase>
struct MessageImplProcessWriteFields<TBase, true> {
using Type = MessageImplFieldsWriteBase<TBase>;
};
template <typename TBase>
struct MessageImplProcessWriteFields<TBase, false> {
using Type = TBase;
};
// valid impl
template <typename TBase, bool TImplement>
struct MessageImplProcessValidFields;
template <typename TBase>
struct MessageImplProcessValidFields<TBase, true> {
using Type = MessageImplFieldsValidBase<TBase>;
};
template <typename TBase>
struct MessageImplProcessValidFields<TBase, false> {
using Type = TBase;
};
// TBase is interface class
// TOptions... are the implementation options
template <typename TBase, typename... TOptions>
struct MessageImplBuilder {
// ParsedOptions class is supposed to be defined in comms::Message class
using InterfaceOptions = typename TBase::ParsedOptions;
// Parse implementation options
using ImplOptions = MessageImplParsedOptions<TOptions...>;
// Provide GetIdImpl() if possible
static const bool HasStaticNumIdImpl = InterfaceOptions::HasMsgIdType && ImplOptions::HasStaticNumIdImpl;
using Base1 = typename MessageImplProcessStaticNumId<TBase, ImplOptions, HasStaticNumIdImpl>::Type;
// Provide DispatchImpl() if possible
static const bool HasDispatchImpl = InterfaceOptions::HasHandler && ImplOptions::HasDispatchImpl;
using Base2 = typename MessageImplProcessDispatch<Base1, ImplOptions, HasDispatchImpl>::Type;
// Provide access to fields if possible
using Base3 = typename MessageImplProcessFields<Base2, ImplOptions, ImplOptions::HasFieldsImpl>::Type;
// Provide ReadImpl() if possible
static const bool HasReadImpl = InterfaceOptions::HasReadOperations && ImplOptions::HasFieldsImpl;
using Base4 = typename MessageImplProcessReadFields<Base3, HasReadImpl>::Type;
// Provide WriteImpl() if possible
static const bool HasWriteImpl = InterfaceOptions::HasWriteOperations && ImplOptions::HasFieldsImpl;
using Base5 = typename MessageImplProcessWriteFields<Base4, HasWriteImpl>::Type;
// Provide ValidImpl() if possible
static const bool HasValidImpl = InterfaceOptions::HasWriteOperations && ImplOptions::HasFieldsImpl;
using Base6 = typename MessageImplProcessValidFields<Base5, HasValidImpl>::Type;
// The last BaseN must be taken as final type.
using Type = Base6;
};
template <typename TBase, typename... TOptions>
class MessageBase : public MessageImplBuilder<TBase, TOptions...>::Type {};
} // namespace sp } // namespace sp

View File

@@ -0,0 +1,38 @@
#pragma once
#include <sp/MessageInterfaces.h>
namespace sp {
namespace option {
class EmptyBase {};
template <typename... TOptions>
struct MessageInterfaceBuilder {
// Parse the options
using ParsedOptions = MessageInterfaceParsedOptions<TOptions...>;
// Add ID retrieval functionality if ID type was provided
using Base1 = typename MessageInterfaceProcessMsgId<EmptyBase, ParsedOptions, ParsedOptions::HasMsgIdType>::Type;
// Add ReadData() and WriteData(), that use the right endian
using Base2 = typename MessageInterfaceProcessEndian<Base1, ParsedOptions::HasLittleEndian>::Type;
// Add read functionality if Read type was provided
using Base3 = typename MessageInterfaceProcessRead<Base2, ParsedOptions::HasReadOperations>::Type;
// Add write functionality if Write type was provided
using Base4 = typename MessageInterfaceProcessWrite<Base3, ParsedOptions::HasWriteOperations>::Type;
// add dispatch functionality if Handler type was provided
using Base5 = typename MessageInterfaceProcessHandler<Base4, ParsedOptions, ParsedOptions::HasHandler>::Type;
// add valid functionality if Valid tpe was provided
using Base6 = typename MessageInterfaceProcessValid<Base5, ParsedOptions::HasValid>::Type;
// The last Base6 must be taken as final type.
using Type = Base6;
};
} // namespace option
} // namespace sp

View File

@@ -0,0 +1,243 @@
#pragma once
#include <sp/Options.h>
namespace sp {
namespace option {
template <typename... TOptions>
struct MessageInterfaceParsedOptions {};
template <>
struct MessageInterfaceParsedOptions<> {
static const bool HasMsgIdType = false;
static const bool HasLittleEndian = false;
static const bool HasReadOperations = false;
static const bool HasWriteOperations = false;
static const bool HasHandler = false;
static const bool HasValid = false;
};
template <typename T, typename... TOptions>
struct MessageInterfaceParsedOptions<MsgIdType<T>, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasMsgIdType = true;
using MsgIdType = T;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<LittleEndian, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasLittleEndian = true;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<ReadOperations, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasReadOperations = true;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<WriteOperations, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasWriteOperations = true;
};
template <typename T, typename... TOptions>
struct MessageInterfaceParsedOptions<Handler<T>, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasHandler = true;
using HandlerType = Handler<T>;
};
template <typename... TOptions>
struct MessageInterfaceParsedOptions<ValidCheckInterface, TOptions...> : public MessageInterfaceParsedOptions<TOptions...> {
static const bool HasValid = true;
};
// ID retrieval chunk
template <typename TBase, typename TId>
class MessageInterfaceIdTypeBase : public TBase {
public:
using MsgIdType = TId;
MsgIdType GetId() const {
return GetIdImpl();
}
protected:
virtual MsgIdType GetIdImpl() const = 0;
};
// Big endian serialisation chunk
template <typename TBase>
class MessageInterfaceBigEndian : public TBase {
protected:
template <typename T>
static T ReadData(DataBuffer& buffer) {
// use big endian
}
template <typename T>
static void WriteData(T value, DataBuffer& buffer) {
// use big endian
}
};
// Little endian serialisation chunk
template <typename TBase>
class MessageInterfaceLittleEndian : public TBase {
protected:
template <typename T>
static T ReadData(DataBuffer& buffer) {
// use little endian
}
template <typename T>
static void WriteData(const T& value, DataBuffer& buffer) {
// use little endian
}
};
// Read functionality chunk
template <typename TBase>
class MessageInterfaceReadBase : public TBase {
public:
void Read(DataBuffer& buffer) {
return ReadImpl(buffer);
}
protected:
virtual void ReadImpl(DataBuffer& buffer) = 0;
};
// Write functionality chunk
template <typename TBase>
class MessageInterfaceWriteBase : public TBase {
public:
void Write(DataBuffer& buffer) {
return WriteImpl(buffer);
}
protected:
virtual void WriteImpl(DataBuffer& buffer) = 0;
};
// Handler functionality chunk
template <typename TBase, typename THandler>
class MessageInterfaceHandlerBase : public TBase {
public:
using HandlerType = typename THandler::HandlerT;
void Dispatch(HandlerType& handler) {
DispatchImpl(handler);
}
protected:
virtual void DispatchImpl(HandlerType& handler) = 0;
};
// Validity functionality chunk
template <typename TBase>
class MessageInterfaceValidityBase : public TBase {
public:
bool Valid() const {
return ValidImpl();
}
protected:
virtual bool ValidImpl() const = 0;
};
// Build message Id
template <typename TBase, typename TParsedOptions, bool THasMsgIdType>
struct MessageInterfaceProcessMsgId;
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessMsgId<TBase, TParsedOptions, true> {
using Type = MessageInterfaceIdTypeBase<TBase, typename TParsedOptions::MsgIdType>;
};
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessMsgId<TBase, TParsedOptions, false> {
using Type = TBase;
};
// Build endianess
template <typename TBase, bool THasLittleEndian>
struct MessageInterfaceProcessEndian;
template <typename TBase>
struct MessageInterfaceProcessEndian<TBase, true> {
using Type = MessageInterfaceLittleEndian<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessEndian<TBase, false> {
using Type = MessageInterfaceBigEndian<TBase>;
};
// Build read
template <typename TBase, bool THasRead>
struct MessageInterfaceProcessRead;
template <typename TBase>
struct MessageInterfaceProcessRead<TBase, true> {
using Type = MessageInterfaceReadBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessRead<TBase, false> {
using Type = TBase;
};
// Build write
template <typename TBase, bool THasWrite>
struct MessageInterfaceProcessWrite;
template <typename TBase>
struct MessageInterfaceProcessWrite<TBase, true> {
using Type = MessageInterfaceWriteBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessWrite<TBase, false> {
using Type = TBase;
};
// Build handler
template <typename TBase, typename TParsedOptions, bool THasHandler>
struct MessageInterfaceProcessHandler;
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessHandler<TBase, TParsedOptions, true> {
using Type = MessageInterfaceHandlerBase<TBase, typename TParsedOptions::HandlerType>;
};
template <typename TBase, typename TParsedOptions>
struct MessageInterfaceProcessHandler<TBase, TParsedOptions, false> {
using Type = TBase;
};
// Build valid
template <typename TBase, bool THasValid>
struct MessageInterfaceProcessValid;
template <typename TBase>
struct MessageInterfaceProcessValid<TBase, true> {
using Type = MessageInterfaceValidityBase<TBase>;
};
template <typename TBase>
struct MessageInterfaceProcessValid<TBase, false> {
using Type = TBase;
};
} // namespace option
} // namespace sp

31
include/sp/Options.h Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
#include <sp/common/DataBuffer.h>
namespace sp {
namespace option {
// Define type used to store message ID
template <typename T>
struct MsgIdType {};
// Enable reading
struct ReadOperations {};
// Enable writing
struct WriteOperations {};
// Use little endian for serialisation (instead of default big)
struct LittleEndian {};
// Include validity check in public interface
struct ValidCheckInterface {};
// Define handler class
template <typename T>
struct Handler {
using HandlerT = T;
};
} // namespace option
} // namespace sp

View File

@@ -44,4 +44,43 @@ void loop(LoopBody&& loop_body) {
loop_impl(std::make_index_sequence<N>{}, std::forward<LoopBody>(loop_body)); loop_impl(std::make_index_sequence<N>{}, std::forward<LoopBody>(loop_body));
} }
namespace details {
template <std::size_t TRem>
class TupleForEachHelper {
public:
template <typename TTuple, typename TFunc>
static void exec(TTuple&& tuple, TFunc&& func) {
using Tuple = typename std::decay<TTuple>::type;
static const std::size_t TupleSize = std::tuple_size<Tuple>::value;
static_assert(TRem <= TupleSize, "Incorrect parameters");
// Invoke function with current element
static const std::size_t Idx = TupleSize - TRem;
func(std::get<Idx>(std::forward<TTuple>(tuple)));
// Compile time recursion - invoke function with the remaining elements
TupleForEachHelper<TRem - 1>::exec(std::forward<TTuple>(tuple), std::forward<TFunc>(func));
}
};
template <>
class TupleForEachHelper<0> {
public:
// Stop compile time recursion
template <typename TTuple, typename TFunc>
static void exec(TTuple&& tuple, TFunc&& func) {
static_cast<void>(tuple);
static_cast<void>(func);
}
};
} // namespace details
template <typename TTuple, typename TFunc>
void tupleForEach(TTuple&& tuple, TFunc&& func) {
using Tuple = typename std::decay<TTuple>::type;
static const std::size_t TupleSize = std::tuple_size<Tuple>::value;
details::TupleForEachHelper<TupleSize>::exec(std::forward<TTuple>(tuple), std::forward<TFunc>(func));
}
} // namespace sp } // namespace sp

View File

@@ -1,125 +1,58 @@
#include <iostream> #include <iostream>
#include <sp/Field.h>
#include <sp/GenericHandler.h> #include <sp/GenericHandler.h>
#include <sp/MessageBase.h> #include <sp/MessageBase.h>
#include <sp/Templates.h>
#define PacketClass(className) class className : public sp::MessageBase<className> #include <memory>
template <typename T> enum MyMsgId {
class Packet : public sp::MessageBase<T> { MyMsgId_Msg1,
public: MyMsgId_Msg2,
virtual std::string_view GetName() const = 0;
void WriteImpl(sp::DataBuffer& buffer) const = 0;
void ReadImpl(const sp::DataBuffer& buffer) = 0;
int GetId() const = 0;
}; };
template <typename T> class MyHandler; // forward declaration of the handler class.
class Command : public sp::MessageBase<T> {
using MyMessage = sp::Message<sp::option::MsgIdType<MyMsgId>, // add id() operation
sp::option::ReadOperations, // add read() operation
sp::option::WriteOperations, // add write() operation
sp::option::Handler<MyHandler>, // add dispatch() operation
sp::option::ValidCheckInterface, // add valid() operation
sp::option::LittleEndian // use little endian for serialisation
>;
using ActualMessage1Fields = std::tuple<sp::Field<std::uint16_t>, sp::Field<std::int8_t>>;
template <typename TMessageInterface>
class ActualMessage1 : public sp::MessageBase<TMessageInterface,
sp::option::StaticNumIdImpl<MyMsgId_Msg1>, // provide idImpl() if needed
sp::option::DispatchImpl<ActualMessage1<TMessageInterface>>, // provide dispatchImpl() if needed
sp::option::FieldsImpl<ActualMessage1Fields> // provide access to fields and
// readImpl(), writeImpl(),
// lengthImpl(), validImpl()
// functions if needed
> {};
using MyActualMessage1 = ActualMessage1<MyMessage>;
using AllMessages = std::tuple<MyActualMessage1>;
class MyHandler : public sp::GenericHandler<MyMessage, AllMessages> {
public: public:
virtual std::string_view GetName() const = 0; void Handle(MyActualMessage1& msg) {
void WriteImpl(sp::DataBuffer& buffer) const = 0; std::cout << "Got message 1 !\n";
void ReadImpl(const sp::DataBuffer& buffer) = 0;
int GetId() const = 0;
};
#define TestPacket(packetName) \
class packetName : public Packet<packetName> { \
public: \
std::string_view GetName() const { \
return #packetName; \
} \
void WriteImpl(sp::DataBuffer& buffer) const {} \
void ReadImpl(const sp::DataBuffer& buffer) {} \
int GetId() const { \
return 0; \
} \
}
#define TestCommand(commandName) \
class commandName : public Command<commandName> { \
public: \
std::string_view GetName() const { \
return #commandName; \
} \
void WriteImpl(sp::DataBuffer& buffer) const {} \
void ReadImpl(const sp::DataBuffer& buffer) {} \
int GetId() const { \
return 0; \
} \
}
TestPacket(ChatPacket);
TestPacket(PlayerJoinPacket);
TestPacket(PlayerLeavePacket);
TestCommand(SpawnTroopCommand);
TestCommand(PlaceTowerCommand);
class ActualMessage2 : public sp::MessageBase<ActualMessage2> {
std::string_view GetName() const {
return "mesmes";
}
void WriteImpl(sp::DataBuffer& buffer) const {}
void ReadImpl(const sp::DataBuffer& buffer) {}
int GetId() const {
return 0;
}
};
using AllPackets = std::tuple<ChatPacket, PlayerJoinPacket, PlayerLeavePacket>;
using AllCommands = std::tuple<SpawnTroopCommand, PlaceTowerCommand>;
using AllTests = std::tuple<ActualMessage2>;
using AllMessages = sp::tuple_cat_t<AllPackets, AllCommands, AllTests>;
namespace sp {
class Handler : public sp::GenericHandler<sp::Message, AllMessages> {};
} // namespace sp
using PacketHandler = sp::Handler;
using CommandHandler = sp::Handler;
class PacketPrinter : public PacketHandler {
public:
void Handle(ChatPacket& packet) override {
std::cout << packet.GetName() << std::endl;
}
// void Handle(SpawnTroopCommand& cmd) {
// std::cout << "NOOOOOO\n";
// }
};
class ActualHandler1 : public sp::Handler {
public:
virtual void Handle(ActualMessage2& msg) override {
std::cout << "Handling ActualMessage2" << std::endl;
}
virtual void Handle(sp::Message& msg) override {
std::cout << "Common handling function is invoked" << std::endl;
}
virtual void Handle(ChatPacket& msg) override {
std::cout << "Chat invoked" << std::endl;
} }
}; };
int main() { int main() {
std::unique_ptr<sp::Message> msg = std::make_unique<ActualMessage2>(); MyMessage::MsgIdType test;
std::unique_ptr<sp::Message> chat = std::make_unique<ChatPacket>(); auto yes = std::make_unique<ActualMessage1<MyMessage>>();
SpawnTroopCommand cmd; MyHandler handlerTest;
PacketPrinter packetHandler; yes->Dispatch(handlerTest);
PacketHandler pp;
chat->Dispatch(packetHandler);
ActualHandler1 handler;
msg->Dispatch(handler);
chat->Dispatch(handler);
return 0; return 0;
} }