From 6bbc84907617ca348c7d819886103d884d76411b Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Wed, 5 Feb 2025 20:42:46 +0100 Subject: [PATCH] dirty messages --- include/sp/Field.h | 61 ++++++ include/sp/Message.h | 33 +--- include/sp/MessageBase.h | 270 ++++++++++++++++++++++++++- include/sp/MessageInterfaceBuilder.h | 38 ++++ include/sp/MessageInterfaces.h | 243 ++++++++++++++++++++++++ include/sp/Options.h | 31 +++ include/sp/Templates.h | 39 ++++ src/main.cpp | 151 +++++---------- 8 files changed, 720 insertions(+), 146 deletions(-) create mode 100644 include/sp/Field.h create mode 100644 include/sp/MessageInterfaceBuilder.h create mode 100644 include/sp/MessageInterfaces.h create mode 100644 include/sp/Options.h diff --git a/include/sp/Field.h b/include/sp/Field.h new file mode 100644 index 0000000..23861ec --- /dev/null +++ b/include/sp/Field.h @@ -0,0 +1,61 @@ +#pragma once + +#include + +namespace sp { + +template +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 + 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 + void operator()(TField& field) { + field.Write(m_Buffer); + } + + private: + DataBuffer& m_Buffer; +}; + +} // namespace sp diff --git a/include/sp/Message.h b/include/sp/Message.h index e640efd..2dddd70 100644 --- a/include/sp/Message.h +++ b/include/sp/Message.h @@ -1,38 +1,13 @@ #pragma once -#include -#include - -#include +#include namespace sp { -class Handler; -class Message { +template +class Message : public option::MessageInterfaceBuilder::Type { public: - virtual ~Message() {} - - 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; + using ParsedOptions = typename option::MessageInterfaceBuilder::ParsedOptions; }; } // namespace sp \ No newline at end of file diff --git a/include/sp/MessageBase.h b/include/sp/MessageBase.h index 28b8437..40d5fab 100644 --- a/include/sp/MessageBase.h +++ b/include/sp/MessageBase.h @@ -3,16 +3,270 @@ #include namespace sp { +namespace option { -template -class MessageBase : public Message { - protected: - virtual void DispatchImpl(Handler& handler) override { - handler.Handle(static_cast(*this)); - } +// Provide static numeric ID, to facilitate implementation of GetIdImpl() +template +struct StaticNumIdImpl {}; - virtual void WriteImpl(DataBuffer& buffer) const = 0; - virtual void ReadImpl(const DataBuffer& buffer) = 0; +// Facilitate implementation of DispatchImpl() +template +struct DispatchImpl {}; + +// Provide fields of the message, facilitate implementation of +// ReadImpl(), WriteImpl(), ValidImpl(), etc... +template +struct FieldsImpl {}; + +} // namespace option + + + + + +template +class MessageImplParsedOptions; + +template <> +struct MessageImplParsedOptions<> { + static const bool HasStaticNumIdImpl = false; + static const bool HasDispatchImpl = false; + static const bool HasFieldsImpl = false; }; + + + + +template +struct MessageImplParsedOptions, TOptions...> : public MessageImplParsedOptions { + static const bool HasStaticNumIdImpl = true; + static const std::intmax_t MsgId = TId; +}; + +template +struct MessageImplParsedOptions, TOptions...> : public MessageImplParsedOptions { + static const bool HasDispatchImpl = true; + using ActualMessage = TActual; +}; + +template +struct MessageImplParsedOptions, TOptions...> : public MessageImplParsedOptions { + static const bool HasFieldsImpl = true; + using Fields = TFields; +}; + + + + + +// ID information chunk +template +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(TId); + } +}; + +// Dispatch implementation chunk +template +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(*this)); + } +}; + + + +template +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 +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 +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 +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 +struct MessageImplProcessStaticNumId; + +template +struct MessageImplProcessStaticNumId { + using Type = MessageImplStaticNumIdBase; +}; + +template +struct MessageImplProcessStaticNumId { + using Type = TBase; +}; + +// dispatch impl +template +struct MessageImplProcessDispatch; + +template +struct MessageImplProcessDispatch { + using Type = MessageImplDispatchBase; +}; + +template +struct MessageImplProcessDispatch { + using Type = TBase; +}; + +// fields impl +template +struct MessageImplProcessFields; + +template +struct MessageImplProcessFields { + using Type = MessageImplFieldsBase; +}; + +template +struct MessageImplProcessFields { + using Type = TBase; +}; + +// read impl +template +struct MessageImplProcessReadFields; + +template +struct MessageImplProcessReadFields { + using Type = MessageImplFieldsReadBase; +}; + +template +struct MessageImplProcessReadFields { + using Type = TBase; +}; + +// write impl +template +struct MessageImplProcessWriteFields; + +template +struct MessageImplProcessWriteFields { + using Type = MessageImplFieldsWriteBase; +}; + +template +struct MessageImplProcessWriteFields { + using Type = TBase; +}; + +// valid impl +template +struct MessageImplProcessValidFields; + +template +struct MessageImplProcessValidFields { + using Type = MessageImplFieldsValidBase; +}; + +template +struct MessageImplProcessValidFields { + using Type = TBase; +}; + + + + + +// TBase is interface class +// TOptions... are the implementation options +template +struct MessageImplBuilder { + // ParsedOptions class is supposed to be defined in comms::Message class + using InterfaceOptions = typename TBase::ParsedOptions; + + // Parse implementation options + using ImplOptions = MessageImplParsedOptions; + + // Provide GetIdImpl() if possible + static const bool HasStaticNumIdImpl = InterfaceOptions::HasMsgIdType && ImplOptions::HasStaticNumIdImpl; + using Base1 = typename MessageImplProcessStaticNumId::Type; + + // Provide DispatchImpl() if possible + static const bool HasDispatchImpl = InterfaceOptions::HasHandler && ImplOptions::HasDispatchImpl; + using Base2 = typename MessageImplProcessDispatch::Type; + + // Provide access to fields if possible + using Base3 = typename MessageImplProcessFields::Type; + + // Provide ReadImpl() if possible + static const bool HasReadImpl = InterfaceOptions::HasReadOperations && ImplOptions::HasFieldsImpl; + using Base4 = typename MessageImplProcessReadFields::Type; + + // Provide WriteImpl() if possible + static const bool HasWriteImpl = InterfaceOptions::HasWriteOperations && ImplOptions::HasFieldsImpl; + using Base5 = typename MessageImplProcessWriteFields::Type; + + // Provide ValidImpl() if possible + static const bool HasValidImpl = InterfaceOptions::HasWriteOperations && ImplOptions::HasFieldsImpl; + using Base6 = typename MessageImplProcessValidFields::Type; + + // The last BaseN must be taken as final type. + using Type = Base6; +}; + + + +template +class MessageBase : public MessageImplBuilder::Type {}; + + + } // namespace sp \ No newline at end of file diff --git a/include/sp/MessageInterfaceBuilder.h b/include/sp/MessageInterfaceBuilder.h new file mode 100644 index 0000000..2dd5dfa --- /dev/null +++ b/include/sp/MessageInterfaceBuilder.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +namespace sp { +namespace option { + +class EmptyBase {}; + +template +struct MessageInterfaceBuilder { + // Parse the options + using ParsedOptions = MessageInterfaceParsedOptions; + + // Add ID retrieval functionality if ID type was provided + using Base1 = typename MessageInterfaceProcessMsgId::Type; + + // Add ReadData() and WriteData(), that use the right endian + using Base2 = typename MessageInterfaceProcessEndian::Type; + + // Add read functionality if Read type was provided + using Base3 = typename MessageInterfaceProcessRead::Type; + + // Add write functionality if Write type was provided + using Base4 = typename MessageInterfaceProcessWrite::Type; + + // add dispatch functionality if Handler type was provided + using Base5 = typename MessageInterfaceProcessHandler::Type; + + // add valid functionality if Valid tpe was provided + using Base6 = typename MessageInterfaceProcessValid::Type; + + // The last Base6 must be taken as final type. + using Type = Base6; +}; + +} // namespace option +} // namespace sp diff --git a/include/sp/MessageInterfaces.h b/include/sp/MessageInterfaces.h new file mode 100644 index 0000000..6ab39b1 --- /dev/null +++ b/include/sp/MessageInterfaces.h @@ -0,0 +1,243 @@ +#pragma once + +#include + +namespace sp { +namespace option { + +template +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 +struct MessageInterfaceParsedOptions, TOptions...> : public MessageInterfaceParsedOptions { + static const bool HasMsgIdType = true; + using MsgIdType = T; +}; + +template +struct MessageInterfaceParsedOptions : public MessageInterfaceParsedOptions { + static const bool HasLittleEndian = true; +}; + +template +struct MessageInterfaceParsedOptions : public MessageInterfaceParsedOptions { + static const bool HasReadOperations = true; +}; + +template +struct MessageInterfaceParsedOptions : public MessageInterfaceParsedOptions { + static const bool HasWriteOperations = true; +}; + +template +struct MessageInterfaceParsedOptions, TOptions...> : public MessageInterfaceParsedOptions { + static const bool HasHandler = true; + using HandlerType = Handler; +}; + +template +struct MessageInterfaceParsedOptions : public MessageInterfaceParsedOptions { + static const bool HasValid = true; +}; + + + + + +// ID retrieval chunk +template +class MessageInterfaceIdTypeBase : public TBase { + public: + using MsgIdType = TId; + MsgIdType GetId() const { + return GetIdImpl(); + } + + protected: + virtual MsgIdType GetIdImpl() const = 0; +}; + +// Big endian serialisation chunk +template +class MessageInterfaceBigEndian : public TBase { + protected: + template + static T ReadData(DataBuffer& buffer) { + // use big endian + } + + template + static void WriteData(T value, DataBuffer& buffer) { + // use big endian + } +}; + +// Little endian serialisation chunk +template +class MessageInterfaceLittleEndian : public TBase { + protected: + template + static T ReadData(DataBuffer& buffer) { + // use little endian + } + + template + static void WriteData(const T& value, DataBuffer& buffer) { + // use little endian + } +}; + +// Read functionality chunk +template +class MessageInterfaceReadBase : public TBase { + public: + void Read(DataBuffer& buffer) { + return ReadImpl(buffer); + } + + protected: + virtual void ReadImpl(DataBuffer& buffer) = 0; +}; + +// Write functionality chunk +template +class MessageInterfaceWriteBase : public TBase { + public: + void Write(DataBuffer& buffer) { + return WriteImpl(buffer); + } + + protected: + virtual void WriteImpl(DataBuffer& buffer) = 0; +}; + +// Handler functionality chunk +template +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 +class MessageInterfaceValidityBase : public TBase { + public: + bool Valid() const { + return ValidImpl(); + } + + protected: + virtual bool ValidImpl() const = 0; +}; + + + + + + +// Build message Id +template +struct MessageInterfaceProcessMsgId; + +template +struct MessageInterfaceProcessMsgId { + using Type = MessageInterfaceIdTypeBase; +}; + +template +struct MessageInterfaceProcessMsgId { + using Type = TBase; +}; + +// Build endianess +template +struct MessageInterfaceProcessEndian; + +template +struct MessageInterfaceProcessEndian { + using Type = MessageInterfaceLittleEndian; +}; + +template +struct MessageInterfaceProcessEndian { + using Type = MessageInterfaceBigEndian; +}; + +// Build read +template +struct MessageInterfaceProcessRead; + +template +struct MessageInterfaceProcessRead { + using Type = MessageInterfaceReadBase; +}; + +template +struct MessageInterfaceProcessRead { + using Type = TBase; +}; + +// Build write +template +struct MessageInterfaceProcessWrite; + +template +struct MessageInterfaceProcessWrite { + using Type = MessageInterfaceWriteBase; +}; + +template +struct MessageInterfaceProcessWrite { + using Type = TBase; +}; + +// Build handler +template +struct MessageInterfaceProcessHandler; + +template +struct MessageInterfaceProcessHandler { + using Type = MessageInterfaceHandlerBase; +}; + +template +struct MessageInterfaceProcessHandler { + using Type = TBase; +}; + +// Build valid +template +struct MessageInterfaceProcessValid; + +template +struct MessageInterfaceProcessValid { + using Type = MessageInterfaceValidityBase; +}; + +template +struct MessageInterfaceProcessValid { + using Type = TBase; +}; +} // namespace option +} // namespace sp diff --git a/include/sp/Options.h b/include/sp/Options.h new file mode 100644 index 0000000..bc1d1ae --- /dev/null +++ b/include/sp/Options.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace sp { +namespace option { + +// Define type used to store message ID +template +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 +struct Handler { + using HandlerT = T; +}; + +} // namespace option +} // namespace sp \ No newline at end of file diff --git a/include/sp/Templates.h b/include/sp/Templates.h index 845ee02..efbc5ad 100644 --- a/include/sp/Templates.h +++ b/include/sp/Templates.h @@ -44,4 +44,43 @@ void loop(LoopBody&& loop_body) { loop_impl(std::make_index_sequence{}, std::forward(loop_body)); } +namespace details { +template +class TupleForEachHelper { + public: + template + static void exec(TTuple&& tuple, TFunc&& func) { + using Tuple = typename std::decay::type; + static const std::size_t TupleSize = std::tuple_size::value; + static_assert(TRem <= TupleSize, "Incorrect parameters"); + + // Invoke function with current element + static const std::size_t Idx = TupleSize - TRem; + func(std::get(std::forward(tuple))); + + // Compile time recursion - invoke function with the remaining elements + TupleForEachHelper::exec(std::forward(tuple), std::forward(func)); + } +}; + +template <> +class TupleForEachHelper<0> { + public: + // Stop compile time recursion + template + static void exec(TTuple&& tuple, TFunc&& func) { + static_cast(tuple); + static_cast(func); + } +}; +} // namespace details + +template +void tupleForEach(TTuple&& tuple, TFunc&& func) { + using Tuple = typename std::decay::type; + static const std::size_t TupleSize = std::tuple_size::value; + + details::TupleForEachHelper::exec(std::forward(tuple), std::forward(func)); +} + } // namespace sp diff --git a/src/main.cpp b/src/main.cpp index f154b8f..cf67f56 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,125 +1,58 @@ #include +#include #include #include -#include -#define PacketClass(className) class className : public sp::MessageBase +#include -template -class Packet : public sp::MessageBase { - public: - 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; +enum MyMsgId { + MyMsgId_Msg1, + MyMsgId_Msg2, }; -template -class Command : public sp::MessageBase { +class MyHandler; // forward declaration of the handler class. + +using MyMessage = sp::Message, // add id() operation + sp::option::ReadOperations, // add read() operation + sp::option::WriteOperations, // add write() operation + sp::option::Handler, // add dispatch() operation + sp::option::ValidCheckInterface, // add valid() operation + sp::option::LittleEndian // use little endian for serialisation + >; + + + +using ActualMessage1Fields = std::tuple, sp::Field>; + + + +template +class ActualMessage1 : public sp::MessageBase, // provide idImpl() if needed + sp::option::DispatchImpl>, // provide dispatchImpl() if needed + sp::option::FieldsImpl // provide access to fields and + // readImpl(), writeImpl(), + // lengthImpl(), validImpl() + // functions if needed + > {}; + +using MyActualMessage1 = ActualMessage1; + +using AllMessages = std::tuple; + +class MyHandler : public sp::GenericHandler { public: - 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; -}; - - -#define TestPacket(packetName) \ - class packetName : public Packet { \ - 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 { \ - 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 { - 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; -using AllCommands = std::tuple; -using AllTests = std::tuple; - -using AllMessages = sp::tuple_cat_t; - -namespace sp { -class Handler : public sp::GenericHandler {}; -} // 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; + void Handle(MyActualMessage1& msg) { + std::cout << "Got message 1 !\n"; } }; int main() { - std::unique_ptr msg = std::make_unique(); - std::unique_ptr chat = std::make_unique(); - SpawnTroopCommand cmd; - PacketPrinter packetHandler; - PacketHandler pp; - chat->Dispatch(packetHandler); - ActualHandler1 handler; - msg->Dispatch(handler); - chat->Dispatch(handler); + MyMessage::MsgIdType test; + auto yes = std::make_unique>(); + MyHandler handlerTest; + yes->Dispatch(handlerTest); return 0; } \ No newline at end of file