From db0c5f324570bfa071e182537b9a96664770446a Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Sun, 23 Feb 2025 09:40:46 +0000 Subject: [PATCH] fix alignment + refactor Reviewed-on: https://git.ale-pri.com/Persson-dev/Simple-Protocol-Lib/pulls/1 Co-authored-by: Persson-dev Co-committed-by: Persson-dev --- include/examples/DisconnectPacket.h | 13 +- include/examples/KeepAlivePacket.h | 17 +- include/examples/PacketExample.h | 6 +- include/examples/UpgradeTowerPacket.h | 31 ++ include/sp/common/DataBuffer.h | 4 +- include/sp/common/MacroMap.h | 54 --- include/sp/common/NonCopyable.h | 25 -- include/sp/default/DefaultPacket.h | 31 +- include/sp/misc/Format.h | 38 -- include/sp/misc/Log.h | 32 -- include/sp/misc/Test.h | 70 ---- include/sp/protocol/Field.h | 13 +- include/sp/protocol/GenericHandler.h | 100 +++--- include/sp/protocol/Message.h | 2 +- include/sp/protocol/MessageBase.h | 336 +----------------- include/sp/protocol/MessageInterfaces.h | 278 --------------- .../sp/protocol/message/MessageImplBuilder.h | 45 +++ .../sp/protocol/message/MessageImplOptions.h | 61 ++++ .../sp/protocol/message/MessageImplProcess.h | 93 +++++ .../{ => message}/MessageInterfaceBuilder.h | 4 +- .../message/MessageInterfaceProcess.h | 105 ++++++ .../sp/protocol/message/MessageInterfaces.h | 7 + .../protocol/message/MessageInterfacesImpl.h | 119 +++++++ .../message/MessageInterfacesOptions.h | 63 ++++ .../{Options.h => message/MessageOptions.h} | 0 include/sp/protocol/message/MessagesImpl.h | 160 +++++++++ src/sp/common/DataBuffer.cpp | 37 +- src/sp/misc/Log.cpp | 37 -- src/sp/misc/Test.cpp | 14 - src/main.cpp => test/test_packets.cpp | 21 +- xmake.lua | 85 +---- 31 files changed, 831 insertions(+), 1070 deletions(-) create mode 100644 include/examples/UpgradeTowerPacket.h delete mode 100644 include/sp/common/MacroMap.h delete mode 100644 include/sp/common/NonCopyable.h delete mode 100644 include/sp/misc/Format.h delete mode 100644 include/sp/misc/Log.h delete mode 100644 include/sp/misc/Test.h delete mode 100644 include/sp/protocol/MessageInterfaces.h create mode 100644 include/sp/protocol/message/MessageImplBuilder.h create mode 100644 include/sp/protocol/message/MessageImplOptions.h create mode 100644 include/sp/protocol/message/MessageImplProcess.h rename include/sp/protocol/{ => message}/MessageInterfaceBuilder.h (93%) create mode 100644 include/sp/protocol/message/MessageInterfaceProcess.h create mode 100644 include/sp/protocol/message/MessageInterfaces.h create mode 100644 include/sp/protocol/message/MessageInterfacesImpl.h create mode 100644 include/sp/protocol/message/MessageInterfacesOptions.h rename include/sp/protocol/{Options.h => message/MessageOptions.h} (100%) create mode 100644 include/sp/protocol/message/MessagesImpl.h delete mode 100644 src/sp/misc/Log.cpp delete mode 100644 src/sp/misc/Test.cpp rename src/main.cpp => test/test_packets.cpp (53%) diff --git a/include/examples/DisconnectPacket.h b/include/examples/DisconnectPacket.h index 6a69371..013f7f0 100644 --- a/include/examples/DisconnectPacket.h +++ b/include/examples/DisconnectPacket.h @@ -4,10 +4,17 @@ #include #include -enum DisconnectPacketFields { - Reason = 0 +enum class DisconnectFieldsE { + Reason = 0 }; using DisconnectFields = std::tuple; -DeclarePacket(Disconnect); \ No newline at end of file +DeclarePacket(Disconnect){ + public: + PacketConstructor(Disconnect) + + const std::string& GetReason() const { + return GetField(); + } +}; diff --git a/include/examples/KeepAlivePacket.h b/include/examples/KeepAlivePacket.h index dfa45ae..770b078 100644 --- a/include/examples/KeepAlivePacket.h +++ b/include/examples/KeepAlivePacket.h @@ -4,10 +4,19 @@ #include #include -enum KeepAlivePacketFields { - KeepAliveId = 0 +enum class KeepAliveFieldsE { + KeepAliveId = 0, }; -using KeepAliveFields = std::tuple; +using KeepAliveFields = std::tuple< + std::uint64_t //<- KeepAliveId + >; -DeclarePacket(KeepAlive); \ No newline at end of file +DeclarePacket(KeepAlive){ + public: + PacketConstructor(KeepAlive) + + std::uint64_t GetKeepAliveId() const { + return GetField(); + } +}; \ No newline at end of file diff --git a/include/examples/PacketExample.h b/include/examples/PacketExample.h index 02e18fc..c185c8f 100644 --- a/include/examples/PacketExample.h +++ b/include/examples/PacketExample.h @@ -3,13 +3,15 @@ enum PacketId { KeepAlive = 0, Disconnect, + UpgradeTower, }; #include #include +#include -// they must be in the same order ! -using AllPackets = std::tuple; +// they must be in the same order as in the enum ! +using AllPackets = std::tuple; #include #include \ No newline at end of file diff --git a/include/examples/UpgradeTowerPacket.h b/include/examples/UpgradeTowerPacket.h new file mode 100644 index 0000000..de69cdf --- /dev/null +++ b/include/examples/UpgradeTowerPacket.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + + +enum class UpgradeTowerFieldsE { + m_Tower = 0, + m_Upgrade, +}; + +using UpgradeTowerFields = std::tuple< + sp::BitField, //<- m_Tower + sp::Field //<- m_Upgrade + > + >; + +DeclarePacket(UpgradeTower){ + public: + PacketConstructor(UpgradeTower) + + std::uint16_t GetTowerId() const { + return GetField<0>().GetField(); + } + + std::uint8_t GetTowerUpgrade() const { + return GetField<0>().GetField(); + } +}; \ No newline at end of file diff --git a/include/sp/common/DataBuffer.h b/include/sp/common/DataBuffer.h index 7c1f063..32ac95b 100644 --- a/include/sp/common/DataBuffer.h +++ b/include/sp/common/DataBuffer.h @@ -261,13 +261,13 @@ class DataBuffer { * \brief Read a file into the buffer * \param fileName The name of the file to read */ - bool ReadFile(const std::string& fileName); + void ReadFile(const std::string& fileName); /** * \brief Write a file into the buffer * \param fileName The name of the file to write to */ - bool WriteFile(const std::string& fileName) const; + void WriteFile(const std::string& fileName) const; /** * \brief Allocate the buffer on the heap diff --git a/include/sp/common/MacroMap.h b/include/sp/common/MacroMap.h deleted file mode 100644 index e5afacf..0000000 --- a/include/sp/common/MacroMap.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Created by William Swanson in 2012. - * - * I, William Swanson, dedicate this work to the public domain. - * I waive all rights to the work worldwide under copyright law, - * including all related and neighboring rights, - * to the extent allowed by law. - * - * You can copy, modify, distribute and perform the work, - * even for commercial purposes, all without asking permission. - */ - -#ifndef MAP_H_INCLUDED -#define MAP_H_INCLUDED - -#define EVAL0(...) __VA_ARGS__ -#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__))) -#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) -#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) -#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) -#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) - -#define MAP_END(...) -#define MAP_OUT -#define MAP_COMMA , - -#define MAP_GET_END2() 0, MAP_END -#define MAP_GET_END1(...) MAP_GET_END2 -#define MAP_GET_END(...) MAP_GET_END1 -#define MAP_NEXT0(test, next, ...) next MAP_OUT -#define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0) -#define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next) - -#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__) -#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__) - -#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0) -#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next) - -#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__) -#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__) - -/** - * Applies the function macro `f` to each of the remaining parameters. - */ -#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -/** - * Applies the function macro `f` to each of the remaining parameters and - * inserts commas between the results. - */ -#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#endif \ No newline at end of file diff --git a/include/sp/common/NonCopyable.h b/include/sp/common/NonCopyable.h deleted file mode 100644 index 85b155e..0000000 --- a/include/sp/common/NonCopyable.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/** - * \file NonCopyable.h - * \brief File containing the sp::NonCopyable class - */ - -namespace sp { - -/** - * \class NonCopyable - * \brief Class used to make a class non copyable - * \note Inherit from this class privately to make a class non copyable - */ -class NonCopyable { - public: - NonCopyable(const NonCopyable&) = delete; - NonCopyable& operator=(const NonCopyable&) = delete; - - protected: - NonCopyable() {} - ~NonCopyable() {} -}; - -} // namespace sp diff --git a/include/sp/default/DefaultPacket.h b/include/sp/default/DefaultPacket.h index 43a0770..24e8156 100644 --- a/include/sp/default/DefaultPacket.h +++ b/include/sp/default/DefaultPacket.h @@ -6,22 +6,25 @@ namespace sp { class PacketHandler; -using PacketMessage = Message, // add id() operation - option::ReadOperations, // add read() operation - option::WriteOperations, // add write() operation - option::WriteId, // write id before data - option::Handler // add dispatch() operation - >; +using PacketMessage = Message< + option::MsgIdType, // add id() operation + option::ReadOperations, // add read() operation + option::WriteOperations, // add write() operation + option::WriteId, // write id before data + option::Handler // add dispatch() operation +>; + +#define PacketConstructor(packetName) \ + packetName##Packet() {} \ + template \ + packetName##Packet(Args... args) { \ + Construct(args...); \ + } #define DeclarePacket(packetName) \ class packetName##Packet : public sp::MessageBase, \ - sp::option::DispatchImpl, sp::option::FieldsImpl> { \ - public: \ - packetName##Packet() {} \ - template \ - packetName##Packet(Args... args) { \ - Construct(args...); \ - } \ - } + sp::option::DispatchImpl, sp::option::FieldsImpl> + + } // namespace sp diff --git a/include/sp/misc/Format.h b/include/sp/misc/Format.h deleted file mode 100644 index 55ba136..0000000 --- a/include/sp/misc/Format.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -/** - * \file Format.h - * \brief This file contains the definition of the `Format` function. - */ - -#include -#include -#include - -namespace sp { -namespace utils { - -/** - * \brief Formats a string using a format string and variadic arguments. - * - * This function takes a format string and a variable number of arguments and returns a formatted string. - * The format string can contain placeholders that will be replaced by the corresponding arguments. - * - * \param format The format string. - * \param args The variadic arguments to be formatted. - * \return The formatted string. - * \throws std::runtime_error if an error occurs during formatting. - */ -template -std::string Format(const std::string& format, Args... args) { - int size = snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0' - if (size <= 0) { - throw std::runtime_error("Error during formatting."); - } - std::unique_ptr buf(new char[size]); - snprintf(buf.get(), static_cast(size), format.c_str(), args...); - return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside -} - -} // namespace utils -} // namespace sp diff --git a/include/sp/misc/Log.h b/include/sp/misc/Log.h deleted file mode 100644 index 4e83c7d..0000000 --- a/include/sp/misc/Log.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/** - * \file Log.h - * \brief File defining log functions - */ - -#include - -namespace sp { -namespace utils { - -/** - * \brief Logs a normal message. - * \param msg The message to be logged. - */ -void LOG(const std::string& msg); - -/** - * \brief Logs a normal message in debug mode. - * \param msg The message to be logged. - */ -void LOGD(const std::string& msg); - -/** - * \brief Logs an error message. - * \param err The error message to be logged. - */ -void LOGE(const std::string& err); - -} // namespace utils -} // namespace sp diff --git a/include/sp/misc/Test.h b/include/sp/misc/Test.h deleted file mode 100644 index da122c7..0000000 --- a/include/sp/misc/Test.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -/** - * \file Test.h - * \brief File containing unit testing utilities - */ - -#include -#include - -namespace sp { -namespace test { - -/** - * \def SP_TEST_SUCCESSFUL - * \brief Used in tests to indicate that a test was successful - */ -#define SP_TEST_SUCCESSFUL 0 - -/** - * \def SP_TEST_FAILED - * \brief Used in tests to indicate that a test failed - */ -#define SP_TEST_FAILED 1 - -#ifndef __FUNCTION_NAME__ -#ifdef _WIN32 -#define __FUNCTION_NAME__ __FUNCTION__ -#else -#define __FUNCTION_NAME__ __PRETTY_FUNCTION__ -#endif -#endif - -/** - * \def blitz_test_assert - * \param ... The expression to evaluate - * \brief Evaluates the expression and exits the program if not valid. - * \note It works like a basic assert() but also in release mode - */ -#define sp_test_assert(...) \ - if (!static_cast(__VA_ARGS__)) { \ - td::test::LogAssert(#__VA_ARGS__, __FILE__, __LINE__, __FUNCTION_NAME__); \ - std::exit(SP_TEST_FAILED); \ - } - - -/** - * \def blitz_debug_assert - * \param ... The expression to execute - * \brief Assertion without checks in release mode - * \note The expression is always executed. However, in release, no checks are made ! - */ -#ifdef NDEBUG -#define sp_debug_assert(...) __VA_ARGS__ -#else -#define sp_debug_assert sp_test_assert -#endif - - -/** - * \brief Prints an error message associated with a failed assertion - * \param expression The expression that was tested - * \param file The file in which the assertion failed - * \param line The line in the file in which the assertion failed - * \param function The function in which the assertion failed - */ -void LogAssert(const char* expression, const char* file, int line, const char* function); - -} // namespace test -} // namespace sp diff --git a/include/sp/protocol/Field.h b/include/sp/protocol/Field.h index 6bafa85..7645546 100644 --- a/include/sp/protocol/Field.h +++ b/include/sp/protocol/Field.h @@ -40,6 +40,17 @@ class BitField { return std::get(this->GetFields()).GetValue(); } + template + const auto& GetField() const { + return std::get(this->GetFields()).GetValue(); + } + + // allow use of enums + template + const auto& GetField() const { + return std::get(FIndex)>(this->GetFields()).GetValue(); + } + private: template = sizeof...(T), bool> = true> void Apply(const std::tuple& args) {} @@ -103,6 +114,6 @@ template struct FieldsBuilder { using Type = sp::tuple_cat_t>, typename FieldsBuilder::Type>; }; -} // namespace details +} // namespace details } // namespace sp diff --git a/include/sp/protocol/GenericHandler.h b/include/sp/protocol/GenericHandler.h index 9e50a37..dca0813 100644 --- a/include/sp/protocol/GenericHandler.h +++ b/include/sp/protocol/GenericHandler.h @@ -23,26 +23,26 @@ namespace sp using Base = GenericHandler >; public: using Base::Handle; // Don't hide all Handle() functions from base classes - virtual void Handle(T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T3& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T4& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T5& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T6& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T7& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T8& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T9& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T10& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T11& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T12& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T13& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T14& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T15& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T16& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T17& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T18& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T19& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T20& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T3& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T4& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T5& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T6& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T7& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T8& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T9& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T10& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T11& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T12& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T13& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T14& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T15& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T16& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T17& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T18& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T19& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T20& msg) { this->Handle(static_cast(msg)); } }; // 10 by 10 @@ -55,16 +55,16 @@ namespace sp using Base = GenericHandler >; public: using Base::Handle; // Don't hide all Handle() functions from base classes - virtual void Handle(T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T3& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T4& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T5& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T6& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T7& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T8& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T9& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T10& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T3& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T4& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T5& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T6& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T7& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T8& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T9& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T10& msg) { this->Handle(static_cast(msg)); } }; // 5 by 5 @@ -76,11 +76,11 @@ namespace sp using Base = GenericHandler >; public: using Base::Handle; // Don't hide all Handle() functions from base classes - virtual void Handle(T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T3& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T4& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T5& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T3& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T4& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T5& msg) { this->Handle(static_cast(msg)); } }; // Deal with rest with 4 types @@ -89,11 +89,11 @@ namespace sp { public: virtual ~GenericHandler() {} - virtual void Handle(T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T3& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T4& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(TCommon&) { } //Nothing to do + virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T3& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T4& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const TCommon&) { } //Nothing to do }; // Deal with rest with 3 types @@ -102,10 +102,10 @@ namespace sp { public: virtual ~GenericHandler() {} - virtual void Handle(T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T3& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(TCommon&) { } //Nothing to do + virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T3& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const TCommon&) { } //Nothing to do }; // Deal with rest with 2 types @@ -114,9 +114,9 @@ namespace sp { public: virtual ~GenericHandler() {} - virtual void Handle(T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(T2& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(TCommon&) { } //Nothing to do + virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const T2& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const TCommon&) { } //Nothing to do }; // Deal with rest with 1 type @@ -125,8 +125,8 @@ namespace sp { public: virtual ~GenericHandler() {} - virtual void Handle(T1& msg) { this->Handle(static_cast(msg)); } - virtual void Handle(TCommon&) { } //Nothing to do + virtual void Handle(const T1& msg) { this->Handle(static_cast(msg)); } + virtual void Handle(const TCommon&) { } //Nothing to do }; // Deal with rest with 0 type @@ -135,7 +135,7 @@ namespace sp { public: virtual ~GenericHandler() {} - virtual void Handle(TCommon&) { } //Nothing to do + virtual void Handle(const TCommon&) { } //Nothing to do }; } // sp \ No newline at end of file diff --git a/include/sp/protocol/Message.h b/include/sp/protocol/Message.h index a83ca82..c347b24 100644 --- a/include/sp/protocol/Message.h +++ b/include/sp/protocol/Message.h @@ -3,7 +3,7 @@ // Inspired by // https://alex-robenko.gitbook.io/comms-protocols-cpp -#include +#include namespace sp { diff --git a/include/sp/protocol/MessageBase.h b/include/sp/protocol/MessageBase.h index a489b7b..628233b 100644 --- a/include/sp/protocol/MessageBase.h +++ b/include/sp/protocol/MessageBase.h @@ -2,339 +2,15 @@ #include +#include + +#include +#include +#include + namespace sp { -namespace option { - -// Provide static numeric ID, to facilitate implementation of GetIdImpl() -template -struct StaticNumIdImpl {}; - -// Facilitate implementation of DispatchImpl() -template -struct DispatchImpl {}; - -// Provide fields of the message, facilitate implementation of -// ReadImpl(), WriteImpl(), ValidImpl(), etc... -template -struct FieldsImpl {}; - -} // namespace option - - -namespace details { - - - - - -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 = typename details::FieldsBuilder::Type; - - template - void Construct(Args... args) { - m_Fields = std::make_tuple(args...); - } - - AllFields& GetFields() { - return m_Fields; - } - - const AllFields& GetFields() const { - return m_Fields; - } - - template - auto& GetField() { - return std::get(GetFields()).GetValue(); - } - - private: - AllFields m_Fields; -}; - -template -class MessageImplFieldsReadBase : public TBase { - private: - // normal reading - template - void ReadField(Field& field, DataBuffer& buffer) { - this->ReadData(field.GetValue(), buffer); - } - - // reading field in bitfield - template - void ReadField(Field& field, TFieldType data, std::size_t offset) { - static constexpr std::size_t TotalBitCount = sizeof(TFieldType) * 8; - // we suppose that the first element is at the highest bits - field.GetValue() = (data >> TotalBitCount - IAlignment - offset) & ((1 << IAlignment) - 1); - } - - // reading bitfield - template - void ReadField(Field, 0>& field, DataBuffer& buffer) { - TContainer data; - this->ReadData(data, buffer); - std::size_t offset = 0; - TupleForEach( - [data, this, &offset](auto& field) { - this->ReadField(field, data, offset); - offset += field.GetAlignment(); - }, - field.GetValue().GetFields()); - } - - void ReadImpl(DataBuffer& buffer) override { - auto& allFields = this->GetFields(); - TupleForEach([&buffer, this](auto& field) { this->ReadField(field, buffer); }, allFields); - } -}; - -template -class MessageImplFieldsWriteBase : public TBase { - private: - // normal writing - template - void WriteField(Field& field, DataBuffer& buffer) { - this->WriteData(field.GetValue(), buffer); - } - - // writing field in bitfield - template - void WriteField(Field& field, TFieldType& data, std::size_t offset) { - static constexpr std::size_t TotalBitCount = sizeof(TFieldType) * 8; - // we suppose that the first element is at the highest bits - data |= (field.GetValue() & ((1 << IAlignment) - 1)) << TotalBitCount - IAlignment - offset; - } - - // writing bitfield - template - void WriteField(Field, 0>& field, DataBuffer& buffer) { - TContainer data = 0; - std::size_t offset = 0; - TupleForEach( - [&data, this, &offset](auto& field) { - this->WriteField(field, data, offset); - offset += field.GetAlignment(); - }, - field.GetValue().GetFields()); - this->WriteData(data, buffer); - } - - void WriteImpl(DataBuffer& buffer) override { - auto& allFields = this->GetFields(); - TupleForEach([&buffer, this](auto& field) { this->WriteField(field, buffer); }, allFields); - } -}; - -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 - return true; - } -}; - - - - -// 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::HasValid && ImplOptions::HasFieldsImpl; - using Base6 = typename MessageImplProcessValidFields::Type; - - // The last BaseN must be taken as final type. - using Type = Base6; -}; - -} // namespace details template class MessageBase : public details::MessageImplBuilder::Type {}; - } // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/MessageInterfaces.h b/include/sp/protocol/MessageInterfaces.h deleted file mode 100644 index bc15cb3..0000000 --- a/include/sp/protocol/MessageInterfaces.h +++ /dev/null @@ -1,278 +0,0 @@ -#pragma once - -#include -#include - -namespace sp { -namespace details { - -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 HasWriteId = 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 : public MessageInterfaceParsedOptions { - static const bool HasWriteId = true; -}; - -template -struct MessageInterfaceParsedOptions, TOptions...> : public MessageInterfaceParsedOptions { - static const bool HasHandler = true; - using HandlerType = option::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 - void ReadData(T& value, DataBuffer& buffer) { - buffer >> value; - FromNetwork(value); - } - - template - void WriteData(T value, DataBuffer& buffer) { - ToNetwork(value); - buffer << value; - } -}; - -// Little endian serialisation chunk -template -class MessageInterfaceLittleEndian : public TBase { - protected: - template - void ReadData(T& value, DataBuffer& buffer) { - buffer >> value; - TrySwapBytes(value); - FromNetwork(value); - } - - template - void WriteData(const T& value, DataBuffer& buffer) { - ToNetwork(value); - TrySwapBytes(value); - buffer << value; - } -}; - -// 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) { - 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; -}; - -// Writing id functionality chunk -template -class MessageInterfaceWriteIdBase : public TBase { - public: - void Write(DataBuffer& buffer) { - this->WriteData(this->GetId(), buffer); - this->WriteImpl(buffer); - } -}; - - - - -// 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; -}; - -// Build id writing -template -struct MessageInterfaceProcessWriteId; - -template -struct MessageInterfaceProcessWriteId { - using Type = MessageInterfaceWriteIdBase; -}; - -template -struct MessageInterfaceProcessWriteId { - using Type = TBase; -}; -} // namespace details -} // namespace sp diff --git a/include/sp/protocol/message/MessageImplBuilder.h b/include/sp/protocol/message/MessageImplBuilder.h new file mode 100644 index 0000000..2ae61fd --- /dev/null +++ b/include/sp/protocol/message/MessageImplBuilder.h @@ -0,0 +1,45 @@ +#pragma once + +namespace sp { +namespace details { + + +// 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::HasValid && ImplOptions::HasFieldsImpl; + using Base6 = typename MessageImplProcessValidFields::Type; + + // The last BaseN must be taken as final type. + using Type = Base6; +}; + +} // namespace details +} // namespace sp diff --git a/include/sp/protocol/message/MessageImplOptions.h b/include/sp/protocol/message/MessageImplOptions.h new file mode 100644 index 0000000..18b0fc2 --- /dev/null +++ b/include/sp/protocol/message/MessageImplOptions.h @@ -0,0 +1,61 @@ +#pragma once + +namespace sp { +namespace option { + +// Provide static numeric ID, to facilitate implementation of GetIdImpl() +template +struct StaticNumIdImpl {}; + +// Facilitate implementation of DispatchImpl() +template +struct DispatchImpl {}; + +// Provide fields of the message, facilitate implementation of +// ReadImpl(), WriteImpl(), ValidImpl(), etc... +template +struct FieldsImpl {}; + +} // namespace option + + +namespace details { + + + + + +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; +}; + +} // namespace details +} // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/message/MessageImplProcess.h b/include/sp/protocol/message/MessageImplProcess.h new file mode 100644 index 0000000..c377df1 --- /dev/null +++ b/include/sp/protocol/message/MessageImplProcess.h @@ -0,0 +1,93 @@ +#pragma once + +namespace sp { +namespace details { + + + +// 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; +}; + +} // namespace details +} // namespace sp diff --git a/include/sp/protocol/MessageInterfaceBuilder.h b/include/sp/protocol/message/MessageInterfaceBuilder.h similarity index 93% rename from include/sp/protocol/MessageInterfaceBuilder.h rename to include/sp/protocol/message/MessageInterfaceBuilder.h index 4fb0b71..310e2cd 100644 --- a/include/sp/protocol/MessageInterfaceBuilder.h +++ b/include/sp/protocol/message/MessageInterfaceBuilder.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace sp { namespace details { @@ -33,7 +33,7 @@ struct MessageInterfaceBuilder { // add write id functionality if write id and write was provided using Base7 = typename MessageInterfaceProcessWriteId::Type; - // The last Base6 must be taken as final type. + // The last Base7 must be taken as final type. using Type = Base7; }; diff --git a/include/sp/protocol/message/MessageInterfaceProcess.h b/include/sp/protocol/message/MessageInterfaceProcess.h new file mode 100644 index 0000000..b6d7da2 --- /dev/null +++ b/include/sp/protocol/message/MessageInterfaceProcess.h @@ -0,0 +1,105 @@ +#pragma once + +namespace sp { +namespace details { + +// 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; +}; + +// Build id writing +template +struct MessageInterfaceProcessWriteId; + +template +struct MessageInterfaceProcessWriteId { + using Type = MessageInterfaceWriteIdBase; +}; + +template +struct MessageInterfaceProcessWriteId { + using Type = TBase; +}; + +} // namespace details +} // namespace sp \ No newline at end of file diff --git a/include/sp/protocol/message/MessageInterfaces.h b/include/sp/protocol/message/MessageInterfaces.h new file mode 100644 index 0000000..a1b1176 --- /dev/null +++ b/include/sp/protocol/message/MessageInterfaces.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include +#include +#include diff --git a/include/sp/protocol/message/MessageInterfacesImpl.h b/include/sp/protocol/message/MessageInterfacesImpl.h new file mode 100644 index 0000000..9ccf3f9 --- /dev/null +++ b/include/sp/protocol/message/MessageInterfacesImpl.h @@ -0,0 +1,119 @@ +#pragma once + +#include + +namespace sp { +namespace details { + + +// 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 + void ReadData(T& value, DataBuffer& buffer) { + buffer >> value; + FromNetwork(value); + } + + template + void WriteData(T value, DataBuffer& buffer) { + ToNetwork(value); + buffer << value; + } +}; + +// Little endian serialisation chunk +template +class MessageInterfaceLittleEndian : public TBase { + protected: + template + void ReadData(T& value, DataBuffer& buffer) { + buffer >> value; + TrySwapBytes(value); + FromNetwork(value); + } + + template + void WriteData(const T& value, DataBuffer& buffer) { + ToNetwork(value); + TrySwapBytes(value); + buffer << value; + } +}; + +// 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) { + 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; +}; + +// Writing id functionality chunk +template +class MessageInterfaceWriteIdBase : public TBase { + public: + void Write(DataBuffer& buffer) { + this->WriteData(this->GetId(), buffer); + this->WriteImpl(buffer); + } +}; + +} // namespace details +} // namespace sp diff --git a/include/sp/protocol/message/MessageInterfacesOptions.h b/include/sp/protocol/message/MessageInterfacesOptions.h new file mode 100644 index 0000000..15ecb06 --- /dev/null +++ b/include/sp/protocol/message/MessageInterfacesOptions.h @@ -0,0 +1,63 @@ +#pragma once + +namespace sp { +namespace details { + + +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 HasWriteId = 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 : public MessageInterfaceParsedOptions { + static const bool HasWriteId = true; +}; + +template +struct MessageInterfaceParsedOptions, TOptions...> : public MessageInterfaceParsedOptions { + static const bool HasHandler = true; + using HandlerType = option::Handler; +}; + +template +struct MessageInterfaceParsedOptions : public MessageInterfaceParsedOptions { + static const bool HasValid = true; +}; + +} // namespace details +} // namespace sp diff --git a/include/sp/protocol/Options.h b/include/sp/protocol/message/MessageOptions.h similarity index 100% rename from include/sp/protocol/Options.h rename to include/sp/protocol/message/MessageOptions.h diff --git a/include/sp/protocol/message/MessagesImpl.h b/include/sp/protocol/message/MessagesImpl.h new file mode 100644 index 0000000..00a5b45 --- /dev/null +++ b/include/sp/protocol/message/MessagesImpl.h @@ -0,0 +1,160 @@ +#pragma once + +namespace sp { +namespace details { + + + +// 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 = typename details::FieldsBuilder::Type; + + template + void Construct(Args... args) { + m_Fields = std::make_tuple(args...); + } + + AllFields& GetFields() { + return m_Fields; + } + + const AllFields& GetFields() const { + return m_Fields; + } + + template + auto& GetField() { + return std::get(GetFields()).GetValue(); + } + + template + const auto& GetField() const { + return std::get(GetFields()).GetValue(); + } + + // allow use of enums + template + const auto& GetField() const { + return std::get(FIndex)>(this->GetFields()).GetValue(); + } + + private: + AllFields m_Fields; +}; + +template +class MessageImplFieldsReadBase : public TBase { + private: + // normal reading + template + void ReadField(Field& field, DataBuffer& buffer) { + this->ReadData(field.GetValue(), buffer); + } + + // reading field in bitfield + template + void ReadField(Field& field, TFieldType data, std::size_t offset) { + static constexpr std::size_t TotalBitCount = sizeof(TFieldType) * 8; + // we suppose that the first element is at the highest bits + field.GetValue() = (data >> TotalBitCount - IAlignment - offset) & ((1 << IAlignment) - 1); + } + + // reading bitfield + template + void ReadField(Field, 0>& field, DataBuffer& buffer) { + TContainer data; + this->ReadData(data, buffer); + std::size_t offset = 0; + TupleForEach( + [data, this, &offset](auto& field) { + this->ReadField(field, data, offset); + offset += field.GetAlignment(); + }, + field.GetValue().GetFields()); + } + + void ReadImpl(DataBuffer& buffer) override { + auto& allFields = this->GetFields(); + TupleForEach([&buffer, this](auto& field) { this->ReadField(field, buffer); }, allFields); + } +}; + +template +class MessageImplFieldsWriteBase : public TBase { + private: + // normal writing + template + void WriteField(Field& field, DataBuffer& buffer) { + this->WriteData(field.GetValue(), buffer); + } + + // writing field in bitfield + template + void WriteField(Field& field, TFieldType& data, std::size_t offset) { + static constexpr std::size_t TotalBitCount = sizeof(TFieldType) * 8; + // we suppose that the first element is at the highest bits + data |= (field.GetValue() & ((1 << IAlignment) - 1)) << TotalBitCount - IAlignment - offset; + } + + // writing bitfield + template + void WriteField(Field, 0>& field, DataBuffer& buffer) { + TContainer data = 0; + std::size_t offset = 0; + TupleForEach( + [&data, this, &offset](auto& field) { + this->WriteField(field, data, offset); + offset += field.GetAlignment(); + }, + field.GetValue().GetFields()); + this->WriteData(data, buffer); + } + + void WriteImpl(DataBuffer& buffer) override { + auto& allFields = this->GetFields(); + TupleForEach([&buffer, this](auto& field) { this->WriteField(field, buffer); }, allFields); + } +}; + +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 + return true; + } +}; + +} // namespace details +} // namespace sp diff --git a/src/sp/common/DataBuffer.cpp b/src/sp/common/DataBuffer.cpp index 42d424f..00d8335 100644 --- a/src/sp/common/DataBuffer.cpp +++ b/src/sp/common/DataBuffer.cpp @@ -4,9 +4,6 @@ #include #include -#include -#include - namespace sp { DataBuffer::DataBuffer() : m_ReadOffset(0) {} @@ -133,31 +130,19 @@ std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer) { return os; } -bool DataBuffer::ReadFile(const std::string& fileName) { - try { - std::ifstream file(fileName, std::istream::binary); - std::ostringstream ss; - ss << file.rdbuf(); - const std::string& s = ss.str(); - m_Buffer = DataBuffer::Data(s.begin(), s.end()); - m_ReadOffset = 0; - } catch (std::exception& e) { - utils::LOGE(utils::Format("[IO] Failed to read file %s ! reason : %s", fileName.c_str(), e.what())); - return false; - } - return m_Buffer.size() > 0; +void DataBuffer::ReadFile(const std::string& fileName) { + std::ifstream file(fileName, std::istream::binary); + std::ostringstream ss; + ss << file.rdbuf(); + const std::string& s = ss.str(); + m_Buffer = DataBuffer::Data(s.begin(), s.end()); + m_ReadOffset = 0; } -bool DataBuffer::WriteFile(const std::string& fileName) const { - try { - std::ofstream file(fileName, std::ostream::binary); - file.write(reinterpret_cast(m_Buffer.data()), static_cast(m_Buffer.size())); - file.flush(); - } catch (std::exception& e) { - utils::LOGE(utils::Format("[IO] Failed to read file %s ! reason : %s", fileName.c_str(), e.what())); - return false; - } - return true; +void DataBuffer::WriteFile(const std::string& fileName) const { + std::ofstream file(fileName, std::ostream::binary); + file.write(reinterpret_cast(m_Buffer.data()), static_cast(m_Buffer.size())); + file.flush(); } } // namespace sp diff --git a/src/sp/misc/Log.cpp b/src/sp/misc/Log.cpp deleted file mode 100644 index 829f46d..0000000 --- a/src/sp/misc/Log.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include - - - -#ifdef SP_ANDROID_LOGGING -#include -#else -#include -#endif - -namespace sp { -namespace utils { - -void LOG(const std::string& msg) { -#ifdef SP_ANDROID_LOGGING - __android_log_print(ANDROID_LOG_INFO, "TRACKERS", "%s", msg.c_str()); -#else - std::cout << msg << "\n"; -#endif -} - -void LOGD(const std::string& msg) { -#if !defined(NDEBUG) - LOG(msg); -#endif -} - -void LOGE(const std::string& err) { -#ifdef SP_ANDROID_LOGGING - __android_log_print(ANDROID_LOG_ERROR, "TRACKERS", "%s", err.c_str()); -#else - std::cerr << err << "\n"; -#endif -} - -} // namespace utils -} // namespace sp diff --git a/src/sp/misc/Test.cpp b/src/sp/misc/Test.cpp deleted file mode 100644 index 28cb8ff..0000000 --- a/src/sp/misc/Test.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include - -namespace sp { -namespace test { - -void LogAssert(const char* expression, const char* file, int line, const char* function) { - utils::LOGE(utils::Format("%s:%i: %s: Assertion failed !", file, line, function)); - utils::LOGE(utils::Format(" %i |\t%s;", line, expression)); -} - -} // namespace test -} // namespace sp diff --git a/src/main.cpp b/test/test_packets.cpp similarity index 53% rename from src/main.cpp rename to test/test_packets.cpp index 0cde3b6..c3fff88 100644 --- a/src/main.cpp +++ b/test/test_packets.cpp @@ -4,18 +4,23 @@ #include class KeepAliveHandler : public sp::PacketHandler { - void Handle(KeepAlivePacket& packet) { + void Handle(const KeepAlivePacket& packet) { std::cout << "KeepAlive handled !\n"; } - void Handle(DisconnectPacket& packet) { + void Handle(const DisconnectPacket& packet) { std::cout << "Disconnect handled !\n"; } + + void Handle(const UpgradeTowerPacket& packet) { + std::cout << "UpgradeTower handled !\n"; + } }; int main() { - auto keepAlive = std::make_unique(69); - sp::PacketMessage* msg = keepAlive.get(); + auto upgradeTower = std::make_unique(std::make_tuple(666, 9)); + + sp::PacketMessage* msg = upgradeTower.get(); KeepAliveHandler handler; msg->Dispatch(handler); @@ -26,15 +31,15 @@ int main() { std::uint8_t msgId; buffer >> msgId; - auto keepAlive2 = std::make_unique(); - keepAlive2->Read(buffer); + auto upgradeTower2 = std::make_unique(); + upgradeTower2->Read(buffer); - std::cout << "KeepAlive2 : " << keepAlive2->GetField() << "\n"; + std::cout << "Test : " << (unsigned) upgradeTower2->GetTowerId() << "\n"; sp::PacketFactory factory; auto packet = factory.CreateMessage(msgId); if (packet == nullptr) { - std::cout << "Mauvais ID !\n"; + std::cout << "Bad ID !\n"; return 1; } std::cout << (unsigned)packet->GetId() << std::endl; diff --git a/xmake.lua b/xmake.lua index 71fed3a..0c1c8af 100644 --- a/xmake.lua +++ b/xmake.lua @@ -2,15 +2,12 @@ add_rules("mode.debug", "mode.release") set_languages("c++17") -add_requires("enet6") - target("SimpleProtocolLib") - add_includedirs("include", {public = true}) - set_kind("binary") - add_files("src/**.cpp") - add_packages("enet6", {public = true}) - - + add_includedirs("include") + add_headerfiles("include/(sp/**.h)") + set_group("Library") + add_files("src/sp/**.cpp") + set_kind("$(kind)") -- Tests for _, file in ipairs(os.files("test/**.cpp")) do @@ -19,79 +16,9 @@ for _, file in ipairs(os.files("test/**.cpp")) do set_kind("binary") add_files(file) - - set_default(false) + add_includedirs("include") add_deps("SimpleProtocolLib") add_tests("compile_and_run") end --- --- If you want to known more usage about xmake, please see https://xmake.io --- --- ## FAQ --- --- You can enter the project directory firstly before building project. --- --- $ cd projectdir --- --- 1. How to build project? --- --- $ xmake --- --- 2. How to configure project? --- --- $ xmake f -p [macosx|linux|iphoneos ..] -a [x86_64|i386|arm64 ..] -m [debug|release] --- --- 3. Where is the build output directory? --- --- The default output directory is `./build` and you can configure the output directory. --- --- $ xmake f -o outputdir --- $ xmake --- --- 4. How to run and debug target after building project? --- --- $ xmake run [targetname] --- $ xmake run -d [targetname] --- --- 5. How to install target to the system directory or other output directory? --- --- $ xmake install --- $ xmake install -o installdir --- --- 6. Add some frequently-used compilation flags in xmake.lua --- --- @code --- -- add debug and release modes --- add_rules("mode.debug", "mode.release") --- --- -- add macro definition --- add_defines("NDEBUG", "_GNU_SOURCE=1") --- --- -- set warning all as error --- set_warnings("all", "error") --- --- -- set language: c99, c++11 --- set_languages("c99", "c++11") --- --- -- set optimization: none, faster, fastest, smallest --- set_optimize("fastest") --- --- -- add include search directories --- add_includedirs("/usr/include", "/usr/local/include") --- --- -- add link libraries and search directories --- add_links("tbox") --- add_linkdirs("/usr/local/lib", "/usr/lib") --- --- -- add system link libraries --- add_syslinks("z", "pthread") --- --- -- add compilation and link flags --- add_cxflags("-stdnolib", "-fno-strict-aliasing") --- add_ldflags("-L/usr/local/lib", "-lpthread", {force = true}) --- --- @endcode --- -