diff --git a/include/sp/protocol/MessageBase.h b/include/sp/protocol/MessageBase.h index 61f4947..628233b 100644 --- a/include/sp/protocol/MessageBase.h +++ b/include/sp/protocol/MessageBase.h @@ -2,350 +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(); - } - - 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; - } -}; - - - - -// 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/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/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